ci(secrets->kms): migrate CP_ADMIN_API_TOKEN to Infisical KMS (wave-3 molecule-ai/molecule-core) #3274
@@ -772,7 +772,10 @@ jobs:
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CP_URL: ${{ vars.PROD_CP_URL || 'https://api.moleculesai.app' }}
|
||||
CP_ADMIN_API_TOKEN: ${{ secrets.CP_ADMIN_API_TOKEN }}
|
||||
# CP_ADMIN_API_TOKEN now arrives via $GITHUB_ENV from the "Fetch
|
||||
# CP_ADMIN_API_TOKEN from Infisical KMS" step below (wave-3 migration);
|
||||
# it is the Infisical SSOT value. The "Build deploy plan" step still
|
||||
# validates it is non-empty before any production rollout.
|
||||
GITEA_HOST: git.moleculesai.app
|
||||
GITEA_TOKEN: ${{ secrets.PROD_AUTO_DEPLOY_CONTROL_TOKEN || secrets.AUTO_SYNC_TOKEN }}
|
||||
CI_STATUS_TIMEOUT_SECONDS: "3600"
|
||||
@@ -804,6 +807,50 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
# KMS wave-3 (Gitea-Actions-secrets -> Infisical SSOT): source
|
||||
# CP_ADMIN_API_TOKEN from Infisical (prod /shared/controlplane-admin) via
|
||||
# the CI machine identity instead of the duplicated Gitea Actions secret,
|
||||
# mirroring this repo's boot-to-registration-e2e KMS pattern. Exported to
|
||||
# $GITHUB_ENV so the plan + redeploy steps read it as a masked env var.
|
||||
#
|
||||
# TRUSTED-REF GATING: molecule-core is a PUBLIC repo; this step consumes
|
||||
# the INFISICAL_CI_* machine-identity secrets, so it must only run on a
|
||||
# TRUSTED event — a push to the protected main branch or an operator
|
||||
# workflow_dispatch. This workflow has no pull_request trigger, so an
|
||||
# untrusted-fork PR cannot reach it.
|
||||
- name: Fetch CP_ADMIN_API_TOKEN from Infisical KMS
|
||||
id: cp_admin_token
|
||||
if: >-
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'push' && github.ref == 'refs/heads/main')
|
||||
env:
|
||||
INFISICAL_CI_CLIENT_ID: ${{ secrets.INFISICAL_CI_CLIENT_ID }}
|
||||
INFISICAL_CI_CLIENT_SECRET: ${{ secrets.INFISICAL_CI_CLIENT_SECRET }}
|
||||
INFISICAL_PROJECT_ID: ${{ secrets.INFISICAL_CI_PROJECT_ID }}
|
||||
run: |
|
||||
set -uo pipefail
|
||||
BASE="https://key.moleculesai.app"
|
||||
TOK=$(curl -fsS -X POST "$BASE/api/v1/auth/universal-auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"clientId\":\"$INFISICAL_CI_CLIENT_ID\",\"clientSecret\":\"$INFISICAL_CI_CLIENT_SECRET\"}" \
|
||||
| python3 -c 'import sys,json;print(json.load(sys.stdin)["accessToken"])')
|
||||
read_secret() {
|
||||
curl -fsS "$BASE/api/v3/secrets/raw/$1?workspaceId=$INFISICAL_PROJECT_ID&environment=prod&secretPath=$2" \
|
||||
-H "Authorization: Bearer $TOK" \
|
||||
| python3 -c 'import sys,json; v=json.load(sys.stdin).get("secret",{}).get("secretValue"); sys.stdout.write(v if isinstance(v,str) else "")'
|
||||
}
|
||||
CP_ADMIN_API_TOKEN=$(read_secret CP_ADMIN_API_TOKEN %2Fshared%2Fcontrolplane-admin)
|
||||
echo "::add-mask::$CP_ADMIN_API_TOKEN"
|
||||
# Masking-control: production auto-deploy is side-effecting. Assert
|
||||
# NON-EMPTY and FAIL LOUD — never roll the prod fleet with an empty
|
||||
# admin token.
|
||||
if [ -z "$CP_ADMIN_API_TOKEN" ]; then
|
||||
echo "::error::Infisical returned empty CP_ADMIN_API_TOKEN at prod /shared/controlplane-admin — failing closed."
|
||||
exit 1
|
||||
fi
|
||||
echo "CP_ADMIN_API_TOKEN=$CP_ADMIN_API_TOKEN" >> "$GITHUB_ENV"
|
||||
echo "CP_ADMIN_API_TOKEN loaded from Infisical (len=${#CP_ADMIN_API_TOKEN})"
|
||||
|
||||
- name: Build deploy plan
|
||||
id: plan
|
||||
run: |
|
||||
|
||||
@@ -88,6 +88,50 @@ jobs:
|
||||
# integrated auto-deploy workflow.
|
||||
PROD_AUTO_DEPLOY_DISABLED: ${{ vars.PROD_AUTO_DEPLOY_DISABLED || secrets.PROD_AUTO_DEPLOY_DISABLED || '' }}
|
||||
steps:
|
||||
# KMS wave-3 (Gitea-Actions-secrets -> Infisical SSOT): source
|
||||
# CP_ADMIN_API_TOKEN from Infisical (prod /shared/controlplane-admin) via
|
||||
# the CI machine identity instead of the duplicated Gitea Actions secret,
|
||||
# mirroring the molecule-controlplane bench-provision-time bootstrap and
|
||||
# this repo's boot-to-registration-e2e KMS pattern. The value is exported
|
||||
# to $GITHUB_ENV so the "Call CP redeploy-fleet" step reads it as an
|
||||
# ordinary masked env var.
|
||||
#
|
||||
# TRUSTED-REF GATING: molecule-core is a PUBLIC repo; this step consumes
|
||||
# the INFISICAL_CI_* machine-identity secrets, so it must only run on a
|
||||
# TRUSTED event. This workflow is workflow_dispatch-only, so the guard
|
||||
# matches that; it is defense-in-depth if other triggers are ever added.
|
||||
- name: Fetch CP_ADMIN_API_TOKEN from Infisical KMS
|
||||
id: cp_admin_token
|
||||
if: >-
|
||||
github.event_name == 'workflow_dispatch'
|
||||
env:
|
||||
INFISICAL_CI_CLIENT_ID: ${{ secrets.INFISICAL_CI_CLIENT_ID }}
|
||||
INFISICAL_CI_CLIENT_SECRET: ${{ secrets.INFISICAL_CI_CLIENT_SECRET }}
|
||||
INFISICAL_PROJECT_ID: ${{ secrets.INFISICAL_CI_PROJECT_ID }}
|
||||
run: |
|
||||
set -uo pipefail
|
||||
BASE="https://key.moleculesai.app"
|
||||
TOK=$(curl -fsS -X POST "$BASE/api/v1/auth/universal-auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"clientId\":\"$INFISICAL_CI_CLIENT_ID\",\"clientSecret\":\"$INFISICAL_CI_CLIENT_SECRET\"}" \
|
||||
| python3 -c 'import sys,json;print(json.load(sys.stdin)["accessToken"])')
|
||||
read_secret() {
|
||||
curl -fsS "$BASE/api/v3/secrets/raw/$1?workspaceId=$INFISICAL_PROJECT_ID&environment=prod&secretPath=$2" \
|
||||
-H "Authorization: Bearer $TOK" \
|
||||
| python3 -c 'import sys,json; v=json.load(sys.stdin).get("secret",{}).get("secretValue"); sys.stdout.write(v if isinstance(v,str) else "")'
|
||||
}
|
||||
CP_ADMIN_API_TOKEN=$(read_secret CP_ADMIN_API_TOKEN %2Fshared%2Fcontrolplane-admin)
|
||||
echo "::add-mask::$CP_ADMIN_API_TOKEN"
|
||||
# Masking-control: Infisical is the authoritative source for this token.
|
||||
# Assert NON-EMPTY and FAIL LOUD — a side-effecting prod redeploy must
|
||||
# never run with an empty admin token.
|
||||
if [ -z "$CP_ADMIN_API_TOKEN" ]; then
|
||||
echo "::error::Infisical returned empty CP_ADMIN_API_TOKEN at prod /shared/controlplane-admin — failing closed."
|
||||
exit 1
|
||||
fi
|
||||
echo "CP_ADMIN_API_TOKEN=$CP_ADMIN_API_TOKEN" >> "$GITHUB_ENV"
|
||||
echo "CP_ADMIN_API_TOKEN loaded from Infisical (len=${#CP_ADMIN_API_TOKEN})"
|
||||
|
||||
- name: Kill-switch guard
|
||||
# Rule 9 fix: exit fast if kill switch is set. No redeploy happens.
|
||||
if: env.PROD_AUTO_DEPLOY_DISABLED == 'true'
|
||||
@@ -124,13 +168,12 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Call CP redeploy-fleet
|
||||
# CP_ADMIN_API_TOKEN must be set as a repo/org secret on
|
||||
# molecule-ai/molecule-core, matching the staging/prod CP's
|
||||
# CP_ADMIN_API_TOKEN env. Stored in Railway, mirrored to this
|
||||
# repo's secrets for CI.
|
||||
# CP_ADMIN_API_TOKEN now arrives via $GITHUB_ENV from the "Fetch
|
||||
# CP_ADMIN_API_TOKEN from Infisical KMS" step above (wave-3 migration);
|
||||
# it is the Infisical SSOT value, matching the staging/prod CP's
|
||||
# CP_ADMIN_API_TOKEN env. The [ -z ... ] guard below still validates it.
|
||||
env:
|
||||
CP_URL: ${{ vars.CP_URL || 'https://api.moleculesai.app' }}
|
||||
CP_ADMIN_API_TOKEN: ${{ secrets.CP_ADMIN_API_TOKEN }}
|
||||
TARGET_TAG: ${{ steps.tag.outputs.target_tag }}
|
||||
CANARY_SLUG: ${{ vars.PROD_REDEPLOY_CANARY_SLUG || secrets.PROD_REDEPLOY_CANARY_SLUG || '' }}
|
||||
SOAK_SECONDS: ${{ vars.PROD_REDEPLOY_SOAK_SECONDS || secrets.PROD_REDEPLOY_SOAK_SECONDS || '' }}
|
||||
|
||||
@@ -219,15 +219,61 @@ jobs:
|
||||
env:
|
||||
SHA: ${{ needs.staging-smoke.outputs.sha }}
|
||||
CP_URL: ${{ vars.CP_URL || 'https://staging-api.moleculesai.app' }}
|
||||
# CP_ADMIN_API_TOKEN gates write access to the redeploy endpoint.
|
||||
# Stored at the repo level so all workflows pick it up automatically.
|
||||
CP_ADMIN_API_TOKEN: ${{ secrets.CP_ADMIN_API_TOKEN }}
|
||||
# CP_ADMIN_API_TOKEN now arrives via $GITHUB_ENV from the "Fetch
|
||||
# CP_ADMIN_API_TOKEN from Infisical KMS" step below (wave-3 migration);
|
||||
# it is the Infisical SSOT value and gates write access to the redeploy
|
||||
# endpoint. The "Check CP credentials" step still validates it is set.
|
||||
# canary_slug pin: deploy the verified :staging-<sha> to the canary
|
||||
# first (soak 120s), then fan out to the rest of the fleet.
|
||||
CANARY_SLUG: ${{ vars.CANARY_PROMOTE_SLUG || '' }}
|
||||
SOAK_SECONDS: ${{ vars.CANARY_PROMOTE_SOAK || '120' }}
|
||||
BATCH_SIZE: ${{ vars.CANARY_PROMOTE_BATCH || '3' }}
|
||||
steps:
|
||||
# KMS wave-3 (Gitea-Actions-secrets -> Infisical SSOT): source
|
||||
# CP_ADMIN_API_TOKEN from Infisical (prod /shared/controlplane-admin) via
|
||||
# the CI machine identity instead of the duplicated Gitea Actions secret,
|
||||
# mirroring this repo's boot-to-registration-e2e KMS pattern. Exported to
|
||||
# $GITHUB_ENV so the credential-check + promote steps read it as a masked
|
||||
# env var.
|
||||
#
|
||||
# TRUSTED-REF GATING: molecule-core is a PUBLIC repo; this step consumes
|
||||
# the INFISICAL_CI_* machine-identity secrets, so it must only run on a
|
||||
# TRUSTED event — a push to the protected staging branch, or an operator
|
||||
# workflow_dispatch. This workflow has no pull_request trigger, so an
|
||||
# untrusted-fork PR cannot reach it.
|
||||
- name: Fetch CP_ADMIN_API_TOKEN from Infisical KMS
|
||||
id: cp_admin_token
|
||||
if: >-
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'push' && github.ref == 'refs/heads/staging')
|
||||
env:
|
||||
INFISICAL_CI_CLIENT_ID: ${{ secrets.INFISICAL_CI_CLIENT_ID }}
|
||||
INFISICAL_CI_CLIENT_SECRET: ${{ secrets.INFISICAL_CI_CLIENT_SECRET }}
|
||||
INFISICAL_PROJECT_ID: ${{ secrets.INFISICAL_CI_PROJECT_ID }}
|
||||
run: |
|
||||
set -uo pipefail
|
||||
BASE="https://key.moleculesai.app"
|
||||
TOK=$(curl -fsS -X POST "$BASE/api/v1/auth/universal-auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"clientId\":\"$INFISICAL_CI_CLIENT_ID\",\"clientSecret\":\"$INFISICAL_CI_CLIENT_SECRET\"}" \
|
||||
| python3 -c 'import sys,json;print(json.load(sys.stdin)["accessToken"])')
|
||||
read_secret() {
|
||||
curl -fsS "$BASE/api/v3/secrets/raw/$1?workspaceId=$INFISICAL_PROJECT_ID&environment=prod&secretPath=$2" \
|
||||
-H "Authorization: Bearer $TOK" \
|
||||
| python3 -c 'import sys,json; v=json.load(sys.stdin).get("secret",{}).get("secretValue"); sys.stdout.write(v if isinstance(v,str) else "")'
|
||||
}
|
||||
CP_ADMIN_API_TOKEN=$(read_secret CP_ADMIN_API_TOKEN %2Fshared%2Fcontrolplane-admin)
|
||||
echo "::add-mask::$CP_ADMIN_API_TOKEN"
|
||||
# Masking-control: this promote step performs a side-effecting fleet
|
||||
# redeploy. Assert NON-EMPTY and FAIL LOUD — never promote with an
|
||||
# empty admin token.
|
||||
if [ -z "$CP_ADMIN_API_TOKEN" ]; then
|
||||
echo "::error::Infisical returned empty CP_ADMIN_API_TOKEN at prod /shared/controlplane-admin — failing closed."
|
||||
exit 1
|
||||
fi
|
||||
echo "CP_ADMIN_API_TOKEN=$CP_ADMIN_API_TOKEN" >> "$GITHUB_ENV"
|
||||
echo "CP_ADMIN_API_TOKEN loaded from Infisical (len=${#CP_ADMIN_API_TOKEN})"
|
||||
|
||||
- name: Check CP credentials
|
||||
run: |
|
||||
if [ -z "${CP_ADMIN_API_TOKEN:-}" ]; then
|
||||
|
||||
@@ -76,7 +76,9 @@ jobs:
|
||||
AWS_REGION: us-east-2
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_SECRETS_JANITOR_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRETS_JANITOR_SECRET_ACCESS_KEY }}
|
||||
CP_ADMIN_API_TOKEN: ${{ secrets.CP_ADMIN_API_TOKEN }}
|
||||
# CP_ADMIN_API_TOKEN now arrives via $GITHUB_ENV from the "Fetch
|
||||
# CP_ADMIN_API_TOKEN from Infisical KMS" step below (wave-3 migration).
|
||||
# The Verify-required-secrets gate still validates it is non-empty.
|
||||
CP_STAGING_ADMIN_API_TOKEN: ${{ secrets.CP_STAGING_ADMIN_API_TOKEN }}
|
||||
MAX_DELETE_PCT: 50
|
||||
GRACE_HOURS: 24
|
||||
@@ -84,6 +86,47 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
# KMS wave-3 (Gitea-Actions-secrets -> Infisical SSOT): source
|
||||
# CP_ADMIN_API_TOKEN from Infisical (prod /shared/controlplane-admin) via
|
||||
# the CI machine identity instead of the duplicated Gitea Actions secret,
|
||||
# mirroring this repo's boot-to-registration-e2e KMS pattern. Exported to
|
||||
# $GITHUB_ENV so the verify + sweep steps read it as a masked env var.
|
||||
#
|
||||
# TRUSTED-REF GATING: molecule-core is a PUBLIC repo; this step consumes
|
||||
# the INFISICAL_CI_* machine-identity secrets, so it must only run on a
|
||||
# TRUSTED event — the schedule cron or an operator workflow_dispatch. An
|
||||
# untrusted-fork PR cannot reach this step.
|
||||
- name: Fetch CP_ADMIN_API_TOKEN from Infisical KMS
|
||||
id: cp_admin_token
|
||||
if: >-
|
||||
github.event_name == 'schedule' ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
env:
|
||||
INFISICAL_CI_CLIENT_ID: ${{ secrets.INFISICAL_CI_CLIENT_ID }}
|
||||
INFISICAL_CI_CLIENT_SECRET: ${{ secrets.INFISICAL_CI_CLIENT_SECRET }}
|
||||
INFISICAL_PROJECT_ID: ${{ secrets.INFISICAL_CI_PROJECT_ID }}
|
||||
run: |
|
||||
set -uo pipefail
|
||||
BASE="https://key.moleculesai.app"
|
||||
TOK=$(curl -fsS -X POST "$BASE/api/v1/auth/universal-auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"clientId\":\"$INFISICAL_CI_CLIENT_ID\",\"clientSecret\":\"$INFISICAL_CI_CLIENT_SECRET\"}" \
|
||||
| python3 -c 'import sys,json;print(json.load(sys.stdin)["accessToken"])')
|
||||
read_secret() {
|
||||
curl -fsS "$BASE/api/v3/secrets/raw/$1?workspaceId=$INFISICAL_PROJECT_ID&environment=prod&secretPath=$2" \
|
||||
-H "Authorization: Bearer $TOK" \
|
||||
| python3 -c 'import sys,json; v=json.load(sys.stdin).get("secret",{}).get("secretValue"); sys.stdout.write(v if isinstance(v,str) else "")'
|
||||
}
|
||||
CP_ADMIN_API_TOKEN=$(read_secret CP_ADMIN_API_TOKEN %2Fshared%2Fcontrolplane-admin)
|
||||
echo "::add-mask::$CP_ADMIN_API_TOKEN"
|
||||
# Masking-control: assert NON-EMPTY and FAIL LOUD.
|
||||
if [ -z "$CP_ADMIN_API_TOKEN" ]; then
|
||||
echo "::error::Infisical returned empty CP_ADMIN_API_TOKEN at prod /shared/controlplane-admin — failing closed."
|
||||
exit 1
|
||||
fi
|
||||
echo "CP_ADMIN_API_TOKEN=$CP_ADMIN_API_TOKEN" >> "$GITHUB_ENV"
|
||||
echo "CP_ADMIN_API_TOKEN loaded from Infisical (len=${#CP_ADMIN_API_TOKEN})"
|
||||
|
||||
- name: Verify required secrets present
|
||||
id: verify
|
||||
# Schedule-vs-dispatch behaviour split mirrors sweep-cf-orphans
|
||||
|
||||
@@ -83,7 +83,9 @@ jobs:
|
||||
env:
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN || secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CF_ZONE_ID: ${{ secrets.CF_ZONE_ID || secrets.CLOUDFLARE_ZONE_ID }}
|
||||
CP_ADMIN_API_TOKEN: ${{ secrets.CP_ADMIN_API_TOKEN }}
|
||||
# CP_ADMIN_API_TOKEN now arrives via $GITHUB_ENV from the "Fetch
|
||||
# CP_ADMIN_API_TOKEN from Infisical KMS" step below (wave-3 migration).
|
||||
# The Verify-required-secrets gate still validates it is non-empty.
|
||||
CP_STAGING_ADMIN_API_TOKEN: ${{ secrets.CP_STAGING_ADMIN_API_TOKEN }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
@@ -93,6 +95,47 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
# KMS wave-3 (Gitea-Actions-secrets -> Infisical SSOT): source
|
||||
# CP_ADMIN_API_TOKEN from Infisical (prod /shared/controlplane-admin) via
|
||||
# the CI machine identity instead of the duplicated Gitea Actions secret,
|
||||
# mirroring this repo's boot-to-registration-e2e KMS pattern. Exported to
|
||||
# $GITHUB_ENV so the verify + sweep steps read it as a masked env var.
|
||||
#
|
||||
# TRUSTED-REF GATING: molecule-core is a PUBLIC repo; this step consumes
|
||||
# the INFISICAL_CI_* machine-identity secrets, so it must only run on a
|
||||
# TRUSTED event — the schedule cron or an operator workflow_dispatch. An
|
||||
# untrusted-fork PR cannot reach this step.
|
||||
- name: Fetch CP_ADMIN_API_TOKEN from Infisical KMS
|
||||
id: cp_admin_token
|
||||
if: >-
|
||||
github.event_name == 'schedule' ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
env:
|
||||
INFISICAL_CI_CLIENT_ID: ${{ secrets.INFISICAL_CI_CLIENT_ID }}
|
||||
INFISICAL_CI_CLIENT_SECRET: ${{ secrets.INFISICAL_CI_CLIENT_SECRET }}
|
||||
INFISICAL_PROJECT_ID: ${{ secrets.INFISICAL_CI_PROJECT_ID }}
|
||||
run: |
|
||||
set -uo pipefail
|
||||
BASE="https://key.moleculesai.app"
|
||||
TOK=$(curl -fsS -X POST "$BASE/api/v1/auth/universal-auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"clientId\":\"$INFISICAL_CI_CLIENT_ID\",\"clientSecret\":\"$INFISICAL_CI_CLIENT_SECRET\"}" \
|
||||
| python3 -c 'import sys,json;print(json.load(sys.stdin)["accessToken"])')
|
||||
read_secret() {
|
||||
curl -fsS "$BASE/api/v3/secrets/raw/$1?workspaceId=$INFISICAL_PROJECT_ID&environment=prod&secretPath=$2" \
|
||||
-H "Authorization: Bearer $TOK" \
|
||||
| python3 -c 'import sys,json; v=json.load(sys.stdin).get("secret",{}).get("secretValue"); sys.stdout.write(v if isinstance(v,str) else "")'
|
||||
}
|
||||
CP_ADMIN_API_TOKEN=$(read_secret CP_ADMIN_API_TOKEN %2Fshared%2Fcontrolplane-admin)
|
||||
echo "::add-mask::$CP_ADMIN_API_TOKEN"
|
||||
# Masking-control: assert NON-EMPTY and FAIL LOUD.
|
||||
if [ -z "$CP_ADMIN_API_TOKEN" ]; then
|
||||
echo "::error::Infisical returned empty CP_ADMIN_API_TOKEN at prod /shared/controlplane-admin — failing closed."
|
||||
exit 1
|
||||
fi
|
||||
echo "CP_ADMIN_API_TOKEN=$CP_ADMIN_API_TOKEN" >> "$GITHUB_ENV"
|
||||
echo "CP_ADMIN_API_TOKEN loaded from Infisical (len=${#CP_ADMIN_API_TOKEN})"
|
||||
|
||||
- name: Verify required secrets present
|
||||
id: verify
|
||||
# Schedule-vs-dispatch behaviour split (hardened 2026-04-28
|
||||
|
||||
@@ -78,13 +78,58 @@ jobs:
|
||||
env:
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN || secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID || secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
CP_ADMIN_API_TOKEN: ${{ secrets.CP_ADMIN_API_TOKEN }}
|
||||
# CP_ADMIN_API_TOKEN now arrives via $GITHUB_ENV from the "Fetch
|
||||
# CP_ADMIN_API_TOKEN from Infisical KMS" step below (wave-3 migration).
|
||||
# The Verify-required-secrets gate still validates it is non-empty.
|
||||
CP_STAGING_ADMIN_API_TOKEN: ${{ secrets.CP_STAGING_ADMIN_API_TOKEN }}
|
||||
MAX_DELETE_PCT: ${{ github.event.inputs.max_delete_pct || '90' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
# KMS wave-3 (Gitea-Actions-secrets -> Infisical SSOT): source
|
||||
# CP_ADMIN_API_TOKEN from Infisical (prod /shared/controlplane-admin) via
|
||||
# the CI machine identity instead of the duplicated Gitea Actions secret,
|
||||
# mirroring this repo's boot-to-registration-e2e KMS pattern. Exported to
|
||||
# $GITHUB_ENV so the verify + sweep steps read it as a masked env var.
|
||||
#
|
||||
# TRUSTED-REF GATING: molecule-core is a PUBLIC repo; this step consumes
|
||||
# the INFISICAL_CI_* machine-identity secrets, so it must only run on a
|
||||
# TRUSTED event — the schedule cron or an operator workflow_dispatch. An
|
||||
# untrusted-fork PR cannot reach this step.
|
||||
- name: Fetch CP_ADMIN_API_TOKEN from Infisical KMS
|
||||
id: cp_admin_token
|
||||
if: >-
|
||||
github.event_name == 'schedule' ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
env:
|
||||
INFISICAL_CI_CLIENT_ID: ${{ secrets.INFISICAL_CI_CLIENT_ID }}
|
||||
INFISICAL_CI_CLIENT_SECRET: ${{ secrets.INFISICAL_CI_CLIENT_SECRET }}
|
||||
INFISICAL_PROJECT_ID: ${{ secrets.INFISICAL_CI_PROJECT_ID }}
|
||||
run: |
|
||||
set -uo pipefail
|
||||
BASE="https://key.moleculesai.app"
|
||||
TOK=$(curl -fsS -X POST "$BASE/api/v1/auth/universal-auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"clientId\":\"$INFISICAL_CI_CLIENT_ID\",\"clientSecret\":\"$INFISICAL_CI_CLIENT_SECRET\"}" \
|
||||
| python3 -c 'import sys,json;print(json.load(sys.stdin)["accessToken"])')
|
||||
read_secret() {
|
||||
curl -fsS "$BASE/api/v3/secrets/raw/$1?workspaceId=$INFISICAL_PROJECT_ID&environment=prod&secretPath=$2" \
|
||||
-H "Authorization: Bearer $TOK" \
|
||||
| python3 -c 'import sys,json; v=json.load(sys.stdin).get("secret",{}).get("secretValue"); sys.stdout.write(v if isinstance(v,str) else "")'
|
||||
}
|
||||
CP_ADMIN_API_TOKEN=$(read_secret CP_ADMIN_API_TOKEN %2Fshared%2Fcontrolplane-admin)
|
||||
echo "::add-mask::$CP_ADMIN_API_TOKEN"
|
||||
# Masking-control: assert NON-EMPTY and FAIL LOUD. On a scheduled run
|
||||
# an empty token would otherwise be caught by the verify step as
|
||||
# "missing", but failing here is louder and closer to the cause.
|
||||
if [ -z "$CP_ADMIN_API_TOKEN" ]; then
|
||||
echo "::error::Infisical returned empty CP_ADMIN_API_TOKEN at prod /shared/controlplane-admin — failing closed."
|
||||
exit 1
|
||||
fi
|
||||
echo "CP_ADMIN_API_TOKEN=$CP_ADMIN_API_TOKEN" >> "$GITHUB_ENV"
|
||||
echo "CP_ADMIN_API_TOKEN loaded from Infisical (len=${#CP_ADMIN_API_TOKEN})"
|
||||
|
||||
- name: Verify required secrets present
|
||||
id: verify
|
||||
# Schedule-vs-dispatch behaviour split mirrors sweep-cf-orphans
|
||||
|
||||
@@ -105,7 +105,10 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
MOLECULE_CP_URL: ${{ vars.CP_URL || 'https://staging-api.moleculesai.app' }}
|
||||
MOLECULE_ADMIN_TOKEN: ${{ secrets.CP_ADMIN_API_TOKEN }}
|
||||
# MOLECULE_ADMIN_TOKEN (alias for CP_ADMIN_API_TOKEN) now arrives via
|
||||
# $GITHUB_ENV from the "Fetch CP_ADMIN_API_TOKEN from Infisical KMS" step
|
||||
# below (wave-3 migration). The "Verify required secret present" step
|
||||
# still validates it is non-empty before the e2e runs.
|
||||
E2E_EXPECTED_MODEL: moonshot/kimi-k2.6
|
||||
steps:
|
||||
- name: No-op pass (delivery surface unchanged in this diff)
|
||||
@@ -115,6 +118,62 @@ jobs:
|
||||
echo "::notice::template-delivery-e2e no-op pass (detect-changes: ${{ needs.detect-changes.outputs.debug }})."
|
||||
- if: needs.detect-changes.outputs.delivery == 'true'
|
||||
uses: actions/checkout@v4
|
||||
# KMS wave-3 (Gitea-Actions-secrets -> Infisical SSOT): source the admin
|
||||
# token (consumed here as MOLECULE_ADMIN_TOKEN) from Infisical (prod
|
||||
# /shared/controlplane-admin) via the CI machine identity instead of the
|
||||
# duplicated Gitea Actions secret, mirroring this repo's
|
||||
# boot-to-registration-e2e KMS pattern. Exported to $GITHUB_ENV under the
|
||||
# alias MOLECULE_ADMIN_TOKEN so the verify + e2e steps read it unchanged.
|
||||
# Only runs when the delivery surface changed (the real e2e runs); a
|
||||
# no-op PR never touches the CI identity.
|
||||
#
|
||||
# TRUSTED-REF GATING: molecule-core is a PUBLIC repo; this step consumes
|
||||
# the INFISICAL_CI_* machine-identity secrets, so it must only run on a
|
||||
# TRUSTED event — operator workflow_dispatch, a push to the protected main
|
||||
# branch, or a SAME-REPO pull_request. The pull_request clause uses the
|
||||
# FAIL-CLOSED positive allowlist
|
||||
# (github.event.pull_request.head.repo.full_name == github.repository):
|
||||
# it admits ONLY a same-repo head and fails closed on a fork, a
|
||||
# detached/cross-repo head, OR a missing/null head.repo field (the negative
|
||||
# `head.repo.fork == false` form would FAIL-OPEN on a null `fork`, since
|
||||
# `null == false` coerces TRUE in the expression engine). An untrusted-fork
|
||||
# PR is therefore excluded so it cannot harvest the KMS token. A fork
|
||||
# delivery PR will fail the "Verify required secret present" check below
|
||||
# (loud, not silent) until a maintainer runs it from a trusted ref.
|
||||
- name: Fetch CP_ADMIN_API_TOKEN from Infisical KMS
|
||||
id: cp_admin_token
|
||||
if: >-
|
||||
needs.detect-changes.outputs.delivery == 'true' &&
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'push' && github.ref == 'refs/heads/main') ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository))
|
||||
env:
|
||||
INFISICAL_CI_CLIENT_ID: ${{ secrets.INFISICAL_CI_CLIENT_ID }}
|
||||
INFISICAL_CI_CLIENT_SECRET: ${{ secrets.INFISICAL_CI_CLIENT_SECRET }}
|
||||
INFISICAL_PROJECT_ID: ${{ secrets.INFISICAL_CI_PROJECT_ID }}
|
||||
run: |
|
||||
set -uo pipefail
|
||||
BASE="https://key.moleculesai.app"
|
||||
TOK=$(curl -fsS -X POST "$BASE/api/v1/auth/universal-auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"clientId\":\"$INFISICAL_CI_CLIENT_ID\",\"clientSecret\":\"$INFISICAL_CI_CLIENT_SECRET\"}" \
|
||||
| python3 -c 'import sys,json;print(json.load(sys.stdin)["accessToken"])')
|
||||
read_secret() {
|
||||
curl -fsS "$BASE/api/v3/secrets/raw/$1?workspaceId=$INFISICAL_PROJECT_ID&environment=prod&secretPath=$2" \
|
||||
-H "Authorization: Bearer $TOK" \
|
||||
| python3 -c 'import sys,json; v=json.load(sys.stdin).get("secret",{}).get("secretValue"); sys.stdout.write(v if isinstance(v,str) else "")'
|
||||
}
|
||||
CP_ADMIN_API_TOKEN=$(read_secret CP_ADMIN_API_TOKEN %2Fshared%2Fcontrolplane-admin)
|
||||
echo "::add-mask::$CP_ADMIN_API_TOKEN"
|
||||
# Masking-control: this required delivery gate must not run against an
|
||||
# empty admin token. Assert NON-EMPTY and FAIL LOUD.
|
||||
if [ -z "$CP_ADMIN_API_TOKEN" ]; then
|
||||
echo "::error::Infisical returned empty CP_ADMIN_API_TOKEN at prod /shared/controlplane-admin — failing closed."
|
||||
exit 1
|
||||
fi
|
||||
# Export under the alias the e2e consumes.
|
||||
echo "MOLECULE_ADMIN_TOKEN=$CP_ADMIN_API_TOKEN" >> "$GITHUB_ENV"
|
||||
echo "CP_ADMIN_API_TOKEN loaded from Infisical (len=${#CP_ADMIN_API_TOKEN})"
|
||||
- name: Verify required secret present
|
||||
if: needs.detect-changes.outputs.delivery == 'true'
|
||||
run: |
|
||||
|
||||
Reference in New Issue
Block a user