chore(deps): bump the production-dependencies group across 1 directory with 9 updates #1835
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| inputs: | |
| force_rebuild: | |
| description: 'Force rebuild all images (ignore cache)' | |
| required: false | |
| default: false | |
| type: boolean | |
| env: | |
| TEST_HOST: http://localhost | |
| CI_DB_PORT: 5433 | |
| CI_PORT: 8081 | |
| PORT: 8081 | |
| CI_BETTER_AUTH_URL: http://localhost:8083/auth | |
| BETTER_AUTH_URL: http://localhost:8083/auth | |
| DB_USERNAME: test-user | |
| DB_PASSWORD: test-pw | |
| DB_NAME: wxyc_db | |
| AUTH_BYPASS: true | |
| AUTH_USERNAME: test_dj1 | |
| AUTH_PASSWORD: testpassword123 | |
| MOCK_API_URL: http://localhost:9090 | |
| # Workflow-level GITHUB_TOKEN scope. No job in this file pushes code, | |
| # comments on PRs, or creates releases; all writes to external services | |
| # use their own non-GITHUB_TOKEN secrets. contents:read is the safe floor. | |
| permissions: | |
| contents: read | |
| jobs: | |
| # Detect what changed to conditionally run jobs | |
| detect-changes: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| apps: ${{ steps.changes.outputs.apps }} | |
| jobs: ${{ steps.changes.outputs.jobs }} | |
| shared: ${{ steps.changes.outputs.shared }} | |
| tests: ${{ steps.changes.outputs.tests }} | |
| src: ${{ steps.changes.outputs.src }} | |
| run-integration: ${{ steps.should-run.outputs.run-integration }} | |
| db-init: ${{ steps.changes.outputs.db-init }} | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v6 | |
| - name: Detect file changes | |
| uses: dorny/paths-filter@v4 | |
| id: changes | |
| with: | |
| filters: | | |
| apps: | |
| - 'apps/**' | |
| jobs: | |
| - 'jobs/**' | |
| shared: | |
| - 'shared/**' | |
| tests: | |
| - 'tests/**' | |
| db-init: | |
| - 'dev_env/Dockerfile.init' | |
| - 'dev_env/init-db.mjs' | |
| - 'dev_env/format-pg-error.mjs' | |
| - 'dev_env/package.init.json' | |
| - 'dev_env/seed_db.sql' | |
| - 'shared/database/src/migrations/**' | |
| - 'shared/database/src/schema.ts' | |
| docs-only: | |
| - '**/*.md' | |
| - 'docs/**' | |
| mock-api: | |
| - 'dev_env/mock-api-server/**' | |
| src: | |
| - 'apps/**' | |
| - 'jobs/**' | |
| - 'shared/**' | |
| - 'tests/**' | |
| - 'dev_env/mock-api-server/**' | |
| - 'package.json' | |
| - 'package-lock.json' | |
| - 'tsconfig.base.json' | |
| - name: Determine if integration tests should run | |
| id: should-run | |
| run: | | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" || \ | |
| "${{ steps.changes.outputs.apps }}" == "true" || \ | |
| "${{ steps.changes.outputs.jobs }}" == "true" || \ | |
| "${{ steps.changes.outputs.shared }}" == "true" || \ | |
| "${{ steps.changes.outputs.tests }}" == "true" ]]; then | |
| echo "run-integration=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "run-integration=false" >> $GITHUB_OUTPUT | |
| fi | |
| # Fast lint and type check - always runs on code changes | |
| lint-and-typecheck: | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.src == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v6 | |
| - name: Set Up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| - name: Cache node_modules | |
| id: cache-node-modules | |
| uses: actions/cache@v5 | |
| with: | |
| path: node_modules | |
| key: node-modules-${{ runner.os }}-node24-${{ hashFiles('package-lock.json') }} | |
| - name: Validate cached node_modules | |
| id: validate-cache | |
| if: steps.cache-node-modules.outputs.cache-hit == 'true' | |
| run: | | |
| if npm ls --depth=0 >/dev/null 2>&1; then | |
| echo "valid=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "valid=false" >> $GITHUB_OUTPUT | |
| echo "⚠️ Cache corrupted, will reinstall" | |
| rm -rf node_modules | |
| fi | |
| - name: Install Dependencies | |
| if: steps.cache-node-modules.outputs.cache-hit != 'true' || steps.validate-cache.outputs.valid == 'false' | |
| run: npm ci | |
| env: | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Cache tool artifacts (tsc, ESLint, Prettier) | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| .cache | |
| shared/database/.cache | |
| shared/authentication/.cache | |
| apps/backend/tsconfig.tsbuildinfo | |
| apps/auth/tsconfig.tsbuildinfo | |
| key: tool-cache-${{ runner.os }}-${{ hashFiles('eslint.config.mjs', '.prettierrc.json', 'tsconfig.base.json', 'package-lock.json') }}-${{ github.sha }} | |
| restore-keys: | | |
| tool-cache-${{ runner.os }}-${{ hashFiles('eslint.config.mjs', '.prettierrc.json', 'tsconfig.base.json', 'package-lock.json') }}- | |
| tool-cache-${{ runner.os }}- | |
| - name: Type check | |
| run: npm run typecheck | |
| - name: Lint | |
| run: npm run lint | |
| - name: Check formatting | |
| run: npm run format:check | |
| - name: Validate migration journal | |
| run: npm run lint:migrations | |
| - name: Cross-cache-identity flag-doc consistency | |
| run: scripts/check-cross-cache-identity-flags.sh | |
| - name: Migration guards (cross-cache-identity) | |
| run: scripts/check-precondition-guards.sh | |
| - name: legacy_entry_id writes (three-use invariant) | |
| run: node scripts/check-legacy-entry-id-writes.mjs | |
| - name: Bulk UPDATE + ANALYZE pairing (BS#934) | |
| run: node scripts/check-bulk-update-analyze.mjs --strict | |
| - name: Build | |
| run: npm run build | |
| # Unit tests - runs affected tests only | |
| unit-tests: | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.src == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 # Need full history for --changedSince | |
| - name: Set Up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| - name: Cache node_modules | |
| id: cache-node-modules | |
| uses: actions/cache@v5 | |
| with: | |
| path: node_modules | |
| key: node-modules-${{ runner.os }}-node24-${{ hashFiles('package-lock.json') }} | |
| - name: Validate cached node_modules | |
| id: validate-cache | |
| if: steps.cache-node-modules.outputs.cache-hit == 'true' | |
| run: | | |
| if npm ls --depth=0 >/dev/null 2>&1; then | |
| echo "valid=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "valid=false" >> $GITHUB_OUTPUT | |
| echo "⚠️ Cache corrupted, will reinstall" | |
| rm -rf node_modules | |
| fi | |
| - name: Install Dependencies | |
| if: steps.cache-node-modules.outputs.cache-hit != 'true' || steps.validate-cache.outputs.valid == 'false' | |
| run: npm ci | |
| env: | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Run affected unit tests | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| npx jest --config jest.unit.config.ts --changedSince=origin/${{ github.base_ref }} --coverage --passWithNoTests | |
| else | |
| # workflow_dispatch: run all unit tests | |
| npm run test:unit:coverage | |
| fi | |
| - name: Upload Coverage | |
| uses: actions/upload-artifact@v7 | |
| if: always() | |
| with: | |
| name: unit-test-coverage | |
| path: coverage/ | |
| retention-days: 7 | |
| # Integration tests - only when backend/auth/shared/tests change | |
| # Note: Job always runs to report status, but steps are skipped when no relevant changes. | |
| # The services block starts PostgreSQL even when skipped -- this is unavoidable with GHA | |
| # service containers but harmless (starts in seconds, job exits immediately). | |
| Integration-Tests: | |
| needs: [detect-changes, lint-and-typecheck] | |
| # Run when lint-and-typecheck succeeds or was skipped (no src changes). | |
| # This ensures the required check reports status on docs-only PRs. | |
| if: ${{ !failure() && !cancelled() }} | |
| runs-on: ubuntu-latest | |
| services: | |
| postgres: | |
| image: postgres:18.0-alpine | |
| env: | |
| POSTGRES_USER: test-user | |
| POSTGRES_PASSWORD: test-pw | |
| POSTGRES_DB: wxyc_db | |
| ports: | |
| - 5433:5432 | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 2s | |
| --health-timeout 5s | |
| --health-retries 10 | |
| env: | |
| RUN_TESTS: ${{ needs.detect-changes.outputs.run-integration }} | |
| TEST_HOST: http://localhost | |
| DB_HOST: localhost | |
| DB_PORT: 5433 | |
| DB_USERNAME: test-user | |
| DB_PASSWORD: test-pw | |
| DB_NAME: wxyc_db | |
| PORT: 8081 | |
| AUTH_PORT: 8083 | |
| AUTH_BYPASS: true | |
| BETTER_AUTH_URL: http://localhost:8083/auth | |
| BETTER_AUTH_JWKS_URL: http://localhost:8083/auth/jwks | |
| BETTER_AUTH_ISSUER: http://localhost:8083 | |
| BETTER_AUTH_AUDIENCE: http://localhost:8083 | |
| BETTER_AUTH_SECRET: ci-test-secret-key-for-jwt-signing | |
| steps: | |
| - name: Skip notification | |
| if: env.RUN_TESTS != 'true' | |
| run: echo "No relevant changes detected, skipping integration tests" | |
| - name: Checkout Code | |
| if: env.RUN_TESTS == 'true' | |
| uses: actions/checkout@v6 | |
| - name: Set Up Node.js | |
| if: env.RUN_TESTS == 'true' | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| - name: Cache node_modules | |
| if: env.RUN_TESTS == 'true' | |
| id: cache-node-modules | |
| uses: actions/cache@v5 | |
| with: | |
| path: node_modules | |
| key: node-modules-${{ runner.os }}-node24-${{ hashFiles('package-lock.json') }} | |
| - name: Validate cached node_modules | |
| id: validate-cache | |
| if: env.RUN_TESTS == 'true' && steps.cache-node-modules.outputs.cache-hit == 'true' | |
| run: | | |
| if npm ls --depth=0 >/dev/null 2>&1; then | |
| echo "valid=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "valid=false" >> $GITHUB_OUTPUT | |
| echo "Cache corrupted, will reinstall" | |
| rm -rf node_modules | |
| fi | |
| - name: Install Dependencies | |
| if: env.RUN_TESTS == 'true' && (steps.cache-node-modules.outputs.cache-hit != 'true' || steps.validate-cache.outputs.valid == 'false') | |
| run: npm ci | |
| env: | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Lint .env file | |
| if: env.RUN_TESTS == 'true' | |
| run: npm run lint:env | |
| - name: Build | |
| if: env.RUN_TESTS == 'true' | |
| run: npm run build | |
| - name: Build mock API server | |
| if: env.RUN_TESTS == 'true' | |
| run: cd dev_env/mock-api-server && npm ci && npm run build | |
| - name: Initialize database | |
| if: env.RUN_TESTS == 'true' | |
| env: | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| touch .env | |
| node dev_env/init-db.mjs | |
| - name: Start services | |
| if: env.RUN_TESTS == 'true' | |
| env: | |
| NODE_ENV: test | |
| USE_MOCK_SERVICES: 'false' | |
| ANON_DEVICE_JWT_SECRET: ci-test-secret-key-for-anonymous-devices | |
| LIBRARY_METADATA_URL: http://localhost:9090 | |
| SLACK_WEBHOOK_URL: http://localhost:9090 | |
| SLACK_WXYC_REQUESTS_WEBHOOK: /services/T00/B00/ci-test-webhook | |
| TUBAFRENZY_URL: http://localhost:9090 | |
| MIRROR_API_KEY: ci-test-mirror-key | |
| MOCK_API_PORT: '9090' | |
| # Internal endpoints (ETL notify, tubafrenzy webhook). | |
| # authenticateInternal() fails closed on an empty key, so the | |
| # tubafrenzy-webhook integration spec needs this wired explicitly. | |
| # Matches the spec's INTERNAL_KEY fallback default. | |
| ETL_NOTIFY_KEY: test-secret-key | |
| # BS#1261 — request-line ban CRUD + check-request-ban bucketing. | |
| # Same shape as ETL_NOTIFY_KEY but distinct env var. | |
| ROM_INTERNAL_KEY: test-rom-secret-key | |
| # LML client limiter: disable in CI. The integration suite runs | |
| # hundreds of fire-and-forget /lookup calls across spec files under | |
| # --runInBand; the module-level TokenBucket(50/min) would drain | |
| # partway through and subsequent calls would queue ~1200ms each, | |
| # timing out metadata-lml tests at 30s (BS#955). Mirrors the | |
| # unit-test override convention at | |
| # tests/unit/services/lml.client.test.ts:792-793. | |
| LML_CLIENT_MAX_CONCURRENT: '10000' | |
| LML_CLIENT_RATE_PER_MIN: '60000' | |
| # Default org wiring: matches the slug seeded by dev_env/seed_db.sql. | |
| # Consumed by the org-sync hooks and the user.create.after | |
| # auto-membership hook in shared/authentication/src/auth.definition.ts. | |
| # Without this, those hooks log a warning and no-op, which the | |
| # auth-auto-membership integration spec specifically exercises. | |
| DEFAULT_ORG_SLUG: test-org | |
| # Catalog-search alias LATERAL: align GHA with the ci-profile | |
| # docker-compose default (dev_env/docker-compose.yml:245). Without | |
| # this, `getCatalogSearchAliasConfig().enabled` is false in CI and | |
| # tests/integration/library-search-alias.spec.js's BS#1269 + | |
| # BS#1383 cases either silently warn-skip (the OHSEES test) or | |
| # fail-fast (the discogs_member test). The flag has been on by | |
| # default in `npm run ci:testmock` since BS#1269 landed; this | |
| # closes the GHA-vs-compose drift that surfaced when BS#1383 | |
| # promoted its case from warn-skip to fail-fast. | |
| CATALOG_SEARCH_ALIAS_ENABLED: 'true' | |
| run: | | |
| node dev_env/mock-api-server/dist/server.js > /tmp/mock-api.log 2>&1 & | |
| node apps/auth/dist/app.js > /tmp/auth.log 2>&1 & | |
| node apps/backend/dist/app.js > /tmp/backend.log 2>&1 & | |
| timeout 30 bash -c 'until curl -sf http://localhost:9090/_admin/health > /dev/null 2>&1; do sleep 1; done' | |
| timeout 30 bash -c 'until curl -sf http://localhost:8083/healthcheck > /dev/null 2>&1; do sleep 1; done' | |
| timeout 30 bash -c 'until curl -sf http://localhost:8081/healthcheck > /dev/null 2>&1; do sleep 1; done' | |
| - name: Run Integration Tests | |
| if: env.RUN_TESTS == 'true' | |
| # Note: Tests must run sequentially (--runInBand) because they share | |
| # show state, DJ sessions, and flowsheet entries. Parallel execution | |
| # causes tests to interfere with each other's join/leave operations. | |
| run: npm run ci:test | |
| - name: Show service logs on failure | |
| if: env.RUN_TESTS == 'true' && failure() | |
| run: | | |
| echo "=== Mock API Server Log ===" | |
| cat /tmp/mock-api.log || true | |
| echo "" | |
| echo "=== Auth Service Log ===" | |
| cat /tmp/auth.log || true | |
| echo "" | |
| echo "=== Backend Service Log ===" | |
| cat /tmp/backend.log || true | |
| - name: Upload Coverage Report | |
| uses: actions/upload-artifact@v7 | |
| if: env.RUN_TESTS == 'true' && always() | |
| with: | |
| name: integration-test-coverage | |
| path: coverage/ | |
| retention-days: 14 | |
| # Pre-flight migrate-dryrun: restores the most recent automated RDS | |
| # snapshot to a sandbox DB instance and runs `node scripts/dryrun-migrate.mjs` | |
| # against it. Catches preconditions that depend on prod data shape (e.g. | |
| # the `RAISE EXCEPTION` guards added per WXYC/Backend-Service#705) at | |
| # PR-review time rather than at deploy time. See WXYC/Backend-Service#726. | |
| # | |
| # Requires repo-level secrets (one-time ops setup): | |
| # AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION — already present | |
| # for the deploy workflow; needs additional rds:* permissions plus | |
| # ec2:AuthorizeSecurityGroupIngress / ec2:RevokeSecurityGroupIngress | |
| # on SGs tagged Purpose=migrate-dryrun (#757). | |
| # PROD_DB_ID — RDS instance identifier of the prod DB. | |
| # PROD_DB_NAME, PROD_DB_USERNAME, PROD_DB_PASSWORD — match the source | |
| # instance's master credentials (snapshots restore with the source's | |
| # master user). | |
| # SG_DRYRUN_GHA — security group id. Created tagged Purpose=migrate-dryrun | |
| # by scripts/provision-dryrun-aws.mjs. The job authorizes its | |
| # runner's /32 just-in-time and revokes it in always() teardown, | |
| # so the SG carries no persistent ingress rules between runs. | |
| # See WXYC/Backend-Service#726 (preflight) and #757 (JIT ingress). | |
| migrate-dryrun: | |
| name: Migration Dry-Run (prod-shaped data) | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.db-init == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout Code | |
| uses: actions/checkout@v6 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| - name: Install minimal deps for dryrun script | |
| # postgres + drizzle-orm are the only runtime imports the script | |
| # needs. The `--workspaces=false` flag skips workspace resolution | |
| # so npm doesn't try to fetch the `@wxyc/shared` workspace dep | |
| # from GitHub Packages — that lookup needs NPM_TOKEN, and even | |
| # with the token wired in, install + resolution costs ~30s of | |
| # CI time we don't need here. The script imports | |
| # `../dev_env/format-pg-error.mjs` by relative path, so it | |
| # doesn't need anything inside dev_env to be npm-installed. | |
| run: npm install --workspaces=false --no-save --no-package-lock --no-audit --no-fund postgres drizzle-orm | |
| env: | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Configure AWS credentials | |
| # Long-lived AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY pattern | |
| # already used by .github/workflows/deploy-base.yml (lines 246, 254, | |
| # 265, 329, 337, 363, 454). Migrating to OIDC is a separate concern. | |
| uses: aws-actions/configure-aws-credentials@v6 | |
| with: | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| aws-region: ${{ secrets.AWS_REGION }} | |
| - name: Authorize runner IP on SG_DRYRUN_GHA | |
| # GitHub Actions runners pull from a pool of ~3000 egress CIDRs that | |
| # rotate monthly (api.github.com/meta `.actions`). The default AWS | |
| # SG rule limit is 60, so bulk-authorizing the entire pool is not | |
| # an option. Instead, authorize just this run's runner IP /32 here | |
| # and revoke it in the always() teardown step below. Net result: | |
| # SG carries one transient rule per concurrent run; quota usage is | |
| # bounded by run concurrency (db-init paths change rarely, so this | |
| # stays well under 60 in practice). | |
| run: | | |
| set -euo pipefail | |
| RUNNER_IP=$(curl -sS --max-time 10 https://checkip.amazonaws.com | tr -d '[:space:]') | |
| if [[ ! "$RUNNER_IP" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "checkip.amazonaws.com returned unexpected payload: ${RUNNER_IP}" >&2 | |
| exit 1 | |
| fi | |
| echo "RUNNER_IP=${RUNNER_IP}" >> "$GITHUB_ENV" | |
| # Tolerate InvalidPermission.Duplicate so a re-run that hits the | |
| # same runner is a no-op rather than a hard failure. | |
| aws ec2 authorize-security-group-ingress \ | |
| --group-id "${{ secrets.SG_DRYRUN_GHA }}" \ | |
| --protocol tcp --port 5432 \ | |
| --cidr "${RUNNER_IP}/32" \ | |
| 2> >(grep -v 'InvalidPermission.Duplicate' >&2) \ | |
| || aws ec2 describe-security-groups \ | |
| --group-ids "${{ secrets.SG_DRYRUN_GHA }}" \ | |
| --query "SecurityGroups[0].IpPermissions[?FromPort==\`5432\`].IpRanges[?CidrIp=='${RUNNER_IP}/32']" \ | |
| --output text | grep -q "${RUNNER_IP}" | |
| - name: Restore latest prod snapshot to ephemeral sandbox | |
| id: restore | |
| # The sandbox lands in the dedicated `wxyc-dryrun-public` subnet | |
| # group rather than inheriting prod's. Prod's subnet group is all | |
| # private — `--publicly-accessible` is silently ineffective there | |
| # (no IGW route, no public IP). The dedicated group covers public | |
| # subnets so the sandbox gets a real public IP and the GHA runner | |
| # can reach it. Ingress is gated by SG_DRYRUN_GHA; the JIT-authorize | |
| # step above adds this runner's /32 just before the connection. | |
| # The sandbox, the SG rule, and the subnet-group reference are | |
| # all torn down in the always-run steps below. | |
| run: | | |
| set -euo pipefail | |
| # Filter to Status==available so we don't pick up a snapshot | |
| # AWS is still mid-creating (RDS's automated-backup window | |
| # raced this step on PR #1348 / run 27185185066 → exit 254 | |
| # 'DBSnapshot must have state available but actually has | |
| # creating'). The trailing `[-1]` then gives the most-recent | |
| # *available* snapshot. | |
| SNAPSHOT_ID=$(aws rds describe-db-snapshots \ | |
| --db-instance-identifier "${{ secrets.PROD_DB_ID }}" \ | |
| --snapshot-type automated \ | |
| --query 'sort_by(DBSnapshots[?Status==`available`], &SnapshotCreateTime)[-1].DBSnapshotIdentifier' \ | |
| --output text) | |
| SANDBOX_ID="dryrun-${{ github.run_id }}" | |
| echo "SANDBOX_ID=$SANDBOX_ID" >> "$GITHUB_ENV" | |
| aws rds restore-db-instance-from-db-snapshot \ | |
| --db-instance-identifier "$SANDBOX_ID" \ | |
| --db-snapshot-identifier "$SNAPSHOT_ID" \ | |
| --db-instance-class db.t4g.micro \ | |
| --publicly-accessible \ | |
| --db-subnet-group-name wxyc-dryrun-public \ | |
| --vpc-security-group-ids "${{ secrets.SG_DRYRUN_GHA }}" | |
| aws rds wait db-instance-available --db-instance-identifier "$SANDBOX_ID" | |
| ENDPOINT=$(aws rds describe-db-instances \ | |
| --db-instance-identifier "$SANDBOX_ID" \ | |
| --query 'DBInstances[0].Endpoint.Address' --output text) | |
| echo "DB_HOST=$ENDPOINT" >> "$GITHUB_ENV" | |
| - name: Run drizzle:migrate against sandbox | |
| env: | |
| DB_HOST: ${{ env.DB_HOST }} | |
| DB_PORT: '5432' | |
| DB_NAME: ${{ secrets.PROD_DB_NAME }} | |
| DB_USERNAME: ${{ secrets.PROD_DB_USERNAME }} | |
| DB_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }} | |
| run: node scripts/dryrun-migrate.mjs | |
| - name: Revoke runner IP from SG_DRYRUN_GHA | |
| # Always runs so a failure in any earlier step still cleans up the | |
| # transient ingress rule. The guard on RUNNER_IP handles the case | |
| # where the authorize step failed before the env var was written. | |
| if: always() | |
| run: | | |
| if [ -n "${RUNNER_IP:-}" ]; then | |
| aws ec2 revoke-security-group-ingress \ | |
| --group-id "${{ secrets.SG_DRYRUN_GHA }}" \ | |
| --protocol tcp --port 5432 \ | |
| --cidr "${RUNNER_IP}/32" \ | |
| 2> >(grep -v 'InvalidPermission.NotFound' >&2) \ | |
| || true | |
| fi | |
| - name: Tear down sandbox | |
| # The guard on SANDBOX_ID handles the case where restore failed | |
| # before the env var was written; without it, delete-db-instance | |
| # would error and obscure the actual failure. | |
| if: always() | |
| run: | | |
| if [ -n "${SANDBOX_ID:-}" ]; then | |
| aws rds delete-db-instance \ | |
| --db-instance-identifier "$SANDBOX_ID" \ | |
| --skip-final-snapshot \ | |
| --delete-automated-backups | |
| fi |