This document is for maintainers cutting a new release. For usage documentation, see github-action.md.
Pushing a tag before the image is published creates a race condition where
uses: SocketDev/socket-basics@vX.Y.Z resolves an action.yml that references
a GHCR image that doesn't exist yet. Follow this order every time:
1. Open a release PR using the [PR template](../.github/PULL_REQUEST_TEMPLATE.md) — the release checklist is pre-filled
2. Merge release PR to main (version bump + action.yml image ref update)
3. workflow_dispatch → publish-docker.yml with the new version
(builds, integration-tests, and pushes images to GHCR + Docker Hub)
4. Create git tag (e.g. v2.1.0) — image already exists, zero race condition
The release PR must include all three of these changes together:
-
socket_basics/version.pyupdated to new version -
pyproject.tomlversion:field updated to match -
action.ymlimage:ref updated todocker://ghcr.io/socketdev/socket-basics:<new-version>(after v2.0.0 this is auto-updated by the release workflow on every tag push — manual update only required for the initial v2.0.0 release)
💡
python-tests.ymlCI will fail ifaction.ymlandpyproject.tomlversions diverge, so a mismatch cannot be merged accidentally. The release workflow also auto-corrects this viasedon each tag push once the pre-built image switch is in place.
The changelog process has two phases:
Before the release PR (ongoing, optional but recommended):
As PRs land, you can manually add notes to the [Unreleased] section of
CHANGELOG.md. Think of it as a running human-readable
summary of what's accumulating. The PR template checklist asks you to review
this section before merging the release PR.
After the tag is pushed (fully automated):
scripts/update_changelog.py runs as part of
the publish pipeline and:
- Fetches the GitHub Release's auto-generated notes (built from merged PR titles,
categorised by PR labels per
.github/release.yml) - Replaces the
[Unreleased]section content with those generated notes - Inserts a new
## [VERSION] - DATEsection - Updates the comparison links at the bottom
- Commits the result back to
mainviasocket-release-bot
⚠️ The auto-generated notes replace whatever was in[Unreleased]— they are not merged with it. If you want specific wording in the CHANGELOG, the best lever is the PR title and description, since that's what GitHub's note generator pulls from. The[Unreleased]section is useful as a preview during development but should be treated as disposable, not authoritative.
publish-docker.yml runs automatically and:
- Builds and tests the Docker image
- Pushes to
ghcr.io/socketdev/socket-basics:<version>andsocketdev/socket-basics:<version> - Creates the GitHub Release with auto-generated notes (categorised by PR labels)
- Commits an updated
CHANGELOG.mdandaction.ymlback tomainviasocket-release-bot
Note: floating major version tags (
v2) are intentionally not published — see docs/github-action.md.
Tag protection rules prevent tags from being deleted or force-pushed after creation.
This is what makes @v2.0.0 trustworthy as a pin (beyond just recommending SHA pinning).
One-time setup (repo admin, Settings → Rules → Rulesets):
- Create a ruleset targeting tags matching
v* - Enable: Restrict deletions + Block force pushes
Once in place, every v* tag pushed is permanent. Combined with SHA pinning
and Dependabot, users get the full immutable release guarantee.
After the first publish, a GitHub org owner needs to flip the package visibility to public: Package settings → Change visibility → Public. One-time step.