Skip to content
ExponaBLOG
GIT & GITHUB FOR PROFESSIONAL DEVELOPERS · DAY 043 / 100

Automating releases: a Git & GitHub guide for developers

Changelogs and versions without manual work.

Expona Admin5 min read

The release bottleneck nobody talks about

Manually bumping version numbers, writing changelog entries, tagging commits, and pushing builds to package registries eats hours every release cycle. Most teams do this work by hand, copy-pasting git logs, second-guessing semantic versioning rules, and inevitably missing something. Automating releases: a Git & GitHub guide for developers shows how to eliminate that friction entirely using tools that understand your commit history and generate everything from conventional commits.

When you treat releases as code rather than a manual process, you gain consistency, auditability, and speed. Let's walk through how to build a release pipeline that requires almost no human intervention.

Release-please vs semantic-release: choosing your tool

Two mature tools dominate the automated release landscape: Google's release-please and semantic-release. Both parse commit messages, determine version bumps, and automate publishing. The choice depends on your workflow.

Release-please is optimized for GitHub. It creates a release PR that you review and merge, then automatically tags and publishes when that PR lands. This gives you a review gate and a clean audit trail.

Semantic-release is trigger-based and framework-agnostic. When you push to your main branch, it immediately analyzes commits, bumps versions, creates git tags, and publishes in one shot. No PR, no wait.

For teams that want human review before release, release-please wins. For CI/CD purists who trust their main branch and want instant releases, semantic-release is faster.

Here's a minimal release-please setup in a GitHub Actions workflow:

bash
name: release-please
on:
  push:
    branches: [main]

jobs:
  release-please:
    runs-on: ubuntu-latest
    steps:
      - uses: google-github-actions/release-please-action@v3
        with:
          release-type: node
          package-name: my-package

That's it. Push to main, release-please detects conventional commits, opens a PR with bumped versions and generated changelogs, and publishes when merged.

For semantic-release, you'd configure it in your CI pipeline to run after tests pass:

bash
name: release
on:
  push:
    branches: [main]

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm test
      - run: npx semantic-release

Semantic-release then handles versioning, tagging, and publishing automatically based on your commit history.

Conventional commits: the engine behind automation

Automated release tools only work if your commits follow a predictable format. Conventional Commits standardize this: a commit message structure that tools can parse reliably.

The format is simple:

bash
type(scope): subject

body

footer

Type determines the version bump:

  • feat: adds a feature → minor version bump
  • fix: fixes a bug → patch version bump
  • BREAKING CHANGE: in footer or feat! → major version bump
  • docs:, style:, refactor:, test: → no version bump

Real example:

bash
feat(auth): add OAuth2 provider support

Implements Google and GitHub OAuth2 flows with token refresh.

BREAKING CHANGE: removed legacy API_KEY authentication method

This commit triggers a major version bump and gets tagged as a breaking change in the changelog.

Your team must enforce conventional commits. Use a pre-commit hook or CI check to prevent non-conforming commits from reaching main:

bash
npm install --save-dev commitlint @commitlint/config-conventional husky

npx husky install
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

Now every commit is validated locally before it's pushed. No bad commits reach your release tool.

Generated changelogs: from commits to documentation

Once commits follow a standard, changelogs write themselves. Release-please and semantic-release both generate them automatically, organized by type (Features, Bug Fixes, Breaking Changes).

A generated changelog looks like this:

bash
# 2.1.0 (2024-01-15)

## Features
- **auth**: add OAuth2 provider support (#456)
- **api**: implement request rate limiting (#449)

## Bug Fixes
- **database**: fix connection pool leak in high-load scenarios (#451)
- **cache**: prevent stale data after TTL expiry (#448)

## BREAKING CHANGES
- **auth**: removed legacy API_KEY authentication method

This is generated from commit messages alone. No manual writing. No inconsistency.

Both tools support custom changelog templates if you need branded formatting. Release-please uses Handlebars, semantic-release uses a plugins system. Most teams use the defaults.

The changelog is typically committed back to the repository and published to your package manager and GitHub releases page. Users see a clear, structured history of what changed and why.

Publish packages automatically

Once a release is tagged, publishing to npm, PyPI, Maven, NuGet, or any registry should also be automated. Your CI/CD system handles this after version bumps and changelog generation.

For npm packages, both tools support automatic publishing. Here's a semantic-release config that publishes to npm and creates a GitHub release:

bash
// .releaserc.json
{
  "branches": ["main"],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "@semantic-release/npm",
    "@semantic-release/github"
  ]
}

Each plugin handles a stage of the release:

  • commit-analyzer: reads conventional commits, determines version bump
  • release-notes-generator: creates changelog from commits
  • npm: publishes to npm registry
  • github: creates a GitHub release with changelog attached

For release-please, the same happens automatically once you merge the release PR. It tags the commit, publishes to npm, and creates a GitHub release.

The key requirement: your CI system needs credentials to publish. Store npm tokens (or equivalent) as GitHub Secrets, pass them to your publish step, and never hardcode them:

bash
env:
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

For private registries, authentication is handled the same way. For multi-package monorepos, both tools support independent versioning per package, useful for workspaces.

Common mistakes

  • Not enforcing conventional commits: if developers don't follow the format, automation breaks. Use husky and commitlint to enforce it before commits reach main.
  • Mixing manual and automated releases: once you automate, stick with it. Manual version bumps conflict with automated tooling and cause confusion.
  • Releasing on every commit: minor commits (docs, tests) shouldn't trigger releases. Use the type filter: only feat: and fix: (and breaking changes) bump versions.
  • Forgetting to test before release: automation should only run after your test suite passes. Never skip CI.
  • Publishing without reviewing the changelog: with release-please, review the PR before merge. With semantic-release, at least review the commit history before pushing to main.

Key takeaways

  • Automate releases using release-please (GitHub-native, with review) or semantic-release (instant, trigger-based).
  • Enforce conventional commits across your team using husky and commitlint; this is the foundation for all automation.
  • Changelogs and version numbers are generated from your commit history automatically, eliminating manual work and inconsistency.
  • Configure your CI/CD to publish automatically to package registries once a release is created, with credentials stored securely in GitHub Secrets.
  • Choose between review-gated (release-please PR) or instant (semantic-release) releases based on your team's risk tolerance and deployment frequency.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles