Skip to content

ci: publish releases to GHCR via release-please on merge to main - #15

Merged
imariel2d merged 4 commits into
mainfrom
ci/release-ghcr
Aug 3, 2026
Merged

ci: publish releases to GHCR via release-please on merge to main#15
imariel2d merged 4 commits into
mainfrom
ci/release-ghcr

Conversation

@imariel2d

@imariel2d imariel2d commented Aug 3, 2026

Copy link
Copy Markdown
Owner

What

Adds a release pipeline so merging to main can cut versioned releases and publish the container image to the GitHub Container Registry (GHCR).

The flow

  1. Merge feature PRs to main as usual.
  2. release-please keeps an open "release" PR (next version + changelog, derived from the Conventional-Commit history: feat: → minor, fix: → patch).
  3. Merging that release PR tags vX.Y.Z, updates CHANGELOG.md, and creates the GitHub Release.
  4. A gated job builds the root Dockerfile and pushes ghcr.io/imariel2d/keepr tagged X.Y.Z, X.Y, and latest.

The image build lives in the same workflow as release-please because a tag/release created with the built-in GITHUB_TOKEN does not trigger a second workflow.

Files

  • .github/workflows/release.yml — release-please job → gated GHCR image build (buildx + GHA cache).
  • release-please-config.json + .release-please-manifest.json — single root package, simple type, seeded at 0.0.0 so the first release proposes 0.1.0.
  • README.md — new "Releases & registry" section.

Security / scope

  • Uses the built-in GITHUB_TOKEN — no PAT to manage.
  • CI (ci.yml) stays read-only; only this workflow holds packages: write / contents: write.

Before it fully works (post-merge, one-time)

  • Package visibility: GHCR packages are private by default — make the package public (or pull with a token) if an unauthenticated host needs to docker pull.
  • App Platform still builds from source on push (.do/app.yaml); GHCR is a parallel immutable artifact (useful for the VPS/Droplet path). Optional: point App Platform at an image: source to deploy the exact released image.
  • Recommended: enable branch protection requiring the unit + e2e checks so main is green before a release is cut.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added automated releases for changes pushed to the main branch or manually triggered.
    • Added GitHub Releases and versioned container image publishing, including stable latest tags.
    • Added provenance and software bill of materials (SBOM) metadata to published images.
  • Bug Fixes
    • Configured App Platform deployments to run explicitly in Production mode.
  • Documentation
    • Documented release processes, versioning, image usage, permissions, and deployment options.
  • Chores
    • Reduced unnecessary files included in container builds.

Add a Release workflow that lets merging to main cut versioned releases and
publish the container image to the GitHub Container Registry:

- release-please (config + manifest) maintains a release PR from the
  Conventional-Commit history; merging it tags vX.Y.Z, writes CHANGELOG.md,
  and creates the GitHub Release.
- A gated image job then builds the root Dockerfile and pushes
  ghcr.io/imariel2d/keepr tagged X.Y.Z / X.Y / latest, using the built-in
  GITHUB_TOKEN. The build is in the same workflow because a tag/release made
  with GITHUB_TOKEN does not trigger a second workflow.

CI stays read-only; only this workflow holds packages/contents write. Manifest
seeds at 0.0.0 so the first release proposes 0.1.0. Documented in the README
"Releases & registry" section.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c662ffe8-91c7-44f4-a02d-c4da754b280e

📥 Commits

Reviewing files that changed from the base of the PR and between a8af762 and 68d82e5.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

Walkthrough

The PR adds release-please automation for pushes to main and manual recovery publishes. It creates GitHub Releases and publishes release-tagged container images to GHCR. It also sets the production runtime environment, limits the Docker build context, and documents the release process.

Changes

Release automation

Layer / File(s) Summary
Release metadata and workflow orchestration
.github/workflows/release.yml, .release-please-manifest.json, release-please-config.json
The workflow processes pushes to main. Release-please uses the root package version, unprefixed tags, and pre-1.0 bump settings to create release metadata.
Release image publication
.github/workflows/release.yml
The workflow checks out the selected release tag, builds the container with Docker Buildx, and publishes exact semver tags. It publishes major.minor and latest only for the newest stable release. It also publishes provenance and SBOM metadata.
Production deployment configuration and documentation
.do/app.yaml, .dockerignore, README.md
The App Platform service sets ASPNETCORE_ENVIRONMENT to Production. Docker excludes development and local files while retaining .env.example. The README documents release and image deployment procedures.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant release-please
  participant DockerBuildx
  participant GHCR
  GitHubActions->>release-please: Process push to main
  release-please-->>GitHubActions: Return release status and tag
  GitHubActions->>DockerBuildx: Build the selected release-tagged image
  DockerBuildx->>GHCR: Push exact and conditional semver tags
  DockerBuildx->>GHCR: Publish provenance and SBOM metadata
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: publishing releases to GHCR through release-please after merges to main.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/release-ghcr

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 32-36: Review the release-please action configuration identified
by id “rp” and verify that release PRs trigger all required CI and
branch-protection checks. If the default GITHUB_TOKEN prevents those workflows
from running automatically, configure the action to use the repository’s
approved GitHub App or PAT credential instead, preserving the existing
release-please config and manifest settings.
- Around line 13-17: Scope permissions per job in the release workflow: grant
the release-please job contents: write, issues: write, and pull-requests: write,
while limiting the image job to contents: read and packages: write. Remove the
shared top-level write permissions and ensure repository settings allow GitHub
Actions to create and approve pull requests.
- Around line 45-47: Update the image job’s actions/checkout@v4 step to set
persist-credentials to false, and restrict that job’s permissions to contents:
read and packages: write only. Remove the unrelated contents: write and
pull-requests: write grants while preserving the existing tag ref checkout.
- Around line 28-41: Make the image job in the release workflow runnable when
republishing an existing release, not only when release-please reports a newly
created release. Add a durable manual-dispatch path accepting an explicit
release tag, and use that tag for image publication while preserving the current
automatic release-created flow; verify both rerunning all jobs and manually
dispatching with an existing tag.

In `@README.md`:
- Around line 129-132: Update the README deployment guidance around the App
Platform and GHCR image description: replace “immutable artifact” with
“versioned artifact,” and document that exact deployments must pin the image by
digest rather than using a mutable tag such as latest or X.Y.

In `@release-please-config.json`:
- Around line 1-12: Update the release-please package configuration to set
include-component-in-tag to false, preserving the existing package-name and
release settings so generated tags use plain semver rather than the
keepr-prefixed format.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: db8b2688-484d-4ccd-95d0-d17b45e887fd

📥 Commits

Reviewing files that changed from the base of the PR and between d7df94c and 3f138f6.

📒 Files selected for processing (4)
  • .github/workflows/release.yml
  • .release-please-manifest.json
  • README.md
  • release-please-config.json

Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml
Comment thread .github/workflows/release.yml Outdated
Comment thread README.md Outdated
Comment thread release-please-config.json
imariel2d and others added 2 commits August 2, 2026 19:27
- release-please-config.json: set include-component-in-tag=false so tags are
  plain vX.Y.Z, not keepr-vX.Y.Z. The prefixed form fails docker/metadata-action
  semver parsing, which would drop the versioned image tags and publish only
  :latest.
- release.yml: scope permissions per job (read-only floor; release-please gets
  contents/issues/pull-requests write, image gets only packages:write) and add
  issues:write for release-please label/issue management.
- release.yml: set persist-credentials:false on the image job's checkout — it
  only builds/pushes to GHCR, never runs git, so GITHUB_TOKEN needn't persist.
- release.yml: add a workflow_dispatch recovery path with an explicit tag input
  so a failed image push after the release was already tagged can be re-run
  without release-please reporting a new release.
- README: describe the GHCR image as a versioned (not immutable) artifact and
  document digest pinning for exact deploys; correct the permissions note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The image is public on GHCR, so tighten what ships and how it's verified:

- Add .dockerignore that excludes appsettings.Development.json from the
  production build context (verified: it's gone from the image while
  appsettings.json and wwwroot still ship). Also keeps host bin/obj/node_modules
  and local .env files out of the context. The dev compose builds from
  ./src/Api, so local development is unaffected.
- .do/app.yaml: pin ASPNETCORE_ENVIRONMENT=Production so the app can never load
  the dev admin / MinIO defaults, even if the file were present.
- release.yml: attach SLSA provenance + an SBOM to the pushed image so consumers
  can verify the build and pin by digest.
- README: document the public-image hardening.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 56-68: Update the release image publishing flow around RELEASE_TAG
and docker/metadata-action to query the GitHub Releases API for the newest
stable X.Y.Z release before generating Docker metadata. Always publish the exact
RELEASE_TAG during manual recovery, but only enable the mutable X.Y and latest
tags when RELEASE_TAG matches the newest stable release; preserve the existing
release-please and workflow_dispatch trigger behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f416938c-cee0-425d-9b61-d76027463d73

📥 Commits

Reviewing files that changed from the base of the PR and between 3f138f6 and a8af762.

📒 Files selected for processing (5)
  • .do/app.yaml
  • .dockerignore
  • .github/workflows/release.yml
  • README.md
  • release-please-config.json

Comment thread .github/workflows/release.yml
The workflow_dispatch recovery path can rebuild any existing tag, but the image
job published latest and X.Y unconditionally — so re-publishing an OLD release
would drag those mutable tags back to older bytes. Gate latest and X.Y on "is
this the newest stable release?" (GET /releases/latest, which excludes drafts
and pre-releases); the exact X.Y.Z tag is always published. The automatic
release-please path short-circuits to true since the tag it just cut is by
definition the newest, so normal releases are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@imariel2d
imariel2d merged commit e1d4d43 into main Aug 3, 2026
2 of 3 checks passed
@imariel2d
imariel2d deleted the ci/release-ghcr branch August 3, 2026 02:40
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