name: Sweep stale AWS Secrets Manager secrets

# Ported from .github/workflows/sweep-aws-secrets.yml on 2026-05-11 per RFC
# internal#219 §1 sweep. Differences from the GitHub version:
#   - Dropped `workflow_dispatch.inputs` (Gitea 1.22.6 parser rejects them
#     per feedback_gitea_workflow_dispatch_inputs_unsupported).
#   - Dropped `merge_group:` (no Gitea merge queue).
#   - Dropped `environment:` blocks (Gitea has no environments).
#   - Workflow-level env.GITHUB_SERVER_URL pinned per
#     feedback_act_runner_github_server_url.
#   - `continue-on-error: true` on each job (RFC §1 contract).
#

# Janitor for per-tenant AWS Secrets Manager secrets
# (`molecule/tenant/<org_id>/bootstrap`) whose backing tenant no
# longer exists. Parallel-shape to sweep-cf-tunnels.yml and
# sweep-cf-orphans.yml — different cloud, same justification.
#
# Why this exists separately from a long-term reconciler integration:
#   - molecule-controlplane's tenant_resources audit table (mig 024)
#     currently tracks four resource kinds: CloudflareTunnel,
#     CloudflareDNS, EC2Instance, SecurityGroup. SecretsManager is
#     not in the list, so the existing reconciler doesn't catch
#     orphan secrets.
#   - At ~$0.40/secret/month the cost grew to ~$19/month before this
#     sweeper was written, indicating ~45+ orphan secrets from
#     crashed provisions and incomplete deprovision flows.
#   - The proper fix (KindSecretsManagerSecret + recorder hook +
#     reconciler enumerator) is filed as a separate controlplane
#     issue. This sweeper is the immediate cost-relief stopgap.
#
# AWS credentials: use the dedicated Secrets Manager janitor principal.
# Do not fall back to the molecule-cp application principal: it does
# not need account-wide ListSecrets, and a 2026-05-12 CI failure proved
# that using it here turns a least-privilege production credential into
# a red scheduled janitor.
#
# Safety: the script's MAX_DELETE_PCT gate (default 50%, mirroring
# sweep-cf-orphans.yml — tenant secrets are durable by design, unlike
# the mostly-orphan tunnels) refuses to nuke past the threshold.

on:
  schedule:
    # Hourly at :30, offset from sweep-cf-orphans (:15) and
    # sweep-cf-tunnels (:45). This janitor is intentionally schedule-only
    # for deletes; manual dispatch is forced to dry-run below because Gitea
    # 1.22.6 rejects workflow_dispatch.inputs.
    - cron: '30 * * * *'
  workflow_dispatch:
# Don't let two sweeps race the same AWS account.
concurrency:
  group: sweep-aws-secrets
  cancel-in-progress: false

permissions:
  contents: read

env:
  GITHUB_SERVER_URL: https://git.moleculesai.app

jobs:
  sweep:
    name: Sweep AWS Secrets Manager
    runs-on: ubuntu-latest
    # This is a cost/leak janitor. A scheduled failure must be red so
    # operators know tenant bootstrap secrets may be leaking.
    # 30 min cap, mirroring the other janitors. AWS DeleteSecret is
    # fast (~0.3s/call) so even a 100+ backlog drains in seconds
    # under the 8-way xargs parallelism, but the cap is set generously
    # to leave headroom for any actual API hang.
    timeout-minutes: 30
    env:
      # Keep this literal. Gitea/act_runner 1.22.6 can mis-render
      # secret-backed expressions with `||`, which produced an invalid
      # Secrets Manager endpoint in the scheduled janitor.
      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_STAGING_ADMIN_API_TOKEN: ${{ secrets.CP_STAGING_ADMIN_API_TOKEN }}
      MAX_DELETE_PCT: 50
      GRACE_HOURS: 24

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Verify required secrets present
        id: verify
        # Schedule-vs-dispatch behaviour split mirrors sweep-cf-orphans
        # and sweep-cf-tunnels (hardened 2026-04-28). Same principle:
        #   - schedule → exit 1 on missing secrets (red CI surfaces it)
        #   - workflow_dispatch → exit 0 with warning (operator-driven,
        #     they already accepted the repo state)
        run: |
          missing=()
          for var in AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY CP_ADMIN_API_TOKEN CP_STAGING_ADMIN_API_TOKEN; do
            if [ -z "${!var:-}" ]; then
              missing+=("$var")
            fi
          done
          if [ ${#missing[@]} -gt 0 ]; then
            if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
              echo "::warning::skipping sweep — secrets not configured: ${missing[*]}"
              echo "::warning::set them at Settings → Secrets and Variables → Actions, then rerun."
              echo "skip=true" >> "$GITHUB_OUTPUT"
              exit 0
            fi
            echo "::error::sweep cannot run — required secrets missing: ${missing[*]}"
            echo "::error::set them at Settings → Secrets and Variables → Actions, or disable this workflow."
            exit 1
          fi
          echo "All required secrets present ✓"
          echo "skip=false" >> "$GITHUB_OUTPUT"

      - name: Run sweep
        if: steps.verify.outputs.skip != 'true'
        # Schedule-vs-dispatch dry-run asymmetry:
        #   - schedule: execute (the whole point of an hourly janitor).
        #   - workflow_dispatch: dry-run. Gitea 1.22.6 rejects
        #     workflow_dispatch.inputs, so there is no safe manual
        #     "flip it to execute" toggle in this workflow.
        # The script's MAX_DELETE_PCT gate (default 50%) remains the
        # second line of defense regardless of trigger.
        run: |
          set -euo pipefail
          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            echo "Running in dry-run mode — no deletions"
            bash scripts/ops/sweep-aws-secrets.sh
          else
            echo "Running with --execute — will delete identified orphans"
            bash scripts/ops/sweep-aws-secrets.sh --execute
          fi

      - name: Notify on sweep failure
        if: failure()
        run: |
          echo "::error::sweep-aws-secrets FAILED — AWS tenant bootstrap secrets may be leaking. Check missing Gitea secrets, staging/prod CP admin tokens, AWS janitor IAM permissions, or the script safety gate."
          exit 1
