Skip to content

ci: add GHCR release workflow using the TES/RAG BuildKit-secret pattern - #11

Merged
man4ish merged 1 commit into
mainfrom
feature/api-gateway-ghcr-release
Aug 15, 2026
Merged

ci: add GHCR release workflow using the TES/RAG BuildKit-secret pattern#11
man4ish merged 1 commit into
mainfrom
feature/api-gateway-ghcr-release

Conversation

@man4ish

@man4ish man4ish commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Blocked by #12 — API Gateway GHCR publish is blocked because IAM_CLIENT_READ_TOKEN is not currently available to the GitHub Actions workflow.

Problem

This repo has never had working GHCR publish automation. Its Dockerfile required the parent directory as build context (COPY omnibioai-iam-client /tmp/...) — a private sibling repo — and no GitHub Actions workflow anywhere in this org has ever solved cross-repo private checkout. The one published image (latest, 2026-07-10) was pushed manually, once, and now predates the merged PR3b signing code (#10) by over a month.

Fix

Adopts the exact pattern already proven in production by omnibioai-tes and omnibioai-rag — no new mechanism invented:

  • Dockerfile: omnibioai-iam-client is now a pinned git+https dependency (pyproject.toml), installed via pip install . inside a RUN --mount=type=secret,id=github_token layer. Build context is . alone. Never ARG/ENV (those leak into build logs).
  • pyproject.toml: pins omnibioai-iam-client@v0.1.3, matching TES/RAG's own entries. Also adds [tool.hatch.metadata] allow-direct-references = true — required because this repo (unlike TES/RAG, both setuptools-backed) uses hatchling, which otherwise rejects direct-reference dependencies outright. Discovered by an actual failing local build, not assumed.
  • .github/workflows/ci.yml (new): single docker-publish job modeled on omnibioai-security-audit's own tag-gated job. Triggers on push to main, v*.*.* tags, and workflow_dispatch; the job itself only runs for a real tag or manual dispatch. contents: read + packages: write only. Publishes latest, the semver tag, and (new to this org) an immutable sha-<short> tag.

IAM_CLIENT_READ_TOKEN is referenced as a BuildKit secret but not created, assumed present, or guessed at. If missing, the workflow fails cleanly at the private-dependency install step — an intentional, visible gap, not something worked around here.

Validation (local only — nothing pushed)

  • YAML parses cleanly.
  • Full local BuildKit build using the same GHCR_PULL_TOKEN already in omnibioai-studio/.env for this exact purpose (same credential TES/RAG's own local builds use), passed via --secret id=github_token,env=GHCR_PULL_TOKEN — never on the command line. First attempt failed on the hatchling gap above; fixed; rebuild succeeded. Container boot-tested: GET /health200 {"status":"ok"}. Local image/container removed after.
  • docker history + an in-image filesystem search both confirm no credential value anywhere in the built image.
  • Full existing test suite: 199 passed, 4 pre-existing failures (test_iam_client.py cache-key-prefix bug, unrelated, reproduced identically on main).

Not done here

No tag pushed, no image published, no secret created/modified, no other repository touched.

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

HIPAA PR3b GHCR follow-up: closes the API Gateway release-mechanism gap
identified in the prior read-only investigation. This repo's Dockerfile
previously required the *parent* directory as its build context (COPY
omnibioai-iam-client /tmp/...), which no GitHub Actions workflow in this
org has ever been able to do for a private sibling repository -- exactly
why this repo had zero working publish automation despite one image
having been pushed manually, once, over a month ago.

Dockerfile: adopts the exact pattern already proven in production by
omnibioai-tes and omnibioai-rag instead of inventing a new one.
omnibioai-iam-client is now a pinned git+https dependency declared in
pyproject.toml, installed via `pip install .` inside a
`RUN --mount=type=secret,id=github_token` layer -- never ARG/ENV, which
would leak the token into BuildKit's build-log output for that layer; a
secret mount is never printed and never persists in any image layer,
confirmed directly (docker history + an in-container filesystem search
for both the secret mount path and any leftover git-config rewrite found
neither). Build context is now `.` alone. git added to apt-get (required
for pip's own git+https clone; the old raw-COPY approach never needed
it).

pyproject.toml: omnibioai-iam-client @ git+https://.../omnibioai-iam-client.git@v0.1.3,
same pinned-tag convention and version as TES/RAG's own entries for this
exact dependency. Additionally required: [tool.hatch.metadata]
allow-direct-references = true -- hatchling's own metadata validation
otherwise rejects any `name @ <url>` dependency outright ("cannot be a
direct reference unless..."). TES and RAG never needed this because both
are setuptools-backed, not hatchling; this repo's own [build-system]
already was, so this is a required fix, not a style choice -- discovered
by an actual failing local build before it was added, not assumed.
requirements.txt is intentionally left untouched (unrelated to this
build's dependency resolution now, but not removed without being asked).

.github/workflows/ci.yml (new): single docker-publish job, modeled on
omnibioai-security-audit's own tag-gated `docker` job. Triggers on push
to main, v*.*.* tags, and workflow_dispatch; the job itself only runs for
a real tag or an explicit manual dispatch (matches that repo's own
tag-gating, so an ordinary push to main never publishes). Least-privilege
permissions (contents: read, packages: write) -- nothing else. Publishes
ghcr.io/omnibioai/omnibioai-api-gateway with three tags: latest, the
semver from the pushed tag, and an immutable short commit SHA
(sha-<short>) -- this org's convention has never included the SHA tag
before; added because every prior GHCR-content-verification step in this
whole workstream had to fall back to manual docker-cp archaeology for
lack of one. IAM_CLIENT_READ_TOKEN is referenced as a BuildKit secret
(never a build arg/env var) but is NOT created, assumed present, or
guessed at here -- if missing, the workflow will fail cleanly at the
Dockerfile's private-dependency install step, which is an intentional,
visible configuration gap, not something this change works around.

Validation performed (local only, nothing pushed):
- YAML parses cleanly.
- Full local BuildKit build using GHCR_PULL_TOKEN, already present in
  omnibioai-studio's .env for this exact purpose (the same credential
  omnibioai-tes/omnibioai-rag's own local dev builds already use) --
  passed via --secret id=github_token,env=GHCR_PULL_TOKEN (never on the
  command line, never in argv, never printed). First attempt failed on
  the hatchling allow-direct-references gap above; fixed; second and
  third builds (after a later comment-only Dockerfile edit) both
  succeeded, producing a working image. Container boot-tested:
  GET /health -> 200 {"status":"ok"}. Local image and container removed
  after validation.
- git diff --check clean; docker history and an in-image filesystem
  search both confirm no credential value anywhere in the built image.
- Full existing test suite: 199 passed, 4 pre-existing failures
  (test_iam_client.py cache-key-prefix bug, unrelated, reproduced
  identically on main before this branch).

Not done here, deliberately: no tag pushed, no image published, no
secret created, no other repository touched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@man4ish
man4ish merged commit 46f9901 into main Aug 15, 2026
1 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant