6042ce539c
Replace direct Gitea-Actions-secret bot-token consumption with the proven #971/#3274 INFISICAL_CI KMS-fetch pattern. Each consumer job gains a "Fetch <CRED> from Infisical SSOT" step (INFISICAL_CI universal-auth login -> read_secret GET /api/v3/secrets/raw/<KEY> environment=prod -> null-safe extractor -> [ -z ] fail-closed -> ::add-mask:: -> export to $GITHUB_ENV), then the consumer switches secrets.<OLD> -> env.<CRED_KEY>, exported under the env-var NAME the workflow/script already expects so scripts are unchanged. CI_STATUS_TOKEN (prod /shared/ci-status) replaces: - STATUS_POST_TOKEN in qa-review.yml (POST status step) - STATUS_POST_TOKEN in security-review.yml (POST status step) - STATUS_POST_TOKEN in reserved-path-review.yml (POST status step) - STATUS_POST_TOKEN in sop-checklist.yml (qa + security refire steps) - STATUS_REAPER_TOKEN in status-reaper.yml (compensation POST) DRIFT_BOT_TOKEN (prod /shared/drift-bot) replaces: - DRIFT_BOT_TOKEN in ci-required-drift.yml - DRIFT_BOT_TOKEN in lint-continue-on-error-tracking.yml - DRIFT_BOT_TOKEN in lint-required-no-paths.yml - DRIFT_BOT_TOKEN in lint-no-coe-on-required.yml - DRIFT_BOT_TOKEN in lint-bp-context-emit-match.yml - DRIFT_BOT_TOKEN in lint-required-context-exists-in-bp.yml test_gate_review_auto_fire.py: the four assertions that pinned secrets.STATUS_POST_TOKEN now assert env.CI_STATUS_TOKEN (qa/security POST steps + qa/security refire steps). validate-before-delete: the old Gitea Actions secrets (STATUS_POST_TOKEN, STATUS_REAPER_TOKEN, DRIFT_BOT_TOKEN) are intentionally LEFT IN PLACE. Delete them only after a green run proves the Infisical fetch path works. Both consolidated Infisical secrets already exist in prod (verified non-empty). Bootstrap INFISICAL_CI_* and the auto-injected GITEA_TOKEN/GITHUB_TOKEN are untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
138 lines
6.8 KiB
YAML
138 lines
6.8 KiB
YAML
# lint-required-no-paths — structural enforcement of
|
|
# `feedback_path_filtered_workflow_cant_be_required`.
|
|
#
|
|
# Fails the PR if ANY workflow whose status-check context appears in
|
|
# `branch_protections/main.status_check_contexts` carries a
|
|
# `paths:` or `paths-ignore:` filter in its `on:` block.
|
|
#
|
|
# Why this exists:
|
|
# A required-check workflow with a paths filter silently degrades the
|
|
# merge gate. If a PR's diff doesn't touch the filter, the workflow
|
|
# never fires; Gitea (1.22.6) reports the required context as
|
|
# `pending` (NOT `skipped == success`), so the PR cannot merge. For a
|
|
# docs-only PR against `paths: ['**.go']`, the PR is wedged forever.
|
|
#
|
|
# Previously prevented only by reviewer vigilance + the saved memory
|
|
# `feedback_path_filtered_workflow_cant_be_required`. This workflow
|
|
# makes it a hard CI gate.
|
|
#
|
|
# Forward-compat scope:
|
|
# Today (2026-05-11) molecule-core/main protects 3 contexts:
|
|
# - "Secret scan / Scan diff for credential-shaped strings (pull_request)"
|
|
# - "sop-checklist / all-items-acked (pull_request)"
|
|
# - "CI / all-required (pull_request)"
|
|
# Per RFC#324 Step 2 the required-list expands to ~5 contexts
|
|
# (qa-review, security-review added). Each new required context's
|
|
# workflow must remain unconditional. This lint pins that contract.
|
|
#
|
|
# Meta-required-check:
|
|
# This workflow ITSELF deliberately has NO `paths:` filter on its `on:`
|
|
# block — otherwise a paths-non-matching PR could bypass the check.
|
|
# Self-evident from this file: only `pull_request` types + no paths.
|
|
#
|
|
# Auth:
|
|
# `GET /repos/.../branch_protections/{branch}` requires repo-admin
|
|
# role in Gitea 1.22.6. The workflow-default `GITHUB_TOKEN` is
|
|
# non-admin (read-only), so we re-use `DRIFT_BOT_TOKEN` (same persona
|
|
# that powers `ci-required-drift.yml` — verified working there).
|
|
# If `DRIFT_BOT_TOKEN` becomes unavailable, the script exits 0 with a
|
|
# loud `::error::` rather than red-X every PR — token-scope issues
|
|
# should be fixed at the token, not surfaced as a gate failure on
|
|
# every unrelated PR.
|
|
#
|
|
# Behavior-based gate per `feedback_behavior_based_ast_gates`:
|
|
# YAML AST walk (PyYAML), NOT grep. Workflow renames, formatting
|
|
# changes (block-scalar vs flow-style), or moving `paths:` between
|
|
# `pull_request:` and `pull_request_target:` all still detect.
|
|
#
|
|
# IMPORTANT — Gitea 1.22.6 parser quirk per
|
|
# `feedback_gitea_workflow_dispatch_inputs_unsupported`: do NOT add an
|
|
# `inputs:` block to `workflow_dispatch:` — Gitea 1.22.6 rejects the
|
|
# entire workflow as "unknown on type" and it registers for ZERO events.
|
|
|
|
name: lint-required-no-paths
|
|
|
|
on:
|
|
pull_request:
|
|
types: [opened, synchronize, reopened]
|
|
workflow_dispatch:
|
|
|
|
# Read protection + read local YAML. No writes.
|
|
permissions:
|
|
contents: read
|
|
|
|
# Only one in-flight run per PR — re-pushes cancel the previous run to
|
|
# keep the queue short. Required-list reads are cheap (one GET); the
|
|
# cancellation is just hygiene.
|
|
concurrency:
|
|
group: lint-required-no-paths-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# bp-exempt: meta-lint advisory; CI / all-required is the required aggregate.
|
|
lint:
|
|
name: lint-required-no-paths
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- name: Check out repo (we read the workflow YAML files locally)
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
- name: Set up Python (PyYAML for AST parsing)
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: '3.12'
|
|
- name: Install PyYAML
|
|
run: python -m pip install --quiet 'PyYAML==6.0.2'
|
|
# KMS consolidation (#971/#3274 pattern): the lint below previously read
|
|
# the per-bot Gitea Actions secret `DRIFT_BOT_TOKEN`. We now fetch the
|
|
# consolidated DRIFT_BOT_TOKEN from the Infisical SSOT (prod
|
|
# /shared/drift-bot) via the CI machine identity and export it to
|
|
# $GITHUB_ENV. The lint keeps reading it under the GITEA_TOKEN env var
|
|
# name it already expects, so the script is unchanged. The old
|
|
# DRIFT_BOT_TOKEN Gitea secret is left in place until a green run
|
|
# validates this path (validate-before-delete).
|
|
#
|
|
# Trust boundary: molecule-core PRs are same-repo (not forks), so the
|
|
# INFISICAL_CI_* machine-identity secrets are present on pull_request —
|
|
# the same assumption the prior direct DRIFT_BOT_TOKEN consumption made.
|
|
- name: Fetch DRIFT_BOT_TOKEN from Infisical SSOT
|
|
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; v=json.load(sys.stdin).get("accessToken"); sys.stdout.write(v if isinstance(v,str) else "")')
|
|
if [ -z "$TOK" ]; then echo "::error::Infisical accessToken empty - failing closed"; exit 1; fi
|
|
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 "")'
|
|
}
|
|
DRIFT_BOT_TOKEN=$(read_secret DRIFT_BOT_TOKEN %2Fshared%2Fdrift-bot)
|
|
echo "::add-mask::$DRIFT_BOT_TOKEN"
|
|
if [ -z "$DRIFT_BOT_TOKEN" ]; then
|
|
echo "::error::Infisical returned empty DRIFT_BOT_TOKEN at prod /shared/drift-bot — failing closed."
|
|
exit 1
|
|
fi
|
|
echo "DRIFT_BOT_TOKEN=$DRIFT_BOT_TOKEN" >> "$GITHUB_ENV"
|
|
echo "DRIFT_BOT_TOKEN loaded from Infisical (len=${#DRIFT_BOT_TOKEN})"
|
|
- name: Run lint-required-no-paths
|
|
env:
|
|
# DRIFT_BOT_TOKEN is owned by mc-drift-bot, a least-privilege
|
|
# Gitea persona with repo-admin role for branch_protections
|
|
# read. Same secret used by ci-required-drift.yml — see that
|
|
# workflow's header for provisioning trail (internal#329).
|
|
# KMS consolidation: now arrives via $GITHUB_ENV from the fetch step
|
|
# above (was secrets.DRIFT_BOT_TOKEN).
|
|
GITEA_TOKEN: ${{ env.DRIFT_BOT_TOKEN }}
|
|
GITEA_HOST: git.moleculesai.app
|
|
REPO: ${{ github.repository }}
|
|
BRANCH: main
|
|
WORKFLOWS_DIR: .gitea/workflows
|
|
run: python3 .gitea/scripts/lint-required-no-paths.py
|