Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/hooks/post-deploy.sh.example
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions .gitlab/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
24 changes: 24 additions & 0 deletions .gitlab/post-deploy.yml.example
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions .templatesyncignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 29 additions & 3 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` を参照

## クイックスタート

Expand Down
32 changes: 29 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <NUMBER>` — 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

Expand Down
2 changes: 2 additions & 0 deletions docs/setup-github.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. 確認とデプロイ

```
Expand Down
2 changes: 2 additions & 0 deletions docs/setup-github.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

```
Expand Down
2 changes: 2 additions & 0 deletions docs/setup-gitlab.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` スコープ)
Expand Down
2 changes: 2 additions & 0 deletions docs/setup-gitlab.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion jest.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
52 changes: 46 additions & 6 deletions scripts/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 <PROJECT_NUMBER>]

TITLE=""
GAS_TYPE="standalone"
GCP_PROJECT=""
VALID_TYPES="standalone sheets docs slides forms"

while [[ $# -gt 0 ]]; do
Expand All @@ -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
Expand Down Expand Up @@ -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"
Expand All @@ -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
}

# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -203,15 +217,24 @@ 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"
gl_set_variable "$project_id" "DEPLOYMENT_ID" "$dev_deployment_id" "development" "false"
# 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
}

# ---------------------------------------------------------------------------
Expand All @@ -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."

Expand Down Expand Up @@ -273,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
Expand All @@ -284,6 +314,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
Expand All @@ -307,3 +340,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
Loading
Loading