diff --git a/.kokoro/release.cfg b/.kokoro/release.cfg new file mode 100644 index 0000000..e2deabd --- /dev/null +++ b/.kokoro/release.cfg @@ -0,0 +1,13 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +build_file: "github/data-manager-node/.kokoro/release.sh" + +# Default to DRY_RUN=true for safety; can be overridden via Stubby/Kokoro API +env_vars: { + key: "DRY_RUN" + value: "true" +} + +container_properties { + docker_image: "us-central1-docker.pkg.dev/kokoro-container-bakery/kokoro/ubuntu/ubuntu2204/full:current" +} diff --git a/.kokoro/release.sh b/.kokoro/release.sh new file mode 100755 index 0000000..14c3645 --- /dev/null +++ b/.kokoro/release.sh @@ -0,0 +1,108 @@ +#!/bin/bash +set -euo pipefail + +# ----------------------------------------------------------------------------- +# 1. Workspace & Directory Resolution +# ----------------------------------------------------------------------------- +REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "${REPO_DIR}" + +echo "=== Building and Releasing from: ${REPO_DIR} ===" + +# ----------------------------------------------------------------------------- +# 2. Environment & Tooling Setup +# ----------------------------------------------------------------------------- +echo "=== Environment Info ===" +echo "Node version: $(node -v)" +echo "NPM version: $(npm -v)" + +# ----------------------------------------------------------------------------- +# 3. Clean and Build Distribution Packages +# ----------------------------------------------------------------------------- +echo "=== Installing dependencies ===" +npm ci + +echo "=== Building packages ===" +npm run build + +echo "=== Running tests ===" +npm test + +# ----------------------------------------------------------------------------- +# 4. Upload to Internal Exit Gate Artifact Registry +# ----------------------------------------------------------------------------- +cd "${REPO_DIR}/util" + +EXIT_GATE_PROJECT="oss-exit-gate-prod" +EXIT_GATE_LOCATION="us" +EXIT_GATE_REPOSITORY="measurement-devrel--npm" +PACKAGE_NAME="@google-ads/data-manager-util" + +# Delete existing package from the Exit Gate staging repository if present. +# This prevents upload failures on retries or re-releases of the same version. +if gcloud artifacts packages describe "${PACKAGE_NAME}" \ + --project="${EXIT_GATE_PROJECT}" \ + --location="${EXIT_GATE_LOCATION}" \ + --repository="${EXIT_GATE_REPOSITORY}" &>/dev/null; then + echo "=== Deleting existing package '${PACKAGE_NAME}' from Exit Gate staging repository ===" + gcloud artifacts packages delete "${PACKAGE_NAME}" \ + --project="${EXIT_GATE_PROJECT}" \ + --location="${EXIT_GATE_LOCATION}" \ + --repository="${EXIT_GATE_REPOSITORY}" \ + --quiet +else + echo "=== Skipping deletion. Package '${PACKAGE_NAME}' not found in staging repository. 🙂 ===" +fi + +# Configure .npmrc for Artifact Registry according to go/oss-exit-gate-release-npm +cat < .npmrc +@google-ads:registry=https://${EXIT_GATE_LOCATION}-npm.pkg.dev/${EXIT_GATE_PROJECT}/${EXIT_GATE_REPOSITORY}/ +//${EXIT_GATE_LOCATION}-npm.pkg.dev/${EXIT_GATE_PROJECT}/${EXIT_GATE_REPOSITORY}/:always-auth=true +EOF +# Clean up .npmrc and manifest.json on exit. google-artifactregistry-auth writes +# a live GCP OAuth bearer token into .npmrc, so removing it prevents credential +# leakage into Kokoro logs/artifacts and keeps the local workspace clean. +trap 'rm -f .npmrc manifest.json 2>/dev/null || true' EXIT + +# Authenticate with Artifact Registry +npx google-artifactregistry-auth + +echo "=== Publishing package to Exit Gate staging repository ===" +npm publish + +# ----------------------------------------------------------------------------- +# 5. Trigger Exit Gate Release via GCS Manifest +# ----------------------------------------------------------------------------- +# If DRY_RUN is set to "true", stop here so you can verify AR staging +# without publishing to public npm. +if [[ "${DRY_RUN:-false}" == "true" ]]; then + echo "=== DRY_RUN is enabled. Skipping manifest upload to Exit Gate. ===" + echo "Artifacts are staged in Artifact Registry." + exit 0 +fi + +echo "=== Creating targeted release manifest for ${PACKAGE_NAME} ===" +cat < manifest.json +{ + "publish_all": false, + "publishing_groups": [ + { + "packages": [ + { + "name": "${PACKAGE_NAME}" + } + ] + } + ] +} +EOF + +EXIT_GATE_BUCKET="gs://oss-exit-gate-prod-projects-bucket/measurement-devrel/npm/manifests" +MANIFEST_NAME="manifest-$(date +%Y%m%d%H%M%S).json" + +echo "=== Uploading manifest to ${EXIT_GATE_BUCKET}/${MANIFEST_NAME} ===" +gcloud storage cp manifest.json "${EXIT_GATE_BUCKET}/${MANIFEST_NAME}" + +echo "=========================================================================" +echo "Release successfully triggered! Exit Gate will now verify BCID and publish to npm." +echo "=========================================================================" diff --git a/package-lock.json b/package-lock.json index 0344fd4..71992e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@types/node": "^22.7.5", "c8": "^10.0.0", "chai": "^4.2.0", + "google-artifactregistry-auth": "^3.5.0", "gts": "^6.0.2", "mocha": "^12.0.1", "typescript": "^5.6.3" @@ -2114,6 +2115,96 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/google-artifactregistry-auth": { + "version": "3.5.0", + "integrity": "sha512-SIvVBPjVr0KvYFEJEZXKfELt8nvXwTKl6IHyOT7pTHBlS8Ej2UuTOJeKWYFim/JztSjUyna9pKQxa3VhTA12Fg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^9.14.0", + "js-yaml": "^4.1.0", + "yargs": "^17.1.1" + }, + "bin": { + "artifactregistry-auth": "src/main.js" + } + }, + "node_modules/google-artifactregistry-auth/node_modules/gaxios": { + "version": "6.7.1", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-artifactregistry-auth/node_modules/gcp-metadata": { + "version": "6.1.1", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-artifactregistry-auth/node_modules/google-auth-library": { + "version": "9.15.1", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-artifactregistry-auth/node_modules/google-logging-utils": { + "version": "0.0.2", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/google-artifactregistry-auth/node_modules/node-fetch": { + "version": "2.7.0", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/google-auth-library": { "version": "11.0.2", "integrity": "sha512-vzpgPutxrghPsnjrjpzLX2bdv8IOL719Rh0oEjGnQu8YCIbnbMuTTQ5zU9LcKvLdOPgCxBwppbvnhgW90Qna5Q==", @@ -2165,6 +2256,55 @@ "dev": true, "license": "MIT" }, + "node_modules/gtoken": { + "version": "7.1.0", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/gtoken/node_modules/gaxios": { + "version": "6.7.1", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gtoken/node_modules/node-fetch": { + "version": "2.7.0", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/gts": { "version": "6.0.2", "integrity": "sha512-lp9+eDzzm6TYqiBpgGY00EInxBHFTJiU5brsVp11qXCJEw7Q6WNNngja0spZeqSFWSquaRuHQUuWxdZLaxnKmw==", @@ -4296,6 +4436,12 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, "node_modules/trim-newlines": { "version": "3.0.1", "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", @@ -4455,6 +4601,20 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/uuid": { + "version": "9.0.1", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", @@ -4493,6 +4653,22 @@ "node": ">= 8" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", @@ -4646,7 +4822,7 @@ "version": "0.1.0", "license": "Apache-2.0", "dependencies": { - "@google-ads/data-manager-util": "^0.4.0", + "@google-ads/data-manager-util": "^0.4.0-rc1", "@google-ads/datamanager": "^1.0.0", "csv-parser": "^3.0.0", "yargs": "^17.7.2" @@ -4661,7 +4837,7 @@ }, "util": { "name": "@google-ads/data-manager-util", - "version": "0.4.0", + "version": "0.4.0-rc1", "license": "Apache-2.0", "devDependencies": { "@types/chai": "^4.3.0", diff --git a/package.json b/package.json index c8553ff..4f8365d 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "gts": "^6.0.2", "mocha": "^12.0.1", "typescript": "^5.6.3", - "@types/node": "^22.7.5" + "@types/node": "^22.7.5", + "google-artifactregistry-auth": "^3.5.0" } } diff --git a/samples/package.json b/samples/package.json index a899ce5..393dfd8 100644 --- a/samples/package.json +++ b/samples/package.json @@ -18,7 +18,7 @@ }, "dependencies": { "@google-ads/datamanager": "^1.0.0", - "@google-ads/data-manager-util": "^0.4.0", + "@google-ads/data-manager-util": "^0.4.0-rc1", "csv-parser": "^3.0.0", "yargs": "^17.7.2" }, diff --git a/util/package.json b/util/package.json index 68a23cc..7550587 100644 --- a/util/package.json +++ b/util/package.json @@ -1,11 +1,14 @@ { "name": "@google-ads/data-manager-util", - "version": "0.4.0", + "version": "0.4.0-rc1", "description": "A utility library for the Data Manager API for Node.js.", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", "license": "Apache-2.0", "author": "Google LLC", + "publishConfig": { + "access": "public" + }, "engines": { "node": ">=22" },