diff --git a/scripts/delivery/deploy.sh b/scripts/delivery/deploy.sh index f694866..0113899 100755 --- a/scripts/delivery/deploy.sh +++ b/scripts/delivery/deploy.sh @@ -102,7 +102,10 @@ wait_for_public_health() { cleanup_secret_versions() { local secret_id="$1" keep="$2" version - gcloud secrets versions list "$secret_id" --project "$GCP_PROJECT_ID" --filter='state=ENABLED' \ + # Disabled versions still retain their payload and count as active Secret Manager + # versions. Keep the newest rollback window across both enabled and disabled + # versions; already-destroyed versions are immutable and need no action. + gcloud secrets versions list "$secret_id" --project "$GCP_PROJECT_ID" --filter='state!=DESTROYED' \ --format='value(name)' --sort-by='~createTime' | tail -n "+$((keep + 1))" | while read -r version; do gcloud secrets versions destroy "${version##*/}" --secret="$secret_id" --project "$GCP_PROJECT_ID" --quiet || diff --git a/scripts/delivery/platform-context.sh b/scripts/delivery/platform-context.sh index f515743..f92cb4c 100755 --- a/scripts/delivery/platform-context.sh +++ b/scripts/delivery/platform-context.sh @@ -15,10 +15,11 @@ infra::require_tofu_version "$tofu_bin" project_id="$(infra::read_tfvar "$tfvars" project_id)" region="$(infra::read_tfvar "$tfvars" region)" artifact_location="$(infra::read_tfvar "$tfvars" artifact_location)" +state_bucket="$(infra::state_bucket_name "$project_id")" stack_dir="${repo_root}/infra/stacks/platform" "$tofu_bin" -chdir="$stack_dir" init -reconfigure -input=false \ - -backend-config="bucket=${project_id}-proxy-builder-tfstate" \ + -backend-config="bucket=${state_bucket}" \ -backend-config="prefix=platform" >/dev/null outputs="$($tofu_bin -chdir="$stack_dir" output -json)" diff --git a/scripts/github/audit.sh b/scripts/github/audit.sh index 79b7a97..3bdbb5f 100755 --- a/scripts/github/audit.sh +++ b/scripts/github/audit.sh @@ -14,6 +14,8 @@ case "$environment" in development|production) ;; *) exit 2 ;; esac for command_name in gh jq; do command -v "$command_name" >/dev/null 2>&1 || { printf 'missing command: %s\n' "$command_name" >&2; exit 1; }; done repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +# shellcheck source=../infra/common.sh +source "${repo_root}/scripts/infra/common.sh" tfvars="${repo_root}/infra/environments/${environment}.tfvars" expected_repo="$(sed -nE 's/^github_repository[[:space:]]*=[[:space:]]*"([^"]+)"/\1/p' "$tfvars")" expected_repo_id="$(sed -nE 's/^github_repository_id[[:space:]]*=[[:space:]]*"([^"]+)"/\1/p' "$tfvars")" @@ -24,11 +26,12 @@ actual_repo_id="$(gh api "repos/${repo}" --jq '.id')" if [[ -z "$bootstrap_output" ]]; then command -v tofu >/dev/null 2>&1 || { printf '%s\n' 'missing command: tofu' >&2; exit 1; } project_id="$(sed -nE 's/^project_id[[:space:]]*=[[:space:]]*"([^"]+)"/\1/p' "$tfvars")" + state_bucket="$(infra::state_bucket_name "$project_id")" work_dir="$(mktemp -d "${TMPDIR:-/tmp}/proxy-builder-bootstrap-audit.XXXXXX")" trap 'rm -rf "$work_dir"' EXIT bootstrap_output="${work_dir}/bootstrap.json" tofu -chdir="${repo_root}/infra/stacks/bootstrap" init -reconfigure -input=false \ - -backend-config="bucket=${project_id}-proxy-builder-tfstate" -backend-config="prefix=bootstrap" >/dev/null + -backend-config="bucket=${state_bucket}" -backend-config="prefix=bootstrap" >/dev/null tofu -chdir="${repo_root}/infra/stacks/bootstrap" output -json >"$bootstrap_output" fi [[ -f "$bootstrap_output" ]] || { printf '%s\n' 'bootstrap output file is required' >&2; exit 2; } diff --git a/scripts/github/configure.sh b/scripts/github/configure.sh index 37bc583..2c68ab0 100755 --- a/scripts/github/configure.sh +++ b/scripts/github/configure.sh @@ -16,16 +16,19 @@ case "$environment" in development|production) ;; *) exit 2 ;; esac for command_name in gh jq; do command -v "$command_name" >/dev/null 2>&1 || { printf 'missing command: %s\n' "$command_name" >&2; exit 1; }; done repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +# shellcheck source=../infra/common.sh +source "${repo_root}/scripts/infra/common.sh" if [[ -z "$bootstrap_output" ]]; then command -v tofu >/dev/null 2>&1 || { printf '%s\n' 'missing command: tofu' >&2; exit 1; } tfvars="${repo_root}/infra/environments/${environment}.tfvars" project_id="$(sed -nE 's/^project_id[[:space:]]*=[[:space:]]*"([^"]+)"/\1/p' "$tfvars")" [[ -n "$project_id" ]] || { printf '%s\n' 'project_id is missing from environment tfvars' >&2; exit 1; } + state_bucket="$(infra::state_bucket_name "$project_id")" work_dir="$(mktemp -d "${TMPDIR:-/tmp}/proxy-builder-bootstrap-output.XXXXXX")" trap 'rm -rf "$work_dir"' EXIT bootstrap_output="${work_dir}/bootstrap.json" tofu -chdir="${repo_root}/infra/stacks/bootstrap" init -reconfigure -input=false \ - -backend-config="bucket=${project_id}-proxy-builder-tfstate" -backend-config="prefix=bootstrap" >/dev/null + -backend-config="bucket=${state_bucket}" -backend-config="prefix=bootstrap" >/dev/null tofu -chdir="${repo_root}/infra/stacks/bootstrap" output -json >"$bootstrap_output" fi [[ -f "$bootstrap_output" ]] || { printf '%s\n' 'bootstrap output file is required' >&2; exit 2; } diff --git a/scripts/infra/bootstrap-state.sh b/scripts/infra/bootstrap-state.sh index ce869ac..348f415 100755 --- a/scripts/infra/bootstrap-state.sh +++ b/scripts/infra/bootstrap-state.sh @@ -12,7 +12,7 @@ infra::require_command gcloud project_id=$(infra::read_tfvar "$tfvars" project_id) region=$(infra::read_tfvar "$tfvars" region) -bucket="${project_id}-proxy-builder-tfstate" +bucket=$(infra::state_bucket_name "$project_id") active_account=$(gcloud auth list --filter=status:ACTIVE --format='value(account)' | head -n 1) [[ -n "$active_account" ]] || infra::die "gcloud 没有活动账号,请先完成本地登录" diff --git a/scripts/infra/common.sh b/scripts/infra/common.sh index 4ca85ce..e9b90bd 100755 --- a/scripts/infra/common.sh +++ b/scripts/infra/common.sh @@ -43,6 +43,12 @@ infra::read_tfvar() { printf '%s\n' "$value" } +infra::state_bucket_name() { + local project_id="${1:-}" + [[ -n "$project_id" ]] || infra::die "state bucket 缺少 project_id" + printf '%s-proxy-builder-tfstate\n' "$project_id" +} + infra::tofu_bin() { local tofu_bin="${TOFU_BIN:-tofu}" command -v "$tofu_bin" >/dev/null 2>&1 || infra::die "缺少 OpenTofu;安装 1.12.6 或设置 TOFU_BIN" diff --git a/scripts/infra/destroy-platform.sh b/scripts/infra/destroy-platform.sh index 5bedd83..b3f74b5 100755 --- a/scripts/infra/destroy-platform.sh +++ b/scripts/infra/destroy-platform.sh @@ -20,7 +20,7 @@ fi tofu_bin=$(infra::tofu_bin) infra::require_tofu_version "$tofu_bin" -bucket="${project_id}-proxy-builder-tfstate" +bucket=$(infra::state_bucket_name "$project_id") stack_dir="${INFRA_ROOT}/infra/stacks/platform" "$tofu_bin" -chdir="$stack_dir" init -reconfigure \ diff --git a/scripts/infra/test-guards.sh b/scripts/infra/test-guards.sh index b9cff97..862a91a 100755 --- a/scripts/infra/test-guards.sh +++ b/scripts/infra/test-guards.sh @@ -36,6 +36,8 @@ production_project=$(infra::read_tfvar "$production_file" project_id) [[ "$development_project" != "$production_project" ]] || infra::die "两个环境不能共用 GCP Project" [[ "$development_project" == "kyson-proxy-dev" ]] || infra::die "development 必须使用 kyson-proxy-dev Project" [[ "$production_project" == "kyson-proxy-prod" ]] || infra::die "production 必须使用 kyson-proxy-prod Project" +[[ "$(infra::state_bucket_name "$development_project")" == "${development_project}-proxy-builder-tfstate" ]] || infra::die "development state bucket 命名不正确" +[[ "$(infra::state_bucket_name "$production_project")" == "${production_project}-proxy-builder-tfstate" ]] || infra::die "production state bucket 命名不正确" [[ "$(infra::read_tfvar "$development_file" resource_prefix)" == "proxy" ]] || infra::die "development 必须使用 proxy 完整资源名前缀" [[ "$(infra::read_tfvar "$production_file" resource_prefix)" == "proxy" ]] || infra::die "production 必须使用 proxy 完整资源名前缀" diff --git a/scripts/infra/tofu-stack.sh b/scripts/infra/tofu-stack.sh index 29771a6..0f3938f 100755 --- a/scripts/infra/tofu-stack.sh +++ b/scripts/infra/tofu-stack.sh @@ -21,7 +21,7 @@ tofu_bin=$(infra::tofu_bin) infra::require_tofu_version "$tofu_bin" project_id=$(infra::read_tfvar "$tfvars" project_id) -bucket="${project_id}-proxy-builder-tfstate" +bucket=$(infra::state_bucket_name "$project_id") stack_dir="${INFRA_ROOT}/infra/stacks/${stack}" "$tofu_bin" -chdir="$stack_dir" init -reconfigure \ diff --git a/scripts/secrets/subscription-url.sh b/scripts/secrets/subscription-url.sh index 252e3e4..114e0bc 100755 --- a/scripts/secrets/subscription-url.sh +++ b/scripts/secrets/subscription-url.sh @@ -25,7 +25,7 @@ token_encoded="$(jq -nr --arg value "$token" '$value | @uri')" tofu_bin="$(infra::tofu_bin)" infra::require_tofu_version "$tofu_bin" project_id="$(infra::read_tfvar "$tfvars" project_id)" -bucket="${project_id}-proxy-builder-tfstate" +bucket="$(infra::state_bucket_name "$project_id")" stack_dir="${repo_root}/infra/stacks/platform" "$tofu_bin" -chdir="$stack_dir" init -reconfigure -input=false \ -backend-config="bucket=${bucket}" -backend-config="prefix=platform" >/dev/null