ci: enforce production-safe database migrations
ci / verify (pull_request) Successful in 8m50s
ci / verify (pull_request) Successful in 8m50s
This commit is contained in:
@@ -10,22 +10,45 @@ runner_config=$root/deploy/ci/act-runner-v2-config.yaml
|
||||
provision=$root/scripts/provision-ci-runner.sh
|
||||
build_images=$root/scripts/ci-build-images.sh
|
||||
validate_semver=$root/scripts/ci-validate-semver.sh
|
||||
validate_migrations=$root/scripts/ci-validate-migrations.mjs
|
||||
build_images_test=$root/tests/ci/ci-build-images-test.sh
|
||||
migrations_test=$root/tests/ci/migrations-test.sh
|
||||
migration_base=$root/deploy/ci/production-migration-base
|
||||
adr=$root/docs/decisions/001-production-cicd.md
|
||||
runbook=$root/docs/runbooks/production-ci-cd.md
|
||||
migrator=$root/apps/api/cmd/migrate/main.go
|
||||
|
||||
for file in "$workflow" "$release_workflow" "$runner_service" "$runner_config" "$provision" "$build_images" "$validate_semver" "$build_images_test" "$adr" "$runbook"; do
|
||||
for file in "$workflow" "$release_workflow" "$runner_service" "$runner_config" "$provision" "$build_images" "$validate_semver" "$validate_migrations" "$build_images_test" "$migrations_test" "$migration_base" "$adr" "$runbook" "$migrator"; do
|
||||
[[ -f $file ]] || {
|
||||
printf 'missing CI/CD file: %s\n' "$file" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
if [[ $(wc -l <"$migration_base") -ne 1 ]]; then
|
||||
echo 'production migration baseline must contain exactly one line' >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -Fq 'SET standard_conforming_strings = on' "$migrator" || {
|
||||
echo 'database migrator does not pin standard SQL string semantics' >&2
|
||||
exit 1
|
||||
}
|
||||
if ! grep -Eq '^[0-9a-f]{40}$' "$migration_base"; then
|
||||
echo 'production migration baseline must be a full lowercase SHA' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
grep -q '^name: ci$' "$workflow"
|
||||
grep -q '^ push:$' "$workflow"
|
||||
grep -q '^ branches: \[main\]$' "$workflow"
|
||||
grep -q '^ pull_request:$' "$workflow"
|
||||
grep -q '^ verify:$' "$workflow"
|
||||
grep -Fq 'git checkout --detach "$CI_SHA"' "$workflow"
|
||||
grep -Fq 'test "$(git rev-parse HEAD)" = "$CI_SHA"' "$workflow"
|
||||
if grep -Fq 'git checkout --detach FETCH_HEAD' "$workflow"; then
|
||||
echo 'main/PR CI must not checkout mutable FETCH_HEAD after comparison fetches' >&2
|
||||
exit 1
|
||||
fi
|
||||
if grep -Eq "^[[:space:]]+tags:|Verify release tag ancestry" "$workflow" || \
|
||||
grep -Fq './scripts/ci-validate-semver.sh "$tag_name"' "$workflow"; then
|
||||
echo 'main/PR CI must not share the release-tag context' >&2
|
||||
@@ -53,6 +76,7 @@ for quality_workflow in "$workflow" "$release_workflow"; do
|
||||
fi
|
||||
grep -Fq 'test ! -f .git/shallow' "$quality_workflow"
|
||||
grep -Fq './tests/ci/ci-build-images-test.sh' "$quality_workflow"
|
||||
grep -Fq './tests/ci/migrations-test.sh' "$quality_workflow"
|
||||
grep -Fq './tests/ci/pipeline-test.sh' "$quality_workflow"
|
||||
grep -Fq './tests/ci/semver-test.sh' "$quality_workflow"
|
||||
grep -Fq 'docker-compose version' "$quality_workflow"
|
||||
@@ -81,6 +105,7 @@ for quality_workflow in "$workflow" "$release_workflow"; do
|
||||
fi
|
||||
|
||||
for gate in \
|
||||
'scripts/ci-validate-migrations.mjs' \
|
||||
'gofmt -l' \
|
||||
'go vet ./...' \
|
||||
'go test ./...' \
|
||||
@@ -103,6 +128,12 @@ for quality_workflow in "$workflow" "$release_workflow"; do
|
||||
"$quality_workflow" >&2
|
||||
exit 1
|
||||
}
|
||||
grep -Fq 'production_base=$(cat deploy/ci/production-migration-base)' \
|
||||
"$quality_workflow" || {
|
||||
printf '%s does not read the versioned production migration baseline\n' \
|
||||
"$quality_workflow" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
if [[ $(sed -n '/^ - name: Verify Go formatting$/,$p' "$workflow") != \
|
||||
@@ -111,6 +142,13 @@ if [[ $(sed -n '/^ - name: Verify Go formatting$/,$p' "$workflow") != \
|
||||
exit 1
|
||||
fi
|
||||
|
||||
grep -Fq 'CI_EVENT_BEFORE: ${{ github.event.before }}' "$workflow"
|
||||
grep -Fq 'CI_PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}' "$workflow"
|
||||
grep -Fq 'git merge-base --is-ancestor "$immutable_base" HEAD' "$workflow"
|
||||
grep -Fq '"$production_base" "$immutable_base"' "$workflow"
|
||||
grep -Fq 'node ./scripts/ci-validate-migrations.mjs "$production_base"' \
|
||||
"$release_workflow"
|
||||
|
||||
runner_image='docker.io/gitea/runner:2.0.0-dind-rootless@sha256:5b7b625ff773d0ee761788c47582503ec1b241fa5b81edebad48a57e663f4f3a'
|
||||
job_image='docker.io/library/node:24.16.0-bookworm@sha256:40ad9f3064e67d6860b4bc3fe1880b2953934fd6320ada990e45fe0efa6badd7'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user