Skip to content

feat(ci): render Docker Hub readme from versions.yml at build time#715

Merged
kojiromike merged 1 commit into
openemr:masterfrom
kojiromike:dockerhub-readme-automation
May 13, 2026
Merged

feat(ci): render Docker Hub readme from versions.yml at build time#715
kojiromike merged 1 commit into
openemr:masterfrom
kojiromike:dockerhub-readme-automation

Conversation

@kojiromike
Copy link
Copy Markdown
Member

Fixes #709

Short description of what this resolves:

Step 15 of the release runbook — updating the Docker Hub readme on https://hub.docker.com/r/openemr/openemr — was manual: maintainers edited docker/openemr/OVERVIEW.md by hand, then a workflow propagated it to Docker Hub. This PR removes that manual step. The readme is now rendered from tools/release/versions.yml (the same registry the slot rotation workflow already uses) and pushed by each production build workflow after a successful image push.

Changes proposed in this pull request:

  • New Twig template tools/release/templates/dockerhub-overview.md.twig (renamed from docker/openemr/OVERVIEW.md, with version-dependent values parameterized on slot scalars).
  • New renderer OpenEMR\Release\DockerHubOverviewRenderer (tools/release/src/) with a thin Symfony Console wrapper at tools/release/bin/render-dockerhub-overview.php and a release:render-dockerhub-overview Task entry. Unit tests cover slot interpolation, determinism, and error paths.
  • New composite action .github/actions/push-dockerhub-readme that checks out, renders, then calls peter-evans/dockerhub-description@v5.
  • Build workflows build-704, build-800, build-810, build-811 call the composite action as their final step (last-writer-wins is benign because renders are deterministic).
  • Retire .github/workflows/dockerhub-description.yml and the hand-edited docker/openemr/OVERVIEW.md. Drop the corresponding entry from tools/release/versions.yml.
  • Drop the per-build dated tags (e.g. 8.0.0.3-2026-03-25) from the readme — they're an immutable-naming mechanism for pinning, not date-display, and Docker Hub's Tags page already lists them. Replaced with one sentence explaining the scheme.

Notes

  • Blocked on the Docker Hub credential rotation in fix(ci): rotate revoked DOCKERHUB_TOKEN — readme workflow returning Forbidden #714 — the previous workflow has been failing with Forbidden on its last two runs. The new push uses the same DOCKERHUB_USERNAME / DOCKERHUB_TOKEN secrets, so the same fix unblocks both.
  • Flex tracks (build-322, build-323, build-edge, build-flex-core) are intentionally not wired up — they aren't in the rotation registry and their portion of the readme is hand-curated. Easy follow-up if/when they join the rotation.
  • Follow-up on openemr/openemr to remove step 15 from docs/RELEASE_PROCESS.md's release runbook.

Test plan

  • composer check clean in tools/release/ (phpcs, phpstan, rector dry-run, require-checker, phpunit — 83 tests).
  • actionlint clean on the four modified build workflows.
  • Local render of the template against the real versions.yml produces the expected markdown.
  • End-to-end push verifiable once fix(ci): rotate revoked DOCKERHUB_TOKEN — readme workflow returning Forbidden #714 lands: workflow_dispatch on build-800.yml from a topic branch and confirm the Docker Hub readme reflects the rendered output.

Copilot AI review requested due to automatic review settings May 12, 2026 14:27
@kojiromike kojiromike added docker github_actions Pull requests that update GitHub Actions code labels May 12, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Automates Docker Hub README updates for openemr/openemr by rendering a deterministic Markdown document from tools/release/versions.yml at build time, replacing the prior manual-edit + standalone workflow approach.

Changes:

  • Add a Twig-based README template and a PHP renderer + CLI/Taskfile entry to render from versions.yml.
  • Add a composite GitHub Action to render and push the Docker Hub description via peter-evans/dockerhub-description@v5.
  • Wire the composite action into production build workflows and remove the old dockerhub-description.yml workflow.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/release/versions.yml Removes the retired OVERVIEW.md mapping entry from the rotation registry.
tools/release/templates/dockerhub-overview.md.twig New Docker Hub README template parameterized by slot scalars.
tools/release/src/DockerHubOverviewRenderer.php New renderer to load versions.yml slots and render the Twig template.
tools/release/bin/render-dockerhub-overview.php New CLI wrapper to render to stdout or a file for CI usage.
tools/release/Taskfile.yml Adds a release:render-dockerhub-overview task (deps: setup).
tools/release/tests/DockerHubOverviewRendererTest.php Adds unit tests for slot interpolation, determinism, and error cases.
.github/actions/push-dockerhub-readme/action.yml New composite action to checkout, render README, and push to Docker Hub.
.github/workflows/build-704.yml Runs the composite action after pushing the legacy 7.0.4 image.
.github/workflows/build-800.yml Runs the composite action after pushing the current production image.
.github/workflows/build-810.yml Runs the composite action after pushing the next-track manifest/tags.
.github/workflows/build-811.yml Runs the composite action after pushing the dev-track manifest/tags.
.github/workflows/dockerhub-description.yml Removes the old workflow that pushed a hand-edited README file.
Comments suppressed due to low confidence (1)

tools/release/templates/dockerhub-overview.md.twig:21

  • The dated-tag example hard-codes 2026-03-25, which will become stale and potentially confusing over time. Consider using a generic placeholder (e.g. {{ current.patch }}-YYYY-MM-DD) or wording that avoids a specific date in the example while still illustrating the format.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tools/release/src/DockerHubOverviewRenderer.php
Comment thread tools/release/src/DockerHubOverviewRenderer.php
kojiromike added a commit that referenced this pull request May 12, 2026
)

Refs #714

#### Short description of what this resolves:

Adds a `workflow_dispatch` workflow that proves `DOCKERHUB_USERNAME` /
`DOCKERHUB_TOKEN` are valid for the readme-push API path, without
triggering an image build or modifying the readme content. Whoever
rotates the secret in the future (per #714) can click "Run workflow" and
know in seconds whether the new credential works.

#### Why two checks

A Docker Hub token can pass `docker login` (registry auth) and still
fail on `PATCH /v2/repositories/openemr/openemr/` (API auth), because
the API path needs Read+Write+Delete scope on the specific repo while
the registry only requires push permission. That is exactly the failure
mode #714 had to recover from — the `Update Docker Hub Description`
workflow was 403'ing while image pushes kept working.

#### Side effect to be aware of

The API check writes the current description back to itself via a no-op
`PATCH` so it actually exercises the write scope (a read-only token
would 200 on a `GET` and 403 on the `PATCH`). The readme content does
not change, but Docker Hub's `last_modified` timestamp on the repo is
bumped. The real readme push already does this on every run, so nothing
novel.

#### Changes proposed in this pull request:

- New `.github/workflows/dockerhub-credential-check.yml` —
`workflow_dispatch` only. Steps: checkout → `docker/login-action`
(validates registry auth) → setup PHP + Task → `task
ci:check-dockerhub-credential` (validates the API-path auth via login +
GET + no-op PATCH).
- New PHP tooling under `tools/release/` matching the existing pattern
(cf. `bin/render-dockerhub-overview.php` in #715):
- `src/DockerHubCredentialChecker.php` — orchestrates `POST
/v2/users/login/`, `GET /v2/repositories/<repo>/`, then no-op `PATCH
/v2/repositories/<repo>/` via `ext-curl`. Catches `JsonException`
internally and `RuntimeException` from the HTTP layer so the workflow
always emits exactly one diagnostic line.
- `src/DockerHubCredentialCheckResult.php` — pure result interpretation;
the testable surface, no HTTP mocking required. Maps 401/403 to
credential/scope failures and other non-200s to `UNEXPECTED_RESPONSE`
with the actual HTTP status surfaced.
- `src/DockerHubCredentialCheckStatus.php` — `OK` / `INVALID_CREDENTIAL`
/ `INSUFFICIENT_SCOPE` / `UNEXPECTED_RESPONSE` / `NETWORK_ERROR` enum.
  - `bin/check-dockerhub-credential.php` — Symfony Console one-shot.
- `tests/DockerHubCredentialCheckResultTest.php` — covers every status /
step-failure path (16 cases).
- New Taskfile entry `ci:check-dockerhub-credential`.
- `ext-curl` added to `composer.json` requires (declared explicitly so
`composer-require-checker` stays clean).

#### Test plan

- `composer check` clean (phpcs, phpstan, rector dry-run,
require-checker, 93 phpunit tests).
- `actionlint` clean.
- After merge: trigger from the Actions tab; with the currently-revoked
token, expect a clear `::error::` line distinguishing "invalid
credential" (HTTP 401/403 from `/users/login/`) from "lacks scope"
(login OK but 403 from the repo endpoint). After #714 lands and the
secret is rotated, re-trigger and expect a `::notice::` "Credential is
valid" line and a green check.
@kojiromike kojiromike force-pushed the dockerhub-readme-automation branch from e8e7018 to 87c4626 Compare May 13, 2026 15:23
The Docker Hub readme update for openemr/openemr was previously triggered
by edits to docker/openemr/OVERVIEW.md, which release runbook step 15
required maintainers to update by hand. The peter-evans/dockerhub-description
workflow then propagated the file. This left the readme out of sync
whenever the manual edit was forgotten and was the sole reason a maintainer
had to touch a markdown file as part of the release runbook.

Replace the hand-edited file with a Twig template
(tools/release/templates/dockerhub-overview.md.twig) rendered from
tools/release/versions.yml — the same registry that already drives slot
rotation for build workflows and Dockerfiles. A new composite action
(.github/actions/push-dockerhub-readme) renders the template and pushes
to the Docker Hub API. Each production build workflow (build-704,
build-800, build-810, build-811) calls it after a successful image push.
Render is deterministic from versions.yml so the same-day race between
nightly cron builds is benign — last writer wins with identical content.

Drop the per-build dated tags (e.g. 8.0.0.3-2026-03-25) from the readme.
Each build still pushes the immutable dated tag for pinning; users who
want a specific build find it on the Docker Hub Tags page or via
docker inspect. The readme now explains the scheme in one sentence
instead of mirroring listings Docker Hub already displays natively.

Closes openemr#709.

Assisted-by: Claude Code
@kojiromike kojiromike force-pushed the dockerhub-readme-automation branch from 87c4626 to ad5d8d8 Compare May 13, 2026 15:27
@kojiromike kojiromike merged commit 10e07ae into openemr:master May 13, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docker github_actions Pull requests that update GitHub Actions code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Automate DockerHub readme / repo-description update on release

2 participants