Automated Releases and Tagging
Automate Git releases with tags, release notes, GitHub Releases, and CI/CD integration for consistent, repeatable software delivery.
Introduction
Shipping software should be boring. Most teams treat it like a high-wire act instead. Someone bumps a version number by hand, copies changelog entries into a text box, creates a tag at 4:30 on a Friday, and hopes nothing breaks. It’s slow, it’s stressful, and it’s the exact opposite of what continuous delivery promises.
Automated releases fix this. You merge to main and the pipeline does the rest: it figures out the next version, writes release notes, creates a git tag, publishes artifacts, and posts a GitHub or GitLab Release. No human in the loop. No Friday panic.
This post covers the full architecture — semantic versioning strategy, tag management, release note generation, platform-specific release APIs, and CI/CD pipelines that survive contact with production. If you ship software regularly, you should not be doing any of this by hand. For related reading, see commit message conventions for structured commits, changelog generation for release notes, and CI/CD pipelines for the delivery backbone.
When to Use / When Not to Use
Use automated releases when:
- You ship software on a regular cadence (weekly, biweekly, or more)
- You use semantic versioning and conventional commits
- You publish packages to registries (npm, PyPI, Maven, Docker Hub)
- You have multiple release channels (stable, beta, nightly)
- Your team is larger than 2 people and coordination overhead is growing
- You need reproducible, auditable release processes for compliance
Skip them when:
- You release rarely (quarterly or less) and manual processes are manageable
- Your project is a personal experiment with no external consumers
- Your commit history has no structure to derive version bumps from
- Your organization has regulatory requirements that mandate human sign-off at each step
Core Concepts
Automated releases rest on three pillars:
- Semantic Versioning (SemVer) — A versioning scheme that encodes meaning in version numbers:
MAJOR.MINOR.PATCH. Breaking changes bump MAJOR, new features bump MINOR, bug fixes bump PATCH. - Conventional Commits — A structured commit message format that machines can parse to determine which version component to bump.
- Release Automation — CI/CD logic that reads commits, calculates the next version, creates tags, generates notes, and publishes artifacts.
graph TD
A[Developer Pushes Code] --> B{CI Pipeline Triggered}
B --> C[Analyze Commits Since Last Tag]
C --> D{Determine Version Bump}
D -->|BREAKING CHANGE| E[Bump MAJOR]
D -->|feat| F[Bump MINOR]
D -->|fix| G[Bump PATCH]
E --> H[Create Git Tag vX.Y.Z]
F --> H
G --> H
H --> I[Generate Release Notes]
I --> J[Publish Artifacts]
J --> K[Create GitHub/GitLab Release]
K --> L[Notify Stakeholders]
Architecture or Flow Diagram
A production-grade automated release system spans multiple stages and external services. The pipeline kicks off when code lands on the release branch. It reads commits since the last tag, figures out which version component to bump, creates an annotated tag, builds and publishes artifacts, generates a changelog, posts a platform release, and notifies whoever needs to know.
graph LR
subgraph "Developer Workflow"
A[Code Changes] --> B[Conventional Commits]
B --> C[Pull Request]
C --> D[Merge to Main]
end
subgraph "CI/CD Pipeline"
D --> E[Analyze Commits]
E --> F[Calculate Next Version]
F --> G[Create Git Tag]
G --> H[Build Artifacts]
H --> I[Run Post-Tag Tests]
I --> J[Generate Changelog]
end
subgraph "Publishing"
J --> K[GitHub/GitLab Release]
J --> L[Package Registry]
J --> M[Container Registry]
J --> N[Documentation Site]
end
subgraph "Notification"
K --> O[Slack/Teams]
L --> O
M --> O
N --> O
end
The pipeline is triggered by a merge to the release branch. It reads commits since the last tag, calculates the version bump, creates an annotated tag, builds and publishes artifacts, generates a changelog, posts a platform release, and notifies stakeholders.
Step-by-Step Guide / Deep Dive
Step 1: Adopt Semantic Versioning
Before automating anything, agree on what version numbers mean. SemVer defines the basics:
- MAJOR (X.0.0) — Incompatible API changes
- MINOR (0.X.0) — Backwards-compatible new functionality
- PATCH (0.0.X) — Backwards-compatible bug fixes
- Pre-release (1.0.0-alpha, 1.0.0-beta.1) — Unstable versions for testing
You’ll also need to decide on edge cases that SemVer doesn’t cover:
- Do documentation-only changes warrant a PATCH bump?
- How do you handle security patches?
- What’s your pre-release naming convention?
Write these decisions down. Future you will thank present you.
Step 2: Enforce Conventional Commits
Automated version calculation depends on structured commit messages. The format is:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Types that drive version bumps:
feat→ MINOR bumpfix→ PATCH bumpBREAKING CHANGEin footer → MAJOR bumpdocs,chore,ci,test,refactor→ no bump
Install commitlint and husky to enforce this at commit time:
npm install --save-dev @commitlint/cli @commitlint/config-conventional
npx commitlint --init
Create .commitlintrc.json:
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"type-enum": [
2,
"always",
[
"feat",
"fix",
"docs",
"style",
"refactor",
"test",
"ci",
"chore",
"revert"
]
]
}
}
Step 3: Choose Your Release Tool
Several tools handle release automation. Pick based on your ecosystem:
| Tool | Ecosystem | Key Features |
|---|---|---|
| semantic-release | Node.js/npm | Full automation, plugins for every registry |
| release-it | Universal | Interactive or CI mode, Git + npm + GitHub |
| auto | Universal | Plugin-based, great for monorepos |
| goreleaser | Go | Binary builds, homebrew, snap, Docker |
| python-semantic-release | Python | SemVer for Python packages |
| changesets | Monorepos | Per-package versioning, changelog generation |
Step 4: Configure semantic-release
For Node.js projects, semantic-release is the most battle-tested option. Install it:
npm install --save-dev semantic-release @semantic-release/git @semantic-release/changelog @semantic-release/github
Create .releaserc.json:
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/git",
"@semantic-release/github"
]
}
This configuration does six things:
- Analyzes commits to determine version bump
- Generates release notes from conventional commits
- Updates CHANGELOG.md
- Publishes to npm
- Commits updated files back to the repo
- Creates a GitHub Release with notes
Step 5: Set Up the CI Pipeline
Create a GitHub Actions workflow at .github/workflows/release.yml:
name: Release
on:
push:
branches:
- main
permissions:
contents: write
issues: write
pull-requests: write
packages: write
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release
Key configuration notes:
fetch-depth: 0is non-negotiable — semantic-release needs the full git history to calculate versionspersist-credentials: falsestops the checkout action’s token from stepping on semantic-release’s own authenticationGITHUB_TOKENcomes built-in with GitHub ActionsNPM_TOKENneeds to live in your repository secrets
Step 6: Handle Multi-Branch Releases
For projects with LTS branches or beta channels, configure multiple release branches:
{
"branches": [
"main",
{ "name": "next", "prerelease": "beta" },
{ "name": "lts", "range": "1.x" }
]
}
This creates:
main→ stable releases (1.0.0, 1.1.0, 2.0.0)next→ beta releases (1.2.0-beta.1, 1.2.0-beta.2)lts→ patch releases on the 1.x line (1.0.1, 1.0.2)
Step 7: Create GitLab Releases
GitLab uses a similar approach but with its own release CLI tool:
release:
stage: release
image: registry.gitlab.com/gitlab-org/release-cli:latest
script:
- echo "Running release job"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
release:
tag_name: "v${CI_COMMIT_SHORT_SHA}"
description: "Release generated by CI pipeline"
assets:
links:
- name: "Documentation"
url: "https://docs.example.com/${CI_COMMIT_TAG}"
Step 8: Automate Tag Management
Tags come in two flavors: lightweight for internal markers, annotated for actual releases.
# Create an annotated release tag
git tag -a v1.2.3 -m "Release v1.2.3: New dashboard features"
# Push tags to remote
git push origin --tags
# List tags matching a pattern
git tag -l "v1.*"
# Delete a local tag
git tag -d v1.2.3
# Delete a remote tag
git push origin --delete v1.2.3
In automated pipelines, tags get created programmatically. Make sure your CI runner has permission to push them back to the repository.
Production Failure Scenarios + Mitigations
| Scenario | Impact | Mitigation |
|---|---|---|
| Duplicate tag creation | Pipeline fails, release incomplete | Use --no-verify with idempotent tag creation; check if tag exists before creating |
| Token expiration mid-release | Partial release — artifacts published but no tag | Use short-lived tokens with retry logic; implement rollback on failure |
| Race condition: two releases triggered simultaneously | Conflicting version numbers | Serialize release jobs; use a mutex or lock file |
| Commit analysis fails on malformed commit | Pipeline stalls | Configure commitlint to reject bad commits before merge; add fallback to manual version |
| Registry unavailable during publish | Release notes created but package not published | Implement retry with exponential backoff; alert on publish failure |
| Tag pushed but release notes generation fails | Orphaned tag with no release | Clean up orphaned tags automatically; run release notes as pre-publish step |
| Network partition during multi-registry publish | Inconsistent state across registries | Publish to primary registry first; use idempotent secondary publishes |
Trade-offs
| Decision | Pro | Con |
|---|---|---|
| Fully automated vs. manual approval gate | Faster releases, less human error | Less oversight, harder to catch mistakes before they ship |
| Trunk-based vs. branch-based releases | Simpler pipeline, fewer merge conflicts | Requires feature flags, less isolation for unstable work |
| Single toolchain vs. best-of-breed tools | Easier to maintain, consistent behavior | Vendor lock-in, may not fit all use cases |
| Annotated tags vs. lightweight tags | Rich metadata, git describe works better | Slightly more storage, marginally slower operations |
| Per-package versioning vs. fixed versioning (monorepo) | Independent release cycles, smaller diffs | Complex dependency resolution, harder to coordinate |
| GitHub Releases vs. custom release page | Built-in, integrates with ecosystem | Less control over formatting, platform-dependent |
Implementation Snippets
Bash: Manual Release Script
For teams not ready for full automation, a release script reduces human error:
#!/usr/bin/env bash
set -euo pipefail
# Usage: ./release.sh [major|minor|patch]
BUMP_TYPE="${1:-patch}"
CURRENT_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
CURRENT_VERSION="${CURRENT_TAG#v}"
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
case "$BUMP_TYPE" in
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
patch) PATCH=$((PATCH + 1)) ;;
*) echo "Usage: $0 [major|minor|patch]"; exit 1 ;;
esac
NEW_VERSION="v${MAJOR}.${MINOR}.${PATCH}"
echo "Releasing ${CURRENT_VERSION} → ${NEW_VERSION}"
# Create annotated tag
git tag -a "$NEW_VERSION" -m "Release ${NEW_VERSION}"
# Push tag
git push origin "$NEW_VERSION"
echo "Tag ${NEW_VERSION} pushed. Create release notes manually."
GitHub Actions: Publish to Multiple Registries
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
registry-url: "https://npm.pkg.github.com"
- run: npm ci
- run: npm run build
- run: npm test
- name: Publish to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish to GitHub Packages
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Python: semantic-release for Python Packages
# pyproject.toml
[tool.semantic_release]
version_variable = ["src/__init__.py:__version__"]
branch = "main"
upload_to_pypi = true
upload_to_release = true
commit_message = "chore(release): {version} [skip ci]"
# .github/workflows/release.yml
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Python Semantic Release
uses: python-semantic-release/python-semantic-release@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
pypi_token: ${{ secrets.PYPI_API_TOKEN }}
Go: GoReleaser Configuration
# .goreleaser.yaml
before:
hooks:
- go mod tidy
- go generate ./...
builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
archives:
- format: tar.gz
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else }}{{ .Arch }}{{ end }}
changelog:
sort: asc
use: github
filters:
exclude:
- "^docs:"
- "^test:"
- "^ci:"
release:
github:
owner: myorg
name: myproject
draft: false
prerelease: auto
Observability Checklist
A release pipeline without observability is a black box. Here’s what to track:
Logs
- Log every version calculation with the commits that drove it
- Record which artifacts were published to which registries
- Capture the full release notes generation output
- Log tag creation and push events with timestamps
Metrics
- Release frequency — Releases per day/week/month
- Release duration — Time from merge to published release
- Failure rate — Percentage of failed release attempts
- Time-to-recovery — How long to fix a broken release
- Artifact size trend — Track binary/package size over time
Traces
- Trace the full release pipeline as a single distributed trace
- Correlate release events with deployment events in production
- Link release tags to specific CI pipeline runs
Alerts
- Alert on release pipeline failure (immediate, high priority)
- Alert on version regression (e.g., v1.2.3 released after v1.3.0)
- Alert on publish timeout (registry may be down)
- Alert on orphaned tags (tag exists but no release notes)
Security/Compliance Notes
Automated releases touch sensitive systems and credentials. Treat the pipeline like critical infrastructure:
- Token management — Store registry tokens, API keys, and signing keys in a secrets manager (HashiCorp Vault, AWS Secrets Manager). Never hardcode them. Rotate tokens on a schedule.
- Pipeline permissions — Grant the CI runner the minimum permissions needed. Use
GITHUB_TOKENwith scoped permissions rather than a personal access token with full repo access. - Tag signing — Sign release tags with GPG for tamper evidence:
git tag -s v1.0.0 -m "Release". Verify signatures before consuming releases. - Artifact signing — Sign published artifacts (npm packages, Docker images, binaries) using Sigstore/cosign or similar. This provides provenance and integrity guarantees.
- Audit trail — Every automated release should leave an audit trail: who merged the PR, which commits triggered the release, what version was calculated, what artifacts were published. This is essential for SOC 2, ISO 27001, and similar compliance frameworks.
- Branch protection — Protect your release branch with required reviews, status checks, and signed commits. An automated release triggered by an unauthorized merge is a security incident.
- Supply chain security — Pin your CI action versions with SHA hashes, not tags. Tags can be moved; SHAs cannot. Use
actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11instead ofactions/checkout@v4.
Common Pitfalls / Anti-Patterns
Releasing from feature branches. Pick one branch — usually main or release — and release from there. Releasing from feature branches creates version chaos. You won’t know what’s actually in production.
Manual version bumps in CI. If you’re typing the version number into your pipeline yourself, you’ve defeated the point. Let the tooling calculate it from commits.
Skipping the changelog. Release notes aren’t optional. They’re the main way your users find out what changed. Automated changelog generation is table stakes.
Not pinning CI action versions. Using actions/checkout@v4 instead of a SHA-pinned version means you’re trusting a mutable tag. If someone moves that tag, your entire pipeline runs their code with your secrets.
Releasing on every commit. Not every commit needs a release. Trigger releases on merge to main, not on every push.
Ignoring pre-release versions. Beta, alpha, and RC releases catch problems before they hit stable. Skip them and your users become your QA team.
No rollback strategy. If a release breaks production, you should be able to revert to the previous version without anyone typing a command. Build the rollback into the pipeline.
Mixing release and deploy. Releasing creates a versioned artifact. Deploying runs that artifact in production. These are different concerns. Your release pipeline should create the artifact; your deployment pipeline decides when and where to run it.
Quick Recap Checklist
- Adopt semantic versioning and document your versioning policy
- Enforce conventional commits with commitlint and husky
- Choose a release automation tool (semantic-release, release-it, auto, goreleaser)
- Configure your tool with the correct branch strategy
- Set up CI pipeline with
fetch-depth: 0for full git history - Store registry tokens in a secrets manager
- Configure release note generation from conventional commits
- Set up artifact publishing to all target registries
- Create GitHub/GitLab Releases automatically
- Add notifications to Slack, Teams, or email
- Implement observability: logs, metrics, traces, alerts
- Sign release tags with GPG
- Pin CI action versions with SHA hashes
- Define a rollback strategy for failed releases
- Test the release pipeline end-to-end before relying on it
Interview Q&A
Semantic-release analyzes all commits since the last released tag using the conventional commits format. It applies these rules:
- If any commit contains
BREAKING CHANGEin the footer, it bumps the MAJOR version - If any commit has type
feat, it bumps the MINOR version - If any commit has type
fix, it bumps the PATCH version - Other types (
docs,chore,ci,test) do not trigger a version bump
The highest-priority bump wins — a single breaking change commit will trigger a MAJOR bump even if all other commits are fixes.
A git tag is a lightweight reference to a specific commit in your git repository. It's a pointer that marks a point in history — typically a version like v1.2.3. Tags can be lightweight (just a name) or annotated (with a message, tagger, and date).
A GitHub Release is a GitHub-specific feature built on top of a git tag. It adds:
- Release notes — formatted markdown describing what changed
- Binary assets — compiled artifacts attached to the release
- Draft and pre-release states — for reviewing before publishing
- Discussion and reactions — users can comment on releases
Every GitHub Release has an associated git tag, but not every git tag has a GitHub Release. Automated pipelines typically create both together.
Monorepos require per-package versioning because different packages may have different release cadences and breaking change impacts. The standard approaches are:
- Changesets — Developers include a "changeset" file with each PR describing the change type. The release tool reads these files, calculates per-package versions, and generates individual changelogs.
- Lerna + semantic-release — Lerna detects which packages changed since the last release and runs semantic-release independently for each affected package.
- Nx release — Nx's built-in release tooling analyzes the dependency graph and versions packages based on affected changes.
The key principle is independent versioning — a change to package A should not force a version bump on unrelated package B. However, if package A's public API changes in a breaking way, any package that depends on it may need a bump too.
A partial release is one of the most dangerous failure modes. The mitigation strategy depends on which stage failed:
- Before tag creation — Safe to retry. No side effects have occurred.
- After tag but before publish — The tag exists but artifacts are missing. Delete the tag, fix the issue, and retry. Or complete the publish manually.
- After publish but before release notes — Artifacts are live but undocumented. Generate release notes manually and create the platform release.
- After everything but notification — The release is complete. Just fix the notification pipeline.
The best defense is idempotent operations — each step should be safe to retry without creating duplicates. Tools like semantic-release handle this by checking for existing tags before creating new ones.
Git tags are mutable — anyone with write access to a repository can move a tag to point to a different commit. If you use actions/checkout@v4 and someone compromises the actions/checkout repository by moving the v4 tag to malicious code, your pipeline will execute that code with your secrets.
SHA hashes are immutable — they reference a specific commit that cannot be changed. Using actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 guarantees you're running exactly the code you audited.
Tools like Dependabot and Renovate can automate SHA updates when new versions are released, so you get both security and maintainability.
Extended Production Failure Scenarios
Duplicate Tag Creation
A CI pipeline retries a failed release job without checking if the tag already exists. The second attempt fails with fatal: tag 'v1.2.3' already exists, leaving the pipeline in an error state while artifacts were successfully published on the first attempt. The release is partially complete — the package is live but the GitHub Release was never created.
Mitigation: Make tag creation idempotent. Check for existing tags before creating: git tag -l "v1.2.3" | grep -q "v1.2.3" && echo "Tag exists" || git tag -a v1.2.3. Use tools like semantic-release that check for existing tags before attempting creation.
Release Notes Generation Failure
The release notes generator crashes because a commit message contains special characters (emoji, non-UTF8 bytes) that the template engine can’t handle. The tag was already pushed, the package published, but the GitHub Release was never created. Users see a version tag with no description.
Mitigation: Run release notes generation as a pre-publish step, before creating the tag. Sanitize commit messages before template rendering. If release notes fail after tag creation, implement automatic tag cleanup: git push --delete origin v1.2.3.
Extended Trade-offs
| Aspect | Manual Tagging | Automated (semantic-release) |
|---|---|---|
| Consistency | Human error — typos, wrong format | Deterministic — same input, same output |
| Overhead | High — manual steps per release | Zero after initial setup |
| Control | Full — choose any version number | Rule-based — follows conventional commits |
| Audit trail | Depends on discipline | Built into pipeline logs |
| Speed | Minutes per release | Seconds after merge |
| Error handling | Human catches mistakes | Pipeline fails fast with clear errors |
Implementation Snippet: GitHub Actions for Automated Tag and Release
name: Automated Release
on:
push:
branches: [main]
permissions:
contents: write
packages: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "22"
- run: npm ci
- run: npm test
- name: Calculate next version
id: version
run: |
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
COMMITS=$(git log "$LAST_TAG..HEAD" --oneline --no-merges)
MAJOR=$(echo "$COMMITS" | grep -ci "BREAKING CHANGE" || true)
MINOR=$(echo "$COMMITS" | grep -ci "^feat" || true)
PATCH=$(echo "$COMMITS" | grep -ci "^fix" || true)
CURRENT="${LAST_TAG#v}"
IFS='.' read -r CUR_MAJOR CUR_MINOR CUR_PATCH <<< "$CURRENT"
if [ "$MAJOR" -gt 0 ]; then
NEXT="$((CUR_MAJOR + 1)).0.0"
elif [ "$MINOR" -gt 0 ]; then
NEXT="${CUR_MAJOR}.$((CUR_MINOR + 1)).0"
elif [ "$PATCH" -gt 0 ]; then
NEXT="${CUR_MAJOR}.${CUR_MINOR}.$((CUR_PATCH + 1))"
else
echo "No version bump needed"
exit 0
fi
echo "next_version=v${NEXT}" >> $GITHUB_OUTPUT
- name: Create tag
if: steps.version.outputs.next_version
run: |
TAG="${{ steps.version.outputs.next_version }}"
git config user.name "Release Bot"
git config user.email "release@company.com"
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"
- name: Create GitHub Release
if: steps.version.outputs.next_version
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.next_version }}
generate_release_notes: true
draft: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Resources
- Semantic Versioning 2.0.0 Specification{:rel=“noopener noreferrer”} — The official SemVer specification
- Conventional Commits Specification{:rel=“noopener noreferrer”} — Structured commit message format
- semantic-release Documentation{:rel=“noopener noreferrer”} — Full automation for Node.js projects
- release-it{:rel=“noopener noreferrer”} — Universal release automation tool
- auto by Intuit{:rel=“noopener noreferrer”} — Plugin-based release tooling, excellent for monorepos
- GoReleaser{:rel=“noopener noreferrer”} — Release automation for Go projects
- Changesets by Atlassian{:rel=“noopener noreferrer”} — Per-package versioning for monorepos
- GitHub Actions: Publishing Packages{:rel=“noopener noreferrer”} — Official GitHub documentation
- Sigstore/cosign{:rel=“noopener noreferrer”} — Artifact signing and provenance verification
- Keep a Changelog{:rel=“noopener noreferrer”} — Guidelines for maintaining human-readable changelogs
Category
Related Posts
Automated Changelog Generation: From Commit History to Release Notes
Build automated changelog pipelines from git commit history using conventional commits, conventional-changelog, and semantic-release. Learn parsing, templating, and production patterns.
Automated Release Pipeline: From Git Commit to Production Deployment
Build a complete automated release pipeline with Git, CI/CD, semantic versioning, changelog generation, and zero-touch deployment. Hands-on tutorial for production-ready releases.
Commit Message Conventions: Conventional Commits, Angular Style, and Semantic Commits
Master commit message conventions including Conventional Commits, Angular style, and semantic commits. Learn automated changelog generation, linting enforcement, and team-wide standards.