Template for automated releases on GitHub, using the shared pipeline in codam-coding-college/release-config.
Pushing to main analyses the commit history, builds the Docker image, pushes it to
GHCR tagged with the new version and latest, commits the version bump,
and creates a GitHub release.
A minimal but realistic Express service, so the template exercises the same paths a real
Codam project does — npm ci, a multi-stage TypeScript build, and Nunjucks templating.
| Path | |
|---|---|
src/index.ts |
Express app; serves / (HTML) and /version (JSON) |
templates/base.njk |
Base layout with a {% block content %} |
templates/index.njk |
Extends the base, sets page_title |
tsconfig.json |
rootDir: src, outDir: build, module: NodeNext |
Dockerfile |
Multi-stage: build with dev dependencies, run with --omit=dev |
Run it locally with npm ci && npm run build && npm run start, then open
http://localhost:8000.
- Update
nameandversioninpackage.json. - Replace
src/,templates/andDockerfilewith your application. - That's it.
.github/workflows/release.ymlneeds no changes — the image name is derived from the repository slug.
Do not add a release.config.js. The shared workflow writes its own config at runtime,
and a local one would override it.
Do not add semantic-release or any @semantic-release/* package to
devDependencies. The shared workflow installs a pinned toolchain itself, outside the
repository.
Version bumps are derived from commit messages, so they have to follow Conventional Commits:
| Commit prefix | Release |
|---|---|
fix: |
patch — 1.2.3 → 1.2.4 |
feat: |
minor — 1.2.3 → 1.3.0 |
feat!: or a BREAKING CHANGE: footer |
major — 1.2.3 → 2.0.0 |
anything else (chore:, docs:, Bump …) |
no release |
The shared workflow takes optional inputs for repositories that need them — a different image name, submodules, a non-default Dockerfile path. See the release-config README.
/ and /version report the version read from package.json at runtime, which is why this
template serves it at all: it makes an otherwise invisible guarantee checkable.
The shared workflow builds the image inside semantic-release's prepare step, ordered
after @semantic-release/npm has written the new version to package.json. So the version
baked into the image always matches the tag it is published under. Build the image as an
ordinary workflow step instead — before semantic-release runs — and it ships the previous
version, silently.
If your service reports its own version anywhere (a startup log, a health endpoint, an MCP
handshake), it depends on that ordering. Keep package.json in the runtime image, as the
Dockerfile here does.