An Automated Release Pipeline on GitHub Actions

Large engineering organizations can ship software many times per day — Amazon was already deploying every 11.6 seconds in 2011. Most teams won't need that scale, but the underlying practices are no longer exclusive to companies with dedicated release-engineering staff. With GitHub Actions and a few well-established conventions, a small team can automate versioning, changelog generation, and publishing without much ongoing effort.

This article walks through the essential building blocks: a simple branching model, semantic versioning, conventional commit messages, and a release automation tool. The examples use NPM packages, but the same approach applies to Python packages or Docker containers.

A Lightweight Branching Strategy

GitHub Flow is a minimal branching model that suits most teams working on modular codebases or microservices. The rules are deliberately few:

  • main is always in a releasable state.
  • Create a branch from main for each change.
  • Open a Pull Request when the work is complete.
  • Merge the Pull Request into main after approval.
  • Create a release by tagging main.

This model works best with short-lived branches and small changes. If you keep feature branches open for weeks, you'll spend increasing time resolving merge conflicts. Frequent, small releases reduce the risk associated with each deployment, and hotfix branches become less necessary — urgent fixes can simply be treated as regular changes that flow through the same pipeline.

Versioning with SemVer

Semantic versioning communicates the impact of a release in the version number itself, formatted as vX.Y.Z:

  • X — breaking change.
  • Y — new feature.
  • Z — bug fix or non-visible change.

Users can read the version and know whether an upgrade requires attention or can be done automatically.

Commit Message Conventions

Conventional Commits standardize how commit messages are written, enabling tooling to parse the Git history meaningfully. The core structure follows a type prefix with an optional scope, and importantly, a commit may declare a BREAKING CHANGE.

Three types matter most in practice:

  • fix — a bug fix.
  • feat — a new feature.
  • BREAKING CHANGE — requires user action.

This convention is the foundation for automatic version bumping and release-note generation.

Release Automation with Standard Version

Standard Version reads your Git log and performs the mechanical parts of a release:

  • Generates release notes from conventional commits.
  • Determines the next version number from existing Git tags.
  • Bumps the version in package.json.
  • Creates a commit with the release notes and version change.
  • Tags that commit.

Install it with NPM and define a script in package.json, or run it with npx. The tool can be triggered by your CI pipeline after merges, which makes every merge to main a potential release.

One crucial detail is avoiding an infinite build loop. Standard Version commits and pushes changes, which would normally trigger another workflow run. Adding a [skip ci] marker to the commit message tells GitHub Actions to ignore that commit. Configure this in package.json alongside the release script.

The GitHub Actions Workflow

GitHub Actions is configured with YAML files placed in a .github/workflows directory. A minimal example defines triggers, jobs, and steps. To have the workflow commit and push back to the repository, only a standard git config step is required in the job.

The complete setup lives in two places:

  1. package.json — holds release tooling configuration and the release script.
  2. .github/workflows/<your-workflow.yml> — defines when and how the pipeline runs.

What the Release Step Does

When a push to main triggers the workflow, npm run release executes and produces a complete release:

  • Writes a new section in CHANGELOG.md.
  • Computes the next version from tags.
  • Bumps package.json.
  • Creates the commit with those updates.
  • Tags the commit.

The subsequent git push --follow-tags origin main pushes both the new tag and the release commit back to the repository. The entire release decision becomes a matter of what commit messages contain.

Adjusting the Cadence

Releasing on every merge is fast and convenient when you have a strong test suite that runs before the workflow executes. If that cadence feels aggressive, the pipeline can be adapted for manual triggering. The core value remains: versioning, changelog, and tagging are automated, reproducible, and documented entirely in Git history.