Skip to content

Commit 358a957

Browse files
committed
ci(migrations): skip db:migrate on merges that change no migration files
Every push to main/staging ran db:migrate against the production/staging database even when the merge changed no schema, so a no-op migration would dial the DB and fail whenever it was at its connection limit (53300, slots reserved for SUPERUSER) — red-X'ing UI-only merges. Add a detect-migrations job (dorny/paths-filter on packages/db/migrations/**) and pass the result into the reusable migrations workflow, which now skips the apply step when no migration files changed. The migrate job still runs so downstream build/deploy jobs that need it are never skipped, and the flag defaults to 'true' so manual dispatch and any unknown value always apply migrations — the gate only ever skips a provably-empty change.
1 parent 02d254e commit 358a957

2 files changed

Lines changed: 39 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,42 @@ jobs:
4949
# Run database migrations before images are pushed: the ECR push triggers
5050
# CodePipeline, so migrating first guarantees the schema is in place before
5151
# the new app version deploys (replaces the removed ECS migration sidecar)
52+
# Detect whether this push actually changed any migration files, so the
53+
# migrate job below can skip connecting to the production/staging database on
54+
# merges that touch no schema (a no-op db:migrate otherwise dials the DB and
55+
# fails any time it is at its connection limit).
56+
detect-migrations:
57+
name: Detect Migration Changes
58+
runs-on: blacksmith-4vcpu-ubuntu-2404
59+
if: >-
60+
github.event_name == 'push' &&
61+
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
62+
outputs:
63+
migrations_changed: ${{ steps.filter.outputs.migrations }}
64+
steps:
65+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
66+
with:
67+
fetch-depth: 2 # Need the previous commit to diff migration paths
68+
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4
69+
id: filter
70+
with:
71+
filters: |
72+
migrations:
73+
- 'packages/db/migrations/**'
74+
75+
# The job always runs on push to main/staging so downstream build/deploy jobs
76+
# that `need` it are never skipped; the actual db:migrate step is gated on
77+
# detect-migrations inside the reusable workflow.
5278
migrate:
5379
name: Migrate DB
54-
needs: [test-build]
80+
needs: [test-build, detect-migrations]
5581
if: >-
5682
github.event_name == 'push' &&
5783
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
5884
uses: ./.github/workflows/migrations.yml
5985
with:
6086
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
87+
migrations_changed: ${{ needs.detect-migrations.outputs.migrations_changed }}
6188
secrets: inherit
6289

6390
# Same ordering for dev (schema push before the dev image lands in ECR)

.github/workflows/migrations.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ on:
77
description: Target environment (production, staging, or dev)
88
required: true
99
type: string
10+
migrations_changed:
11+
description: >-
12+
Whether this push changed migration files. When 'false' the versioned
13+
apply step is skipped. Defaults to 'true' so manual dispatch and any
14+
omitted/unknown value always applies migrations (never silently skip).
15+
required: false
16+
type: string
17+
default: 'true'
1018
workflow_dispatch:
1119
inputs:
1220
environment:
@@ -61,6 +69,7 @@ jobs:
6169
DATABASE_URL: ${{ inputs.environment == 'production' && secrets.DATABASE_URL || inputs.environment == 'staging' && secrets.STAGING_DATABASE_URL || inputs.environment == 'dev' && secrets.DEV_DATABASE_URL || '' }}
6270
MIGRATION_DATABASE_URL: ${{ inputs.environment == 'production' && secrets.MIGRATION_DATABASE_URL || inputs.environment == 'staging' && secrets.STAGING_MIGRATION_DATABASE_URL || '' }}
6371
ENVIRONMENT: ${{ inputs.environment }}
72+
MIGRATIONS_CHANGED: ${{ inputs.migrations_changed }}
6473
run: |
6574
if [ -z "$DATABASE_URL" ]; then
6675
echo "ERROR: no database URL secret resolved for environment '${ENVIRONMENT}'" >&2
@@ -70,6 +79,8 @@ jobs:
7079
if [ "${ENVIRONMENT}" = "dev" ]; then
7180
echo "Dev environment — pushing schema directly (db:push)"
7281
bun run db:push --force
82+
elif [ "${MIGRATIONS_CHANGED}" = "false" ]; then
83+
echo "No migration files changed in this push — skipping db:migrate (nothing to apply)."
7384
else
7485
echo "Applying versioned migrations (db:migrate)"
7586
bun run ./scripts/migrate.ts

0 commit comments

Comments
 (0)