ci: label Docker images with their bundled schema migration set (#9908)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip publishes Docker images from this repo that self-hosters
and orchestration tooling deploy; the server refuses to start when its
database is missing any schema migration the build bundles
(`ensureMigrations`)
> - Anything that deploys these images therefore needs to know an
image's schema expectations *before* deploying it — today that requires
pulling the image or checking out the matching commit, both heavyweight
for tooling that just wants to answer "will this image boot against a
database migrated to N?"
> - Getting this wrong is expensive: an image ahead of the applied
schema crash-loops at startup and fails healthchecks after deployment
resources are already created
> - This pull request labels every published image with its bundled
migration set (last migration file and count), computed at build time
from `packages/db/src/migrations` in the same tree the image is built
from
> - The benefit is image/schema compatibility verification with two
cheap registry requests (manifest + config blob), no pull, and no drift
risk between label and image contents

## Linked Issues or Issue Description

No existing public issue; inline description per the feature request
template:

- **Subsystem affected**: Docker image publishing
(`.github/workflows/docker.yml`), `packages/db` migrations
- **Problem or motivation**: deployment tooling cannot cheaply determine
which schema migrations a published image expects; the only options are
pulling the image or checking out the matching commit. Deploying an
image whose bundled migrations exceed the applied schema makes the
server refuse to start, so this check is needed *before* resources are
created.
- **Proposed solution**: OCI labels
(`io.github.paperclipai.schema.last-migration`,
`io.github.paperclipai.schema.migration-count`), computed from the
migrations directory at build time via the existing
`docker/metadata-action` step. Keys use org-based reverse-DNS (the
GitHub org) so the label contract survives product-domain migrations.
- **Alternatives considered**: a schema manifest published beside the
image (second artifact to keep in sync — rejected); encoding schema info
in tags (tags already carry semver/sha meaning — rejected).
- **Roadmap alignment**: checked `ROADMAP.md` — no overlap with planned
core work; this is build metadata only.

## What Changed

- `.github/workflows/docker.yml`: a `Compute schema migration labels`
step (`ls` + `sort` over `packages/db/src/migrations/*.sql`) feeding two
custom labels into the existing `docker/metadata-action` step.

## Verification

- Workflow YAML validated locally.
- The label-computation commands run against the current tree produce
`last=0181_decision_training_retention_policy.sql`, `count=180`.
- After merge, verify with: fetch the image config blob for a fresh
`sha-*` tag from ghcr and confirm both `io.github.paperclipai.schema.*`
labels are present.

## Risks

- Low. Labels are metadata only; no change to image contents. If the
migrations directory ever moves, the label step fails the workflow
loudly (`ls` exits non-zero) rather than publishing wrong labels.

## Model Used

- Claude (Anthropic) — model id `claude-fable-5`, via the Claude Code
CLI harness with tool use (shell, file edits). Change authored and
verified agent-assisted, human-directed.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass (YAML validation; label
commands — no app code changed)
- [x] I have added or updated tests where applicable (n/a — CI metadata
only)
- [x] I have updated relevant documentation to reflect my changes (n/a —
workflow comment documents the labels)
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
This commit is contained in:
Devin Foley 2026-07-20 15:54:31 -04:00 committed by GitHub
parent 903bd157c2
commit fa4f900d1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 17 additions and 0 deletions

View File

@ -32,6 +32,20 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
# Deployment tooling reads these labels from the registry to verify an
# image's schema expectations against a migrator before deploying it,
# without pulling the image. The server refuses to start when the
# database is missing bundled migrations, so orchestrators need a cheap
# way to check image/migrator compatibility up front.
- name: Compute schema migration labels
id: schema
run: |
set -euo pipefail
last=$(ls packages/db/src/migrations/*.sql | sed 's|.*/||' | LC_ALL=C sort | tail -1)
count=$(ls packages/db/src/migrations/*.sql | wc -l | tr -d ' ')
echo "last=${last}" >> "$GITHUB_OUTPUT"
echo "count=${count}" >> "$GITHUB_OUTPUT"
- name: Docker meta
id: meta
uses: docker/metadata-action@v6
@ -42,6 +56,9 @@ jobs:
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
labels: |
io.github.paperclipai.schema.last-migration=${{ steps.schema.outputs.last }}
io.github.paperclipai.schema.migration-count=${{ steps.schema.outputs.count }}
- name: Build and push
uses: docker/build-push-action@v7