diff --git a/.kokoro/continuous/dependencies.cfg b/.kokoro/continuous/dependencies.cfg
deleted file mode 100644
index 895832a928..0000000000
--- a/.kokoro/continuous/dependencies.cfg
+++ /dev/null
@@ -1,12 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# Configure the docker image for kokoro-trampoline.
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/java8"
-}
-
-env_vars: {
- key: "TRAMPOLINE_BUILD_FILE"
- value: "github/java-core/.kokoro/dependencies.sh"
-}
diff --git a/.kokoro/continuous/integration.cfg b/.kokoro/continuous/integration.cfg
deleted file mode 100644
index 3b017fc80f..0000000000
--- a/.kokoro/continuous/integration.cfg
+++ /dev/null
@@ -1,7 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# Configure the docker image for kokoro-trampoline.
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/java8"
-}
diff --git a/.kokoro/continuous/java11.cfg b/.kokoro/continuous/java11.cfg
deleted file mode 100644
index 709f2b4c73..0000000000
--- a/.kokoro/continuous/java11.cfg
+++ /dev/null
@@ -1,7 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# Configure the docker image for kokoro-trampoline.
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/java11"
-}
diff --git a/.kokoro/continuous/java7.cfg b/.kokoro/continuous/java7.cfg
deleted file mode 100644
index cb24f44eea..0000000000
--- a/.kokoro/continuous/java7.cfg
+++ /dev/null
@@ -1,7 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# Configure the docker image for kokoro-trampoline.
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/java7"
-}
diff --git a/.kokoro/continuous/java8-osx.cfg b/.kokoro/continuous/java8-osx.cfg
deleted file mode 100644
index 8b617e2594..0000000000
--- a/.kokoro/continuous/java8-osx.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-build_file: "java-core/.kokoro/build.sh"
diff --git a/.kokoro/continuous/java8-win.cfg b/.kokoro/continuous/java8-win.cfg
deleted file mode 100644
index 1a6311a420..0000000000
--- a/.kokoro/continuous/java8-win.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-build_file: "java-core/.kokoro/build.bat"
diff --git a/.kokoro/continuous/lint.cfg b/.kokoro/continuous/lint.cfg
deleted file mode 100644
index 6d323c8ae7..0000000000
--- a/.kokoro/continuous/lint.cfg
+++ /dev/null
@@ -1,13 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# Configure the docker image for kokoro-trampoline.
-
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/java8"
-}
-
-env_vars: {
- key: "JOB_TYPE"
- value: "lint"
-}
\ No newline at end of file
diff --git a/.kokoro/continuous/propose_release.cfg b/.kokoro/continuous/propose_release.cfg
deleted file mode 100644
index fc798ae888..0000000000
--- a/.kokoro/continuous/propose_release.cfg
+++ /dev/null
@@ -1,53 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# Build logs will be here
-action {
- define_artifacts {
- regex: "**/*sponge_log.xml"
- }
-}
-
-# Download trampoline resources.
-gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
-
-# Use the trampoline script to run in docker.
-build_file: "java-core/.kokoro/trampoline.sh"
-
-# Configure the docker image for kokoro-trampoline.
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/node:10-user"
-}
-
-env_vars: {
- key: "TRAMPOLINE_BUILD_FILE"
- value: "github/java-core/.kokoro/continuous/propose_release.sh"
-}
-
-# tokens used by release-please to keep an up-to-date release PR.
-before_action {
- fetch_keystore {
- keystore_resource {
- keystore_config_id: 73713
- keyname: "github-magic-proxy-key-release-please"
- }
- }
-}
-
-before_action {
- fetch_keystore {
- keystore_resource {
- keystore_config_id: 73713
- keyname: "github-magic-proxy-token-release-please"
- }
- }
-}
-
-before_action {
- fetch_keystore {
- keystore_resource {
- keystore_config_id: 73713
- keyname: "github-magic-proxy-url-release-please"
- }
- }
-}
diff --git a/.kokoro/continuous/samples.cfg b/.kokoro/continuous/samples.cfg
deleted file mode 100644
index fa7b493d0b..0000000000
--- a/.kokoro/continuous/samples.cfg
+++ /dev/null
@@ -1,31 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# Configure the docker image for kokoro-trampoline.
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/java8"
-}
-
-env_vars: {
- key: "JOB_TYPE"
- value: "samples"
-}
-
-env_vars: {
- key: "GCLOUD_PROJECT"
- value: "gcloud-devel"
-}
-
-env_vars: {
- key: "GOOGLE_APPLICATION_CREDENTIALS"
- value: "keystore/73713_java_it_service_account"
-}
-
-before_action {
- fetch_keystore {
- keystore_resource {
- keystore_config_id: 73713
- keyname: "java_it_service_account"
- }
- }
-}
diff --git a/.kokoro/dependencies.sh b/.kokoro/dependencies.sh
index cee4f11e75..c91e5a5693 100755
--- a/.kokoro/dependencies.sh
+++ b/.kokoro/dependencies.sh
@@ -43,12 +43,13 @@ function completenessCheck() {
# Output dep list with compile scope generated using the original pom
# Running mvn dependency:list on Java versions that support modules will also include the module of the dependency.
# This is stripped from the output as it is not present in the flattened pom.
+ # Only dependencies with 'compile' or 'runtime' scope are included from original dependency list.
msg "Generating dependency list using original pom..."
- mvn dependency:list -f pom.xml -Dsort=true | grep '\[INFO] .*:.*:.*:.*:.*' | sed -e s/\\s--\\smodule.*// | grep -v ':test$' >.org-list.txt
+ mvn dependency:list -f pom.xml -DincludeScope=runtime -Dsort=true | grep '\[INFO] .*:.*:.*:.*:.*' | sed -e s/\\s--\\smodule.*// >.org-list.txt
- # Output dep list generated using the flattened pom (test scope deps are ommitted)
+ # Output dep list generated using the flattened pom (only 'compile' and 'runtime' scopes)
msg "Generating dependency list using flattened pom..."
- mvn dependency:list -f .flattened-pom.xml -Dsort=true | grep '\[INFO] .*:.*:.*:.*:.*' >.new-list.txt
+ mvn dependency:list -f .flattened-pom.xml -DincludeScope=runtime -Dsort=true | grep '\[INFO] .*:.*:.*:.*:.*' >.new-list.txt
# Compare two dependency lists
msg "Comparing dependency lists..."
@@ -85,4 +86,4 @@ then
else
msg "Errors found. See log statements above."
exit 1
-fi
+fi
\ No newline at end of file
diff --git a/.kokoro/nightly/dependencies.cfg b/.kokoro/nightly/dependencies.cfg
deleted file mode 100644
index 895832a928..0000000000
--- a/.kokoro/nightly/dependencies.cfg
+++ /dev/null
@@ -1,12 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# Configure the docker image for kokoro-trampoline.
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/java8"
-}
-
-env_vars: {
- key: "TRAMPOLINE_BUILD_FILE"
- value: "github/java-core/.kokoro/dependencies.sh"
-}
diff --git a/.kokoro/nightly/lint.cfg b/.kokoro/nightly/lint.cfg
deleted file mode 100644
index 6d323c8ae7..0000000000
--- a/.kokoro/nightly/lint.cfg
+++ /dev/null
@@ -1,13 +0,0 @@
-# Format: //devtools/kokoro/config/proto/build.proto
-
-# Configure the docker image for kokoro-trampoline.
-
-env_vars: {
- key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-kokoro-resources/java8"
-}
-
-env_vars: {
- key: "JOB_TYPE"
- value: "lint"
-}
\ No newline at end of file
diff --git a/.kokoro/release/publish_javadoc.cfg b/.kokoro/release/publish_javadoc.cfg
index 166868b690..6aacd12dda 100644
--- a/.kokoro/release/publish_javadoc.cfg
+++ b/.kokoro/release/publish_javadoc.cfg
@@ -1,14 +1,24 @@
# Format: //devtools/kokoro/config/proto/build.proto
+
+gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/doc-templates/"
+
env_vars: {
key: "STAGING_BUCKET"
value: "docs-staging"
}
+env_vars: {
+ key: "STAGING_BUCKET_V2"
+ value: "docs-staging-v2-staging"
+ # Production will be at: docs-staging-v2
+}
+
env_vars: {
key: "TRAMPOLINE_BUILD_FILE"
value: "github/java-core/.kokoro/release/publish_javadoc.sh"
}
+
before_action {
fetch_keystore {
keystore_resource {
diff --git a/.kokoro/release/publish_javadoc.sh b/.kokoro/release/publish_javadoc.sh
index 3eb587a7dc..2ab8a64aeb 100755
--- a/.kokoro/release/publish_javadoc.sh
+++ b/.kokoro/release/publish_javadoc.sh
@@ -24,6 +24,11 @@ if [[ -z "${STAGING_BUCKET}" ]]; then
exit 1
fi
+if [[ -z "${STAGING_BUCKET_V2}" ]]; then
+ echo "Need to set STAGING_BUCKET_V2 environment variable"
+ exit 1
+fi
+
# work from the git root directory
pushd $(dirname "$0")/../../
@@ -31,13 +36,13 @@ pushd $(dirname "$0")/../../
python3 -m pip install gcp-docuploader
# compile all packages
-mvn clean install -B -DskipTests=true
+mvn clean install -B -q -DskipTests=true
NAME=google-cloud-core
VERSION=$(grep ${NAME}: versions.txt | cut -d: -f3)
# build the docs
-mvn site -B
+mvn site -B -q
pushd target/site/apidocs
@@ -53,3 +58,19 @@ python3 -m docuploader upload . \
--staging-bucket ${STAGING_BUCKET}
popd
+
+# V2
+mvn clean site -B -q -Ddevsite.template="${KOKORO_GFILE_DIR}/java/"
+
+pushd target/devsite
+
+# create metadata
+python3 -m docuploader create-metadata \
+ --name ${NAME} \
+ --version ${VERSION} \
+ --language java
+
+# upload docs
+python3 -m docuploader upload . \
+ --credentials ${CREDENTIALS} \
+ --staging-bucket ${STAGING_BUCKET_V2}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 54f7cc306e..5664dd2126 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,22 @@
# Changelog
+### [1.93.8](https://www.github.com/googleapis/java-core/compare/v1.93.7...v1.93.8) (2020-08-12)
+
+
+### Bug Fixes
+
+* docs of com.google.cloud.Timestamp.parseTimestamp ([#258](https://www.github.com/googleapis/java-core/issues/258)) ([964dd14](https://www.github.com/googleapis/java-core/commit/964dd142609ae8923a285e20746ce9ee8c302bd5))
+
+
+### Dependencies
+
+* update core dependencies ([#263](https://www.github.com/googleapis/java-core/issues/263)) ([44023c3](https://www.github.com/googleapis/java-core/commit/44023c34d0b5d1990c5028f6e04479b8d7539e77))
+* update dependency com.google.api-client:google-api-client-bom to v1.30.10 ([#253](https://www.github.com/googleapis/java-core/issues/253)) ([0fd53fe](https://www.github.com/googleapis/java-core/commit/0fd53fe522e35fdd09783bb618ff7dcf01d82a95))
+* update dependency com.google.api:api-common to v1.10.0 ([#261](https://www.github.com/googleapis/java-core/issues/261)) ([1414e01](https://www.github.com/googleapis/java-core/commit/1414e01a8154533d53911933eb86fc785760af6c))
+* update dependency com.google.api.grpc:proto-google-common-protos to v1.18.1 ([#268](https://www.github.com/googleapis/java-core/issues/268)) ([b59a83c](https://www.github.com/googleapis/java-core/commit/b59a83c212f862043de0f5f8fa6ae7bb5d2baba4))
+* update dependency com.google.api.grpc:proto-google-iam-v1 to v1 ([#269](https://www.github.com/googleapis/java-core/issues/269)) ([fe3987e](https://www.github.com/googleapis/java-core/commit/fe3987e2dffb2fe7b7bdd0d48266eba7aad7929b))
+* update dependency com.google.protobuf:protobuf-bom to v3.12.4 ([#262](https://www.github.com/googleapis/java-core/issues/262)) ([2ffca65](https://www.github.com/googleapis/java-core/commit/2ffca65399a716a0b929ae3f1ae388481b187ae2))
+
### [1.93.7](https://www.github.com/googleapis/java-core/compare/v1.93.6...v1.93.7) (2020-07-08)
diff --git a/google-cloud-core-bom/pom.xml b/google-cloud-core-bom/pom.xml
index 060465f30d..38f231bcd7 100644
--- a/google-cloud-core-bom/pom.xml
+++ b/google-cloud-core-bom/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.google.cloud
google-cloud-core-bom
- 1.93.7
+ 1.93.8
pom
com.google.cloud
@@ -63,17 +63,17 @@
com.google.cloud
google-cloud-core
- 1.93.7
+ 1.93.8
com.google.cloud
google-cloud-core-grpc
- 1.93.7
+ 1.93.8
com.google.cloud
google-cloud-core-http
- 1.93.7
+ 1.93.8
diff --git a/google-cloud-core-grpc/pom.xml b/google-cloud-core-grpc/pom.xml
index 61bcacf854..09c9ac240b 100644
--- a/google-cloud-core-grpc/pom.xml
+++ b/google-cloud-core-grpc/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.google.cloud
google-cloud-core-grpc
- 1.93.7
+ 1.93.8
jar
Google Cloud Core gRPC
https://github.com/googleapis/java-core
@@ -13,7 +13,7 @@
com.google.cloud
google-cloud-core-parent
- 1.93.7
+ 1.93.8
google-cloud-core-grpc
diff --git a/google-cloud-core-http/pom.xml b/google-cloud-core-http/pom.xml
index 12839738bd..8e18e8357f 100644
--- a/google-cloud-core-http/pom.xml
+++ b/google-cloud-core-http/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.google.cloud
google-cloud-core-http
- 1.93.7
+ 1.93.8
jar
Google Cloud Core HTTP
https://github.com/googleapis/java-core
@@ -13,7 +13,7 @@
com.google.cloud
google-cloud-core-parent
- 1.93.7
+ 1.93.8
google-cloud-core-http
diff --git a/google-cloud-core/pom.xml b/google-cloud-core/pom.xml
index ad1786d784..bc49dd6be7 100644
--- a/google-cloud-core/pom.xml
+++ b/google-cloud-core/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.google.cloud
google-cloud-core
- 1.93.7
+ 1.93.8
jar
Google Cloud Core
https://github.com/googleapis/java-core
@@ -13,7 +13,7 @@
com.google.cloud
google-cloud-core-parent
- 1.93.7
+ 1.93.8
google-cloud-core
diff --git a/google-cloud-core/src/main/java/com/google/cloud/Timestamp.java b/google-cloud-core/src/main/java/com/google/cloud/Timestamp.java
index c2cf20a3e2..e0308c3836 100644
--- a/google-cloud-core/src/main/java/com/google/cloud/Timestamp.java
+++ b/google-cloud-core/src/main/java/com/google/cloud/Timestamp.java
@@ -28,6 +28,7 @@
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.format.DateTimeFormatter;
import org.threeten.bp.format.DateTimeFormatterBuilder;
+import org.threeten.bp.format.DateTimeParseException;
import org.threeten.bp.temporal.TemporalAccessor;
/**
@@ -189,8 +190,13 @@ public com.google.protobuf.Timestamp toProto() {
}
/**
- * Creates a Timestamp instance from the given string. String is in the RFC 3339 format without
- * the timezone offset (always ends in "Z").
+ * Creates a Timestamp instance from the given string. Input string should be in the RFC 3339
+ * format, like '2020-12-01T10:15:30.000Z' or with the timezone offset, such as
+ * '2020-12-01T10:15:30+01:00'.
+ *
+ * @param timestamp string in the RFC 3339 format
+ * @return created Timestamp
+ * @throws DateTimeParseException if unable to parse
*/
public static Timestamp parseTimestamp(String timestamp) {
TemporalAccessor temporalAccessor = timestampParser.parse(timestamp);
diff --git a/google-cloud-core/src/test/java/com/google/cloud/TimestampTest.java b/google-cloud-core/src/test/java/com/google/cloud/TimestampTest.java
index 26bf9f2f8d..5eaeb0a894 100644
--- a/google-cloud-core/src/test/java/com/google/cloud/TimestampTest.java
+++ b/google-cloud-core/src/test/java/com/google/cloud/TimestampTest.java
@@ -257,6 +257,18 @@ public void parseTimestampWithoutTimeZoneOffset() {
.isEqualTo(Timestamp.ofTimeSecondsAndNanos(TEST_TIME_SECONDS, 0));
}
+ @Test
+ public void parseTimestampWithTimeZoneOffset() {
+ assertThat(Timestamp.parseTimestamp("0001-01-01T00:00:00-00:00"))
+ .isEqualTo(Timestamp.MIN_VALUE);
+ assertThat(Timestamp.parseTimestamp("9999-12-31T23:59:59.999999999-00:00"))
+ .isEqualTo(Timestamp.MAX_VALUE);
+ assertThat(Timestamp.parseTimestamp("2020-12-06T19:21:12.123+05:30"))
+ .isEqualTo(Timestamp.ofTimeSecondsAndNanos(1607262672, 123000000));
+ assertThat(Timestamp.parseTimestamp("2020-07-10T14:03:00-07:00"))
+ .isEqualTo(Timestamp.ofTimeSecondsAndNanos(1594414980, 0));
+ }
+
@Test
public void fromProto() {
com.google.protobuf.Timestamp proto =
diff --git a/pom.xml b/pom.xml
index d04611c59e..fa8c1dbf85 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.google.cloud
google-cloud-core-parent
pom
- 1.93.7
+ 1.93.8
Google Cloud Core Parent
https://github.com/googleapis/java-core
@@ -151,15 +151,15 @@
UTF-8
github
google-cloud-core-parent
- 1.57.1
- 1.9.3
- 1.18.0
- 0.13.0
+ 1.58.2
+ 1.10.0
+ 1.18.1
+ 1.0.0
0.21.1
- 1.30.9
+ 1.30.10
1.36.0
- 1.30.2
- 3.12.2
+ 1.31.0
+ 3.12.4
0.24.0
1.3.2
29.0-android
diff --git a/synth.metadata b/synth.metadata
index 0bc9ed352e..bfa635fc9c 100644
--- a/synth.metadata
+++ b/synth.metadata
@@ -4,15 +4,76 @@
"git": {
"name": ".",
"remote": "https://github.com/googleapis/java-core.git",
- "sha": "60a4a054d54119807aa8d0342f76d2925c35f2a6"
+ "sha": "7e18035aa22267ffad187a1bebdcd29a835154b7"
}
},
{
"git": {
"name": "synthtool",
"remote": "https://github.com/googleapis/synthtool.git",
- "sha": "4f2c9f752a94042472fc03c5bd9e06e89817d2bd"
+ "sha": "f8823dec98277a9516f2fb6fae9f58b3a59a23e1"
}
}
+ ],
+ "generatedFiles": [
+ ".github/CODEOWNERS",
+ ".github/ISSUE_TEMPLATE/bug_report.md",
+ ".github/ISSUE_TEMPLATE/feature_request.md",
+ ".github/ISSUE_TEMPLATE/support_request.md",
+ ".github/PULL_REQUEST_TEMPLATE.md",
+ ".github/release-please.yml",
+ ".github/trusted-contribution.yml",
+ ".github/workflows/ci.yaml",
+ ".kokoro/build.bat",
+ ".kokoro/build.sh",
+ ".kokoro/coerce_logs.sh",
+ ".kokoro/common.cfg",
+ ".kokoro/common.sh",
+ ".kokoro/continuous/common.cfg",
+ ".kokoro/continuous/java8.cfg",
+ ".kokoro/dependencies.sh",
+ ".kokoro/linkage-monitor.sh",
+ ".kokoro/nightly/common.cfg",
+ ".kokoro/nightly/integration.cfg",
+ ".kokoro/nightly/java11.cfg",
+ ".kokoro/nightly/java7.cfg",
+ ".kokoro/nightly/java8-osx.cfg",
+ ".kokoro/nightly/java8-win.cfg",
+ ".kokoro/nightly/java8.cfg",
+ ".kokoro/nightly/samples.cfg",
+ ".kokoro/populate-secrets.sh",
+ ".kokoro/presubmit/clirr.cfg",
+ ".kokoro/presubmit/common.cfg",
+ ".kokoro/presubmit/dependencies.cfg",
+ ".kokoro/presubmit/integration.cfg",
+ ".kokoro/presubmit/java11.cfg",
+ ".kokoro/presubmit/java7.cfg",
+ ".kokoro/presubmit/java8-osx.cfg",
+ ".kokoro/presubmit/java8-win.cfg",
+ ".kokoro/presubmit/java8.cfg",
+ ".kokoro/presubmit/linkage-monitor.cfg",
+ ".kokoro/presubmit/lint.cfg",
+ ".kokoro/presubmit/samples.cfg",
+ ".kokoro/release/bump_snapshot.cfg",
+ ".kokoro/release/common.cfg",
+ ".kokoro/release/common.sh",
+ ".kokoro/release/drop.cfg",
+ ".kokoro/release/drop.sh",
+ ".kokoro/release/promote.cfg",
+ ".kokoro/release/promote.sh",
+ ".kokoro/release/publish_javadoc.cfg",
+ ".kokoro/release/publish_javadoc.sh",
+ ".kokoro/release/snapshot.cfg",
+ ".kokoro/release/snapshot.sh",
+ ".kokoro/release/stage.cfg",
+ ".kokoro/release/stage.sh",
+ ".kokoro/trampoline.sh",
+ "CODE_OF_CONDUCT.md",
+ "CONTRIBUTING.md",
+ "LICENSE",
+ "codecov.yaml",
+ "java.header",
+ "license-checks.xml",
+ "renovate.json"
]
}
\ No newline at end of file
diff --git a/versions.txt b/versions.txt
index 7416533e5a..3a74853a63 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,4 +1,4 @@
# Format:
# module:released-version:current-version
-google-cloud-core:1.93.7:1.93.7
\ No newline at end of file
+google-cloud-core:1.93.8:1.93.8
\ No newline at end of file