From 0a1fa8da10a13ac536238e7a8836b1a9ccf49456 Mon Sep 17 00:00:00 2001 From: Hironori Takahashi Date: Fri, 27 Mar 2026 21:45:46 +0900 Subject: [PATCH 1/2] feat: add GCP project integration for centralized logging and clasp run Bind all GAS projects to a standard GCP project for Cloud Logging, Error Reporting, and API usage monitoring. Enable clasp run for CI/CD Script Properties injection. - Add --gcp-project option to init.sh (clasp create --parentId) - Add setScriptProperties GAS function + set-properties.sh wrapper - Add auto-injection step in CD workflows (GitHub Actions + GitLab CI) - Add post-deploy hook examples for both platforms - Update README, setup docs, and template sync config --- .github/hooks/post-deploy.sh.example | 25 ++++++++++++ .github/workflows/cd.yml | 6 +++ .gitlab/cd.yml | 4 ++ .gitlab/post-deploy.yml.example | 24 +++++++++++ .templatesyncignore | 5 +++ README.ja.md | 32 +++++++++++++-- README.md | 32 +++++++++++++-- docs/setup-github.ja.md | 2 + docs/setup-github.md | 2 + docs/setup-gitlab.ja.md | 2 + docs/setup-gitlab.md | 2 + jest.config.json | 2 +- scripts/init.sh | 49 ++++++++++++++++++++--- scripts/set-properties.sh | 59 ++++++++++++++++++++++++++++ src/index.ts | 1 + src/setProperties.ts | 13 ++++++ 16 files changed, 247 insertions(+), 13 deletions(-) create mode 100644 .github/hooks/post-deploy.sh.example create mode 100644 .gitlab/post-deploy.yml.example create mode 100755 scripts/set-properties.sh create mode 100644 src/setProperties.ts diff --git a/.github/hooks/post-deploy.sh.example b/.github/hooks/post-deploy.sh.example new file mode 100644 index 0000000..9bc53bf --- /dev/null +++ b/.github/hooks/post-deploy.sh.example @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Post-deploy hook example: Set script properties after deployment +# +# To use: +# 1. Copy this file to .github/hooks/post-deploy.sh +# 2. Set SCRIPT_PROPERTIES as a GitHub environment secret (JSON string) +# e.g. {"API_KEY":"xxx","SLACK_WEBHOOK":"https://hooks.slack.com/..."} +# 3. Ensure GCP_PROJECT_NUMBER is set as an environment variable +# +# This hook runs automatically after every CD deployment. + +if [[ "$DEPLOY_STATUS" != "success" ]]; then + echo "Deploy failed — skipping script properties update." + exit 0 +fi + +if [[ -z "${SCRIPT_PROPERTIES:-}" ]]; then + echo "SCRIPT_PROPERTIES not set — skipping." + exit 0 +fi + +echo "Injecting script properties for ${DEPLOY_ENV}..." +./scripts/set-properties.sh --env SCRIPT_PROPERTIES diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 521ceb9..ba5e9cf 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -51,6 +51,12 @@ jobs: - name: Deploy new version run: pnpm exec clasp deploy -i "${{ vars.DEPLOYMENT_ID }}" --description "CD deploy from ${{ github.event.workflow_run.head_branch }}@${{ github.event.workflow_run.head_sha }}" + - name: Set script properties + if: vars.GCP_PROJECT_NUMBER != '' && secrets.SCRIPT_PROPERTIES != '' + run: ./scripts/set-properties.sh --env SCRIPT_PROPERTIES + env: + SCRIPT_PROPERTIES: ${{ secrets.SCRIPT_PROPERTIES }} + - name: Post-deploy hook if: always() && hashFiles('.github/hooks/post-deploy.sh') != '' run: bash .github/hooks/post-deploy.sh diff --git a/.gitlab/cd.yml b/.gitlab/cd.yml index 1fbf1b5..e2ec72e 100644 --- a/.gitlab/cd.yml +++ b/.gitlab/cd.yml @@ -13,6 +13,10 @@ - echo "$CLASP_JSON" > .clasp.json - pnpm exec clasp push -f - pnpm exec clasp deploy -i "$DEPLOYMENT_ID" --description "GitLab CD from $CI_COMMIT_REF_NAME@$CI_COMMIT_SHORT_SHA" + - | + if [[ -n "${GCP_PROJECT_NUMBER:-}" && -n "${SCRIPT_PROPERTIES:-}" ]]; then + ./scripts/set-properties.sh --env SCRIPT_PROPERTIES + fi cache: key: pnpm-$CI_COMMIT_REF_SLUG paths: diff --git a/.gitlab/post-deploy.yml.example b/.gitlab/post-deploy.yml.example new file mode 100644 index 0000000..5aa9425 --- /dev/null +++ b/.gitlab/post-deploy.yml.example @@ -0,0 +1,24 @@ +# Post-deploy hook example: Set script properties after deployment +# +# To use: +# 1. Copy this file to .gitlab/post-deploy.yml +# 2. Set SCRIPT_PROPERTIES as a CI/CD variable (JSON string, per environment) +# e.g. {"API_KEY":"xxx","SLACK_WEBHOOK":"https://hooks.slack.com/..."} +# 3. Ensure GCP_PROJECT_NUMBER is set as a CI/CD variable +# +# This job runs automatically after every CD deployment. + +post_deploy: + stage: deploy + image: node:24@sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b + before_script: + - corepack enable + - pnpm config set store-dir .pnpm-store + - pnpm install --frozen-lockfile + script: + - echo "$CLASPRC_JSON" > ~/.clasprc.json + - echo "$CLASP_JSON" > .clasp.json + - ./scripts/set-properties.sh --env SCRIPT_PROPERTIES + rules: + - if: $SCRIPT_PROPERTIES && $GCP_PROJECT_NUMBER + when: on_success diff --git a/.templatesyncignore b/.templatesyncignore index 122990e..31882ca 100644 --- a/.templatesyncignore +++ b/.templatesyncignore @@ -43,6 +43,11 @@ # Scripts :!scripts/init.sh +:!scripts/set-properties.sh + +# Hook examples +:!.github/hooks/post-deploy.sh.example +:!.gitlab/post-deploy.yml.example # Meta :!.templatesyncignore diff --git a/README.ja.md b/README.ja.md index 239c0c1..29c6135 100644 --- a/README.ja.md +++ b/README.ja.md @@ -94,24 +94,50 @@ Apps Script Fleet は各 GAS 機能を独立したリポジトリとして扱い 4. **各開発者**はパスワードマネージャーから `~/.clasprc.json` をローカルマシンにコピーします。 +### GCP プロジェクトの設定(任意、推奨) + +全 GAS プロジェクトを 1 つの標準 GCP プロジェクトに紐付けることで、Cloud Logging / Error Reporting / API 使用量の一元管理と、CI/CD からの `clasp run` による Script Properties 自動注入が可能になります。 + +**前提条件:** + +1. **標準 GCP プロジェクトを作成**(または既存のものを使用): [Google Cloud Console](https://console.cloud.google.com/) +2. **Apps Script API を有効化**: [API とサービス → API を有効化](https://console.cloud.google.com/apis/library/script.googleapis.com) +3. **OAuth 同意画面を設定**: [API とサービス → OAuth 同意画面](https://console.cloud.google.com/apis/credentials/consent) — Workspace 組織は「内部」を選択 +4. **プロジェクト番号を確認**(プロジェクト ID ではなく番号): [プロジェクト設定](https://console.cloud.google.com/iam-admin/settings) → プロジェクト番号 +5. **`GCP_PROJECT_NUMBER` を組織レベルの CI/CD 変数に設定**: + - **GitHub**: Organization variable → `GCP_PROJECT_NUMBER` + - **GitLab**: グループ → Settings → CI/CD → Variables → `GCP_PROJECT_NUMBER` + ### プロジェクトごとの初期化 `~/.clasprc.json` がローカルにある状態で、init スクリプトを実行すると GAS プロジェクトの作成と CI/CD 変数の設定を自動で行います: ```bash # GitHub: gh CLI で認証済みであること -./scripts/init.sh --title "My Script" +./scripts/init.sh --title "My Script" --gcp-project 123456789 # GitLab: GITLAB_TOKEN を設定してから実行 -GITLAB_TOKEN="glpat-xxx" ./scripts/init.sh --title "My Script" +GITLAB_TOKEN="glpat-xxx" ./scripts/init.sh --title "My Script" --gcp-project 123456789 ``` オプション: - `--title "名前"` — GAS プロジェクト名(デフォルト: ディレクトリ名) - `--type standalone|sheets|docs|slides|forms` — GAS プロジェクトタイプ(デフォルト: `standalone`) +- `--gcp-project <番号>` — 紐付ける GCP プロジェクト番号(Cloud Logging + `clasp run` が有効に) -スクリプトは dev/prod の GAS プロジェクトを作成し、初回デプロイを行い、`CLASP_JSON` + `DEPLOYMENT_ID` を CI/CD プラットフォームに設定します。 +スクリプトは dev/prod の GAS プロジェクトを作成し、初回デプロイを行い、`CLASP_JSON` + `DEPLOYMENT_ID` を CI/CD プラットフォームに設定します。`--gcp-project` を指定した場合、GAS プロジェクトが GCP プロジェクトに紐付けられ、`GCP_PROJECT_NUMBER` が CI/CD 変数として設定されます。 + +### CI/CD 経由の Script Properties 注入 + +GCP プロジェクト統合が設定されている場合、デプロイ時に Script Properties を自動注入できます: + +1. **`SCRIPT_PROPERTIES`** を CI/CD シークレットとして設定(環境ごとの JSON 文字列): + ```json + {"API_KEY":"xxx","SLACK_WEBHOOK":"https://hooks.slack.com/..."} + ``` +2. `GCP_PROJECT_NUMBER` と `SCRIPT_PROPERTIES` の両方が設定されている場合、`clasp deploy` 後に自動的にプロパティが注入されます +3. hook ベースの代替方法は `.github/hooks/post-deploy.sh.example` または `.gitlab/post-deploy.yml.example` を参照 ## クイックスタート diff --git a/README.md b/README.md index a978457..89dd773 100644 --- a/README.md +++ b/README.md @@ -94,24 +94,50 @@ Before your team can use Apps Script Fleet, an org admin needs to set up shared 4. **Each developer** copies `~/.clasprc.json` from the password manager to their local machine. +### GCP Project Setup (Optional, Recommended) + +Binding all GAS projects to a single standard GCP project enables centralized Cloud Logging, Error Reporting, API usage monitoring, and `clasp run` for CI/CD property injection. + +**Prerequisites:** + +1. **Create a standard GCP project** (or use an existing one) in [Google Cloud Console](https://console.cloud.google.com/) +2. **Enable the Apps Script API** on the project: [APIs & Services → Enable APIs](https://console.cloud.google.com/apis/library/script.googleapis.com) +3. **Configure the OAuth consent screen**: [APIs & Services → OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent) — set to "Internal" for Workspace organizations +4. **Note the project number** (not the project ID): [Project Settings](https://console.cloud.google.com/iam-admin/settings) → Project number +5. **Set `GCP_PROJECT_NUMBER` as an org-level CI/CD variable**: + - **GitHub**: Organization variable → `GCP_PROJECT_NUMBER` + - **GitLab**: Group → Settings → CI/CD → Variables → `GCP_PROJECT_NUMBER` + ### Per-Project Init Once `~/.clasprc.json` is on your machine, run the init script to create GAS projects and configure CI/CD variables automatically: ```bash # GitHub: gh CLI must be authenticated -./scripts/init.sh --title "My Script" +./scripts/init.sh --title "My Script" --gcp-project 123456789 # GitLab: set GITLAB_TOKEN first -GITLAB_TOKEN="glpat-xxx" ./scripts/init.sh --title "My Script" +GITLAB_TOKEN="glpat-xxx" ./scripts/init.sh --title "My Script" --gcp-project 123456789 ``` Options: - `--title "Name"` — GAS project title (default: directory name) - `--type standalone|sheets|docs|slides|forms` — GAS project type (default: `standalone`) +- `--gcp-project ` — GCP project number to bind (enables Cloud Logging + `clasp run`) -The script creates dev/prod GAS projects, deploys initial versions, and sets `CLASP_JSON` + `DEPLOYMENT_ID` on your CI/CD platform. +The script creates dev/prod GAS projects, deploys initial versions, and sets `CLASP_JSON` + `DEPLOYMENT_ID` on your CI/CD platform. When `--gcp-project` is specified, the projects are bound to the GCP project and `GCP_PROJECT_NUMBER` is set as a CI/CD variable. + +### Script Properties via CI/CD + +When GCP project integration is configured, you can automatically inject Script Properties during deployment: + +1. **Set `SCRIPT_PROPERTIES`** as a CI/CD secret (JSON string per environment): + ```json + {"API_KEY":"xxx","SLACK_WEBHOOK":"https://hooks.slack.com/..."} + ``` +2. Properties are injected automatically after `clasp deploy` when both `GCP_PROJECT_NUMBER` and `SCRIPT_PROPERTIES` are set +3. See `.github/hooks/post-deploy.sh.example` or `.gitlab/post-deploy.yml.example` for hook-based alternatives ## Quick Start diff --git a/docs/setup-github.ja.md b/docs/setup-github.ja.md index da0a7d0..c52f067 100644 --- a/docs/setup-github.ja.md +++ b/docs/setup-github.ja.md @@ -46,6 +46,8 @@ pnpm install | `production` | Secret: `CLASP_JSON` | `{"scriptId":"PROD_ID","rootDir":"dist"}` | | `production` | Variable: `DEPLOYMENT_ID` | prod のデプロイメント ID | +> **GCP プロジェクト統合時**: `CLASP_JSON` に `"projectId":"プロジェクト番号"` を追加します(例: `{"scriptId":"...","rootDir":"dist","projectId":"123456789"}`)。`init.sh --gcp-project` 使用時は自動設定されます。 + ### 4. 確認とデプロイ ``` diff --git a/docs/setup-github.md b/docs/setup-github.md index 3848a34..9932bfb 100644 --- a/docs/setup-github.md +++ b/docs/setup-github.md @@ -46,6 +46,8 @@ Create `.clasp-dev.json` and `.clasp-prod.json` (gitignored): | `production` | Secret: `CLASP_JSON` | `{"scriptId":"PROD_ID","rootDir":"dist"}` | | `production` | Variable: `DEPLOYMENT_ID` | Your prod deployment ID | +> **With GCP project**: Add `"projectId":"YOUR_PROJECT_NUMBER"` to `CLASP_JSON` (e.g., `{"scriptId":"...","rootDir":"dist","projectId":"123456789"}`). This is set automatically when using `init.sh --gcp-project`. + ### 4. Verify and deploy ``` diff --git a/docs/setup-gitlab.ja.md b/docs/setup-gitlab.ja.md index f88b7c8..885ecaa 100644 --- a/docs/setup-gitlab.ja.md +++ b/docs/setup-gitlab.ja.md @@ -98,6 +98,8 @@ Settings → CI/CD → Variables で以下を追加: | `DEPLOYMENT_ID` | `development` | dev のデプロイメント ID | | | `DEPLOYMENT_ID` | `production` | prod のデプロイメント ID | | +> **GCP プロジェクト統合時**: `CLASP_JSON` に `"projectId":"プロジェクト番号"` を追加します(例: `{"scriptId":"...","rootDir":"dist","projectId":"123456789"}`)。`init.sh --gcp-project` 使用時は自動設定されます。 + ### 4. Template Sync の設定 1. **Project Access Token** を作成(Settings → Access Tokens、`write_repository` スコープ) diff --git a/docs/setup-gitlab.md b/docs/setup-gitlab.md index ee735c0..1ea163a 100644 --- a/docs/setup-gitlab.md +++ b/docs/setup-gitlab.md @@ -98,6 +98,8 @@ Go to Settings → CI/CD → Variables and add: | `DEPLOYMENT_ID` | `development` | Your dev deployment ID | | | `DEPLOYMENT_ID` | `production` | Your prod deployment ID | | +> **With GCP project**: Add `"projectId":"YOUR_PROJECT_NUMBER"` to `CLASP_JSON` (e.g., `{"scriptId":"...","rootDir":"dist","projectId":"123456789"}`). This is set automatically when using `init.sh --gcp-project`. + ### 4. Set up Template Sync 1. Create a **Project Access Token** (Settings → Access Tokens) with `write_repository` scope diff --git a/jest.config.json b/jest.config.json index dbfeeef..359386b 100644 --- a/jest.config.json +++ b/jest.config.json @@ -6,7 +6,7 @@ "moduleNameMapper": { "(.+)\\.js": "$1" }, - "collectCoverageFrom": ["src/**/*.ts", "!src/index.ts"], + "collectCoverageFrom": ["src/**/*.ts", "!src/index.ts", "!src/setProperties.ts"], "coverageThreshold": { "global": { "branches": 80, diff --git a/scripts/init.sh b/scripts/init.sh index 1f23a06..327bff3 100755 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -9,10 +9,11 @@ set -euo pipefail # - Node.js + pnpm installed # # Usage: -# ./scripts/init.sh [--title "Project Name"] [--type standalone|sheets|docs|slides|forms] +# ./scripts/init.sh [--title "Project Name"] [--type standalone|sheets|docs|slides|forms] [--gcp-project ] TITLE="" GAS_TYPE="standalone" +GCP_PROJECT="" VALID_TYPES="standalone sheets docs slides forms" while [[ $# -gt 0 ]]; do @@ -25,6 +26,10 @@ while [[ $# -gt 0 ]]; do GAS_TYPE="$2" shift 2 ;; + --gcp-project) + GCP_PROJECT="$2" + shift 2 + ;; *) echo "Unknown option: $1" >&2 exit 1 @@ -135,8 +140,12 @@ setup_github() { local dev_script_id="$1" dev_deployment_id="$2" local prod_script_id="$3" prod_deployment_id="$4" - local dev_clasp="{\"scriptId\":\"${dev_script_id}\",\"rootDir\":\"dist\"}" - local prod_clasp="{\"scriptId\":\"${prod_script_id}\",\"rootDir\":\"dist\"}" + local project_id_field="" + if [[ -n "$GCP_PROJECT" ]]; then + project_id_field=",\"projectId\":\"${GCP_PROJECT}\"" + fi + local dev_clasp="{\"scriptId\":\"${dev_script_id}\",\"rootDir\":\"dist\"${project_id_field}}" + local prod_clasp="{\"scriptId\":\"${prod_script_id}\",\"rootDir\":\"dist\"${project_id_field}}" echo "Setting GitHub secrets/variables..." gh_ensure_environment "development" @@ -145,6 +154,11 @@ setup_github() { gh_set_variable "DEPLOYMENT_ID" "$dev_deployment_id" "development" gh_set_secret "CLASP_JSON" "$prod_clasp" "production" gh_set_variable "DEPLOYMENT_ID" "$prod_deployment_id" "production" + + if [[ -n "$GCP_PROJECT" ]]; then + gh_set_variable "GCP_PROJECT_NUMBER" "$GCP_PROJECT" "development" + gh_set_variable "GCP_PROJECT_NUMBER" "$GCP_PROJECT" "production" + fi } # --------------------------------------------------------------------------- @@ -203,8 +217,12 @@ setup_gitlab() { echo "Setting GitLab CI/CD variables (project ID: ${project_id})..." - local dev_clasp="{\"scriptId\":\"${dev_script_id}\",\"rootDir\":\"dist\"}" - local prod_clasp="{\"scriptId\":\"${prod_script_id}\",\"rootDir\":\"dist\"}" + local project_id_field="" + if [[ -n "$GCP_PROJECT" ]]; then + project_id_field=",\"projectId\":\"${GCP_PROJECT}\"" + fi + local dev_clasp="{\"scriptId\":\"${dev_script_id}\",\"rootDir\":\"dist\"${project_id_field}}" + local prod_clasp="{\"scriptId\":\"${prod_script_id}\",\"rootDir\":\"dist\"${project_id_field}}" # dev: protected=false (dev branch is typically not protected) gl_set_variable "$project_id" "CLASP_JSON" "$dev_clasp" "development" "false" @@ -212,6 +230,11 @@ setup_gitlab() { # prod: protected=true gl_set_variable "$project_id" "CLASP_JSON" "$prod_clasp" "production" "true" gl_set_variable "$project_id" "DEPLOYMENT_ID" "$prod_deployment_id" "production" "true" + + if [[ -n "$GCP_PROJECT" ]]; then + gl_set_variable "$project_id" "GCP_PROJECT_NUMBER" "$GCP_PROJECT" "development" "false" + gl_set_variable "$project_id" "GCP_PROJECT_NUMBER" "$GCP_PROJECT" "production" "true" + fi } # --------------------------------------------------------------------------- @@ -230,7 +253,11 @@ clasp_create_and_deploy() { echo "Creating GAS project: ${full_title}..." rm -f .clasp.json - pnpm exec clasp create --title "$full_title" --type "$GAS_TYPE" --rootDir dist 2>&1 + local clasp_args=(create --title "$full_title" --type "$GAS_TYPE" --rootDir dist) + if [[ -n "$GCP_PROJECT" ]]; then + clasp_args+=(--parentId "$GCP_PROJECT") + fi + pnpm exec clasp "${clasp_args[@]}" 2>&1 [[ -f .clasp.json ]] || die "clasp create failed — .clasp.json not generated." @@ -284,6 +311,9 @@ PLATFORM=$(detect_platform) echo "Platform: ${PLATFORM}" echo "Title: ${TITLE}" echo "Type: ${GAS_TYPE}" +if [[ -n "$GCP_PROJECT" ]]; then + echo "GCP Project: ${GCP_PROJECT}" +fi echo "" # Create dev and prod projects @@ -307,3 +337,10 @@ echo "Done! CI/CD variables are configured." echo "" echo " dev script: https://script.google.com/d/${DEV_SCRIPT_ID}/edit" echo " prod script: https://script.google.com/d/${PROD_SCRIPT_ID}/edit" +if [[ -n "$GCP_PROJECT" ]]; then + echo "" + echo " GCP project: https://console.cloud.google.com/home/dashboard?project=${GCP_PROJECT}" + echo "" + echo " clasp run is enabled. To set script properties:" + echo " ./scripts/set-properties.sh --json '{\"KEY\":\"value\"}'" +fi diff --git a/scripts/set-properties.sh b/scripts/set-properties.sh new file mode 100755 index 0000000..712a1f8 --- /dev/null +++ b/scripts/set-properties.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Set Script Properties via clasp run +# +# Prerequisites: +# - ~/.clasprc.json exists +# - .clasp.json exists (with projectId for clasp run) +# - The GAS project is bound to a standard GCP project +# - Apps Script API is enabled on the GCP project +# +# Usage: +# # From a JSON file +# ./scripts/set-properties.sh --file properties.json +# +# # From an environment variable (JSON string) +# ./scripts/set-properties.sh --env SCRIPT_PROPERTIES +# +# # From inline JSON +# ./scripts/set-properties.sh --json '{"API_KEY":"xxx","ENV":"production"}' + +PROPS_JSON="" + +while [[ $# -gt 0 ]]; do + case $1 in + --file) + [[ -f "$2" ]] || { echo "Error: File not found: $2" >&2; exit 1; } + PROPS_JSON=$(cat "$2") + shift 2 + ;; + --env) + PROPS_JSON="${!2:-}" + [[ -n "$PROPS_JSON" ]] || { echo "Error: Environment variable $2 is empty or not set." >&2; exit 1; } + shift 2 + ;; + --json) + PROPS_JSON="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" >&2 + echo "Usage: $0 --file | --env | --json ''" >&2 + exit 1 + ;; + esac +done + +if [[ -z "$PROPS_JSON" ]]; then + echo "Error: No properties provided. Use --file, --env, or --json." >&2 + exit 1 +fi + +# Validate JSON +node -e "JSON.parse(process.argv[1])" "$PROPS_JSON" 2>/dev/null \ + || { echo "Error: Invalid JSON: ${PROPS_JSON}" >&2; exit 1; } + +echo "Setting script properties via clasp run..." +pnpm exec clasp run setScriptProperties --params "[${PROPS_JSON}]" +echo "Done." diff --git a/src/index.ts b/src/index.ts index 0c29f1c..70c847d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import { getGreeting } from "./greeting.js"; +import { setScriptProperties } from "./setProperties.js"; function doGet(): GoogleAppsScript.HTML.HtmlOutput { return HtmlService.createHtmlOutputFromFile("app") diff --git a/src/setProperties.ts b/src/setProperties.ts new file mode 100644 index 0000000..3f070d9 --- /dev/null +++ b/src/setProperties.ts @@ -0,0 +1,13 @@ +/** + * Set script properties via clasp run. + * Called from CI/CD to inject environment-specific configuration. + * + * @param props - Key-value pairs to set as script properties + * @returns The properties that were set (for confirmation) + */ +export function setScriptProperties( + props: Record, +): Record { + PropertiesService.getScriptProperties().setProperties(props, false); + return props; +} From 4b0cba2ad51cf9f074f9f82769cfd4309f697df6 Mon Sep 17 00:00:00 2001 From: Hironori Takahashi Date: Fri, 27 Mar 2026 21:49:29 +0900 Subject: [PATCH 2/2] fix: validate GCP project number and prevent secret leaks in error output --- scripts/init.sh | 3 +++ scripts/set-properties.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/init.sh b/scripts/init.sh index 327bff3..35bcc4a 100755 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -300,6 +300,9 @@ echo "" require_cmd pnpm require_cmd node validate_type "$GAS_TYPE" +if [[ -n "$GCP_PROJECT" ]] && ! [[ "$GCP_PROJECT" =~ ^[0-9]+$ ]]; then + die "--gcp-project must be a numeric project number (not project ID). Got: ${GCP_PROJECT}" +fi # Default title from directory name if [[ -z "$TITLE" ]]; then diff --git a/scripts/set-properties.sh b/scripts/set-properties.sh index 712a1f8..2cd9e44 100755 --- a/scripts/set-properties.sh +++ b/scripts/set-properties.sh @@ -52,7 +52,7 @@ fi # Validate JSON node -e "JSON.parse(process.argv[1])" "$PROPS_JSON" 2>/dev/null \ - || { echo "Error: Invalid JSON: ${PROPS_JSON}" >&2; exit 1; } + || { echo "Error: Invalid JSON provided." >&2; exit 1; } echo "Setting script properties via clasp run..." pnpm exec clasp run setScriptProperties --params "[${PROPS_JSON}]"