Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

✈️ Release Pilot

The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases.

Powered by Conventional Commits · Zero config · One action does it all


Why Release Pilot?

Most release workflows require chaining 3+ actions together, configuring complex tools, or relying on deprecated projects. Release Pilot replaces all of that with a single, modern action:

FeatureRelease PilotOthers
Conventional Commits parsing✅ Native🔗 External dep
Version calculation✅ Built-in
Changelog generation✅ Built-in🔗 Needs another action
GitHub Release creation✅ Built-in🔗 Needs another action
Job Summary✅ Beautiful
Breaking change detection (! + footer)✅ Both⚠️ Varies
Pre-release support⚠️ Varies
Floating major/minor tags (v2, v2.3)✅ Built-in❌ Manual scripts
Node.js 24 + ESM✅ Modern❌ Legacy
Zero dependencies*

* Only uses official @actions/* packages and semver.

Quick Start

name: Releaseon:
push:
branches: [main]permissions:
contents: writejobs:
release:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v6with:
fetch-depth: 0
- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}

That's it. Zero config. Release Pilot will:

  1. Parse all commits since the last tag using Conventional Commits
  2. Determine the next SemVer version
  3. Create an annotated git tag
  4. Output the version, tag, and changelog

Examples

Tag + GitHub Release

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-title: 'Release {{tag}}'

Draft Release (Review Before Publishing)

Create releases as drafts so you can review, edit, or batch multiple releases before they go public:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-draft: truerelease-title: 'Release {{tag}}'

Drafts are invisible to users until you publish them. Go to Releases → Edit draft → Publish release when you're ready. The Conventional Commits changelog is already written for you.

Tag + Use Version Downstream

- name: ✈️ Release Pilotid: releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: 🐳 Build Docker Imageif: steps.release.outputs.released == 'true'run: | docker build -t myapp:${{ steps.release.outputs.version }} . docker push myapp:${{ steps.release.outputs.version }}

Pre-release from Feature Branches

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}prerelease: trueprerelease-suffix: beta # omit to use the branch name as identifier

When prerelease-suffix is omitted, the branch name is sanitised and used as the identifier. For example, a branch named feature/my-api produces 1.2.0-my-api.1, 1.2.0-my-api.2, etc.

Dry Run (CI Validation)

- name: ✈️ Preview Releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}dry-run: true

Custom Release Rules

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}custom-rules: 'hotfix:patch:🔥 Hotfixes,improvement:minor:💡 Improvements'

Floating Major/Minor Tags (GitHub Action Style)

Automatically maintain v2 → latest v2.x.x and v2.3 → latest v2.3.x tags, just like actions/checkout@v6 does:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}major-tag: true # v2 always points to latest v2.x.xminor-tag: true # v2.3 always points to latest v2.3.x

When you release v2.3.1, Release Pilot creates three tags:

  • v2.3.1 — the exact version (immutable)
  • v2.3 — floats to latest v2.3.x (force-updated)
  • v2 — floats to latest v2.x.x (force-updated)

📥 Inputs

InputDescriptionDefault
tokenGitHub token for authentication${{ github.token }}
prefixTag prefixv
default-bumpBump when no conventional type found. One of patch, minor, major, none, false. Both none and false skip the release when nothing is detected.patch
initial-versionStarting version when no tags exist. Must be valid semver.0.1.0
prereleaseEnable prerelease modefalse
prerelease-suffixPrerelease identifier (e.g., beta)Branch name
branchesComma-separated release branch patterns (regex, anchored to full name). Ignored when prerelease is true.main,master
create-releaseCreate a GitHub Releasefalse
release-draftCreate release as draftfalse
release-titleRelease title template ({{version}}, {{tag}}){{tag}}
annotatedCreate annotated tagstrue
commit-shaOverride the commit SHA. Must be a full 40-character hex SHA.GITHUB_SHA
dry-runCalculate without creating anythingfalse
custom-rulesCustom commit type rules''
include-body-in-changelogInclude commit body in changelogfalse
major-tagCreate/update floating major tag (v2 → latest v2.x.x)false
minor-tagCreate/update floating minor tag (v2.3 → latest v2.3.x)false

📤 Outputs

OutputDescriptionExample
versionNew version without prefix1.2.3
tagNew tag with prefixv1.2.3
previous-versionPrevious version1.2.2
previous-tagPrevious tagv1.2.2
bumpBump type appliedminor
changelogGenerated changelog (markdown)See below
release-urlURL to GitHub Releasehttps://...
releasedWhether a new version tag was created. true regardless of whether a GitHub Release was made — check release-url for that.true

Conventional Commits

Release Pilot follows the Conventional Commits specification:

<type>[optional scope][!]: <description>
[optional body]
[optional footer(s)]

Default Commit Types → Bumps

TypeBumpChangelog Section
featminor🚀 Features
fixpatch🐛 Bug Fixes
perfpatch⚡ Performance
revertpatch⏪ Reverts
docsnone📚 Documentation
stylenone💄 Styling
refactornone♻️ Refactoring
testnone✅ Tests
buildnone📦 Build
cinone🔧 CI/CD
chorenone🧹 Chores

Breaking Changes → major

Breaking changes are detected via:

feat!: remove deprecated API

or:

feat: refactor authentication
BREAKING CHANGE: The auth token format has changed.

How It Works

graph TD
A[Push to release branch] --> B[Fetch existing tags]
B --> C[Get commits since last tag]
C --> D[Parse conventional commits]
D --> E[Determine bump type]
E --> F{Bump detected?}
F -->|Yes| G[Calculate next version]
F -->|No + default=false| H[Skip — no release]
F -->|No + default=patch| G
G --> I[Generate changelog]
I --> J{Dry run?}
J -->|Yes| K[Output results only]
J -->|No| L[Create git tag]
L --> M{Create release?}
M -->|Yes| N[Create GitHub Release]
M -->|No| O[Done]
N --> O
K --> O
O --> P[Write Job Summary ✈️]
Loading

Requirements

  • Checkout with full history: Always use fetch-depth: 0 in your checkout step. A shallow clone will cause the action to fail immediately with a clear error message.
  • Write permissions: The token needs contents: write permission.
  • Node.js 24: This action runs on Node.js 24 (handled automatically by GitHub Actions).

Notes

[skip ci] commits are excluded

Commits containing [skip ci] (case-insensitive; also [skip-ci] and [skip_ci]) are excluded from bump detection and the changelog. Release Pilot's own auto-commits (e.g. build: auto-update dist/ [skip ci]) use this convention to avoid triggering new releases.

Branch patterns are fully anchored regex

Each pattern in branches is wrapped as ^pattern$ before matching. Use release/.* to match all branches starting with release/. Invalid regex patterns fall back to exact string comparison.

Floating tags use force-push

major-tag and minor-tag are force-updated on each release. This is intentional — it is the standard GitHub Actions versioning pattern (actions/checkout@v4 does the same thing).

Changelog

See Releases for the full version history and changelogs.

License

MIT © Jonas Ranerfors

About

✈️ The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases — powered by Conventional Commits.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

✈️ Release Pilot

The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases.

Powered by Conventional Commits · Zero config · One action does it all


Why Release Pilot?

Most release workflows require chaining 3+ actions together, configuring complex tools, or relying on deprecated projects. Release Pilot replaces all of that with a single, modern action:

FeatureRelease PilotOthers
Conventional Commits parsing✅ Native🔗 External dep
Version calculation✅ Built-in
Changelog generation✅ Built-in🔗 Needs another action
GitHub Release creation✅ Built-in🔗 Needs another action
Job Summary✅ Beautiful
Breaking change detection (! + footer)✅ Both⚠️ Varies
Pre-release support⚠️ Varies
Floating major/minor tags (v2, v2.3)✅ Built-in❌ Manual scripts
Node.js 24 + ESM✅ Modern❌ Legacy
Zero dependencies*

* Only uses official @actions/* packages and semver.

Quick Start

name: Releaseon:
push:
branches: [main]permissions:
contents: writejobs:
release:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v6with:
fetch-depth: 0
- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}

That's it. Zero config. Release Pilot will:

  1. Parse all commits since the last tag using Conventional Commits
  2. Determine the next SemVer version
  3. Create an annotated git tag
  4. Output the version, tag, and changelog

Examples

Tag + GitHub Release

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-title: 'Release {{tag}}'

Draft Release (Review Before Publishing)

Create releases as drafts so you can review, edit, or batch multiple releases before they go public:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-draft: truerelease-title: 'Release {{tag}}'

Drafts are invisible to users until you publish them. Go to Releases → Edit draft → Publish release when you're ready. The Conventional Commits changelog is already written for you.

Tag + Use Version Downstream

- name: ✈️ Release Pilotid: releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: 🐳 Build Docker Imageif: steps.release.outputs.released == 'true'run: | docker build -t myapp:${{ steps.release.outputs.version }} . docker push myapp:${{ steps.release.outputs.version }}

Pre-release from Feature Branches

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}prerelease: trueprerelease-suffix: beta # omit to use the branch name as identifier

When prerelease-suffix is omitted, the branch name is sanitised and used as the identifier. For example, a branch named feature/my-api produces 1.2.0-my-api.1, 1.2.0-my-api.2, etc.

Dry Run (CI Validation)

- name: ✈️ Preview Releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}dry-run: true

Custom Release Rules

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}custom-rules: 'hotfix:patch:🔥 Hotfixes,improvement:minor:💡 Improvements'

Floating Major/Minor Tags (GitHub Action Style)

Automatically maintain v2 → latest v2.x.x and v2.3 → latest v2.3.x tags, just like actions/checkout@v6 does:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}major-tag: true # v2 always points to latest v2.x.xminor-tag: true # v2.3 always points to latest v2.3.x

When you release v2.3.1, Release Pilot creates three tags:

  • v2.3.1 — the exact version (immutable)
  • v2.3 — floats to latest v2.3.x (force-updated)
  • v2 — floats to latest v2.x.x (force-updated)

📥 Inputs

InputDescriptionDefault
tokenGitHub token for authentication${{ github.token }}
prefixTag prefixv
default-bumpBump when no conventional type found. One of patch, minor, major, none, false. Both none and false skip the release when nothing is detected.patch
initial-versionStarting version when no tags exist. Must be valid semver.0.1.0
prereleaseEnable prerelease modefalse
prerelease-suffixPrerelease identifier (e.g., beta)Branch name
branchesComma-separated release branch patterns (regex, anchored to full name). Ignored when prerelease is true.main,master
create-releaseCreate a GitHub Releasefalse
release-draftCreate release as draftfalse
release-titleRelease title template ({{version}}, {{tag}}){{tag}}
annotatedCreate annotated tagstrue
commit-shaOverride the commit SHA. Must be a full 40-character hex SHA.GITHUB_SHA
dry-runCalculate without creating anythingfalse
custom-rulesCustom commit type rules''
include-body-in-changelogInclude commit body in changelogfalse
major-tagCreate/update floating major tag (v2 → latest v2.x.x)false
minor-tagCreate/update floating minor tag (v2.3 → latest v2.3.x)false

📤 Outputs

OutputDescriptionExample
versionNew version without prefix1.2.3
tagNew tag with prefixv1.2.3
previous-versionPrevious version1.2.2
previous-tagPrevious tagv1.2.2
bumpBump type appliedminor
changelogGenerated changelog (markdown)See below
release-urlURL to GitHub Releasehttps://...
releasedWhether a new version tag was created. true regardless of whether a GitHub Release was made — check release-url for that.true

Conventional Commits

Release Pilot follows the Conventional Commits specification:

<type>[optional scope][!]: <description>
[optional body]
[optional footer(s)]

Default Commit Types → Bumps

TypeBumpChangelog Section
featminor🚀 Features
fixpatch🐛 Bug Fixes
perfpatch⚡ Performance
revertpatch⏪ Reverts
docsnone📚 Documentation
stylenone💄 Styling
refactornone♻️ Refactoring
testnone✅ Tests
buildnone📦 Build
cinone🔧 CI/CD
chorenone🧹 Chores

Breaking Changes → major

Breaking changes are detected via:

feat!: remove deprecated API

or:

feat: refactor authentication
BREAKING CHANGE: The auth token format has changed.

How It Works

graph TD
A[Push to release branch] --> B[Fetch existing tags]
B --> C[Get commits since last tag]
C --> D[Parse conventional commits]
D --> E[Determine bump type]
E --> F{Bump detected?}
F -->|Yes| G[Calculate next version]
F -->|No + default=false| H[Skip — no release]
F -->|No + default=patch| G
G --> I[Generate changelog]
I --> J{Dry run?}
J -->|Yes| K[Output results only]
J -->|No| L[Create git tag]
L --> M{Create release?}
M -->|Yes| N[Create GitHub Release]
M -->|No| O[Done]
N --> O
K --> O
O --> P[Write Job Summary ✈️]
Loading

Requirements

  • Checkout with full history: Always use fetch-depth: 0 in your checkout step. A shallow clone will cause the action to fail immediately with a clear error message.
  • Write permissions: The token needs contents: write permission.
  • Node.js 24: This action runs on Node.js 24 (handled automatically by GitHub Actions).

Notes

[skip ci] commits are excluded

Commits containing [skip ci] (case-insensitive; also [skip-ci] and [skip_ci]) are excluded from bump detection and the changelog. Release Pilot's own auto-commits (e.g. build: auto-update dist/ [skip ci]) use this convention to avoid triggering new releases.

Branch patterns are fully anchored regex

Each pattern in branches is wrapped as ^pattern$ before matching. Use release/.* to match all branches starting with release/. Invalid regex patterns fall back to exact string comparison.

Floating tags use force-push

major-tag and minor-tag are force-updated on each release. This is intentional — it is the standard GitHub Actions versioning pattern (actions/checkout@v4 does the same thing).

Changelog

See Releases for the full version history and changelogs.

License

MIT © Jonas Ranerfors

About

✈️ The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases — powered by Conventional Commits.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

✈️ Release Pilot

The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases.

Powered by Conventional Commits · Zero config · One action does it all


Why Release Pilot?

Most release workflows require chaining 3+ actions together, configuring complex tools, or relying on deprecated projects. Release Pilot replaces all of that with a single, modern action:

FeatureRelease PilotOthers
Conventional Commits parsing✅ Native🔗 External dep
Version calculation✅ Built-in
Changelog generation✅ Built-in🔗 Needs another action
GitHub Release creation✅ Built-in🔗 Needs another action
Job Summary✅ Beautiful
Breaking change detection (! + footer)✅ Both⚠️ Varies
Pre-release support⚠️ Varies
Floating major/minor tags (v2, v2.3)✅ Built-in❌ Manual scripts
Node.js 24 + ESM✅ Modern❌ Legacy
Zero dependencies*

* Only uses official @actions/* packages and semver.

Quick Start

name: Releaseon:
push:
branches: [main]permissions:
contents: writejobs:
release:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v6with:
fetch-depth: 0
- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}

That's it. Zero config. Release Pilot will:

  1. Parse all commits since the last tag using Conventional Commits
  2. Determine the next SemVer version
  3. Create an annotated git tag
  4. Output the version, tag, and changelog

Examples

Tag + GitHub Release

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-title: 'Release {{tag}}'

Draft Release (Review Before Publishing)

Create releases as drafts so you can review, edit, or batch multiple releases before they go public:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-draft: truerelease-title: 'Release {{tag}}'

Drafts are invisible to users until you publish them. Go to Releases → Edit draft → Publish release when you're ready. The Conventional Commits changelog is already written for you.

Tag + Use Version Downstream

- name: ✈️ Release Pilotid: releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: 🐳 Build Docker Imageif: steps.release.outputs.released == 'true'run: | docker build -t myapp:${{ steps.release.outputs.version }} . docker push myapp:${{ steps.release.outputs.version }}

Pre-release from Feature Branches

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}prerelease: trueprerelease-suffix: beta # omit to use the branch name as identifier

When prerelease-suffix is omitted, the branch name is sanitised and used as the identifier. For example, a branch named feature/my-api produces 1.2.0-my-api.1, 1.2.0-my-api.2, etc.

Dry Run (CI Validation)

- name: ✈️ Preview Releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}dry-run: true

Custom Release Rules

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}custom-rules: 'hotfix:patch:🔥 Hotfixes,improvement:minor:💡 Improvements'

Floating Major/Minor Tags (GitHub Action Style)

Automatically maintain v2 → latest v2.x.x and v2.3 → latest v2.3.x tags, just like actions/checkout@v6 does:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}major-tag: true # v2 always points to latest v2.x.xminor-tag: true # v2.3 always points to latest v2.3.x

When you release v2.3.1, Release Pilot creates three tags:

  • v2.3.1 — the exact version (immutable)
  • v2.3 — floats to latest v2.3.x (force-updated)
  • v2 — floats to latest v2.x.x (force-updated)

📥 Inputs

InputDescriptionDefault
tokenGitHub token for authentication${{ github.token }}
prefixTag prefixv
default-bumpBump when no conventional type found. One of patch, minor, major, none, false. Both none and false skip the release when nothing is detected.patch
initial-versionStarting version when no tags exist. Must be valid semver.0.1.0
prereleaseEnable prerelease modefalse
prerelease-suffixPrerelease identifier (e.g., beta)Branch name
branchesComma-separated release branch patterns (regex, anchored to full name). Ignored when prerelease is true.main,master
create-releaseCreate a GitHub Releasefalse
release-draftCreate release as draftfalse
release-titleRelease title template ({{version}}, {{tag}}){{tag}}
annotatedCreate annotated tagstrue
commit-shaOverride the commit SHA. Must be a full 40-character hex SHA.GITHUB_SHA
dry-runCalculate without creating anythingfalse
custom-rulesCustom commit type rules''
include-body-in-changelogInclude commit body in changelogfalse
major-tagCreate/update floating major tag (v2 → latest v2.x.x)false
minor-tagCreate/update floating minor tag (v2.3 → latest v2.3.x)false

📤 Outputs

OutputDescriptionExample
versionNew version without prefix1.2.3
tagNew tag with prefixv1.2.3
previous-versionPrevious version1.2.2
previous-tagPrevious tagv1.2.2
bumpBump type appliedminor
changelogGenerated changelog (markdown)See below
release-urlURL to GitHub Releasehttps://...
releasedWhether a new version tag was created. true regardless of whether a GitHub Release was made — check release-url for that.true

Conventional Commits

Release Pilot follows the Conventional Commits specification:

<type>[optional scope][!]: <description>
[optional body]
[optional footer(s)]

Default Commit Types → Bumps

TypeBumpChangelog Section
featminor🚀 Features
fixpatch🐛 Bug Fixes
perfpatch⚡ Performance
revertpatch⏪ Reverts
docsnone📚 Documentation
stylenone💄 Styling
refactornone♻️ Refactoring
testnone✅ Tests
buildnone📦 Build
cinone🔧 CI/CD
chorenone🧹 Chores

Breaking Changes → major

Breaking changes are detected via:

feat!: remove deprecated API

or:

feat: refactor authentication
BREAKING CHANGE: The auth token format has changed.

How It Works

graph TD
A[Push to release branch] --> B[Fetch existing tags]
B --> C[Get commits since last tag]
C --> D[Parse conventional commits]
D --> E[Determine bump type]
E --> F{Bump detected?}
F -->|Yes| G[Calculate next version]
F -->|No + default=false| H[Skip — no release]
F -->|No + default=patch| G
G --> I[Generate changelog]
I --> J{Dry run?}
J -->|Yes| K[Output results only]
J -->|No| L[Create git tag]
L --> M{Create release?}
M -->|Yes| N[Create GitHub Release]
M -->|No| O[Done]
N --> O
K --> O
O --> P[Write Job Summary ✈️]
Loading

Requirements

  • Checkout with full history: Always use fetch-depth: 0 in your checkout step. A shallow clone will cause the action to fail immediately with a clear error message.
  • Write permissions: The token needs contents: write permission.
  • Node.js 24: This action runs on Node.js 24 (handled automatically by GitHub Actions).

Notes

[skip ci] commits are excluded

Commits containing [skip ci] (case-insensitive; also [skip-ci] and [skip_ci]) are excluded from bump detection and the changelog. Release Pilot's own auto-commits (e.g. build: auto-update dist/ [skip ci]) use this convention to avoid triggering new releases.

Branch patterns are fully anchored regex

Each pattern in branches is wrapped as ^pattern$ before matching. Use release/.* to match all branches starting with release/. Invalid regex patterns fall back to exact string comparison.

Floating tags use force-push

major-tag and minor-tag are force-updated on each release. This is intentional — it is the standard GitHub Actions versioning pattern (actions/checkout@v4 does the same thing).

Changelog

See Releases for the full version history and changelogs.

License

MIT © Jonas Ranerfors

About

✈️ The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases — powered by Conventional Commits.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

✈️ Release Pilot

The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases.

Powered by Conventional Commits · Zero config · One action does it all


Why Release Pilot?

Most release workflows require chaining 3+ actions together, configuring complex tools, or relying on deprecated projects. Release Pilot replaces all of that with a single, modern action:

FeatureRelease PilotOthers
Conventional Commits parsing✅ Native🔗 External dep
Version calculation✅ Built-in
Changelog generation✅ Built-in🔗 Needs another action
GitHub Release creation✅ Built-in🔗 Needs another action
Job Summary✅ Beautiful
Breaking change detection (! + footer)✅ Both⚠️ Varies
Pre-release support⚠️ Varies
Floating major/minor tags (v2, v2.3)✅ Built-in❌ Manual scripts
Node.js 24 + ESM✅ Modern❌ Legacy
Zero dependencies*

* Only uses official @actions/* packages and semver.

Quick Start

name: Releaseon:
push:
branches: [main]permissions:
contents: writejobs:
release:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v6with:
fetch-depth: 0
- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}

That's it. Zero config. Release Pilot will:

  1. Parse all commits since the last tag using Conventional Commits
  2. Determine the next SemVer version
  3. Create an annotated git tag
  4. Output the version, tag, and changelog

Examples

Tag + GitHub Release

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-title: 'Release {{tag}}'

Draft Release (Review Before Publishing)

Create releases as drafts so you can review, edit, or batch multiple releases before they go public:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-draft: truerelease-title: 'Release {{tag}}'

Drafts are invisible to users until you publish them. Go to Releases → Edit draft → Publish release when you're ready. The Conventional Commits changelog is already written for you.

Tag + Use Version Downstream

- name: ✈️ Release Pilotid: releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: 🐳 Build Docker Imageif: steps.release.outputs.released == 'true'run: | docker build -t myapp:${{ steps.release.outputs.version }} . docker push myapp:${{ steps.release.outputs.version }}

Pre-release from Feature Branches

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}prerelease: trueprerelease-suffix: beta # omit to use the branch name as identifier

When prerelease-suffix is omitted, the branch name is sanitised and used as the identifier. For example, a branch named feature/my-api produces 1.2.0-my-api.1, 1.2.0-my-api.2, etc.

Dry Run (CI Validation)

- name: ✈️ Preview Releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}dry-run: true

Custom Release Rules

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}custom-rules: 'hotfix:patch:🔥 Hotfixes,improvement:minor:💡 Improvements'

Floating Major/Minor Tags (GitHub Action Style)

Automatically maintain v2 → latest v2.x.x and v2.3 → latest v2.3.x tags, just like actions/checkout@v6 does:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}major-tag: true # v2 always points to latest v2.x.xminor-tag: true # v2.3 always points to latest v2.3.x

When you release v2.3.1, Release Pilot creates three tags:

  • v2.3.1 — the exact version (immutable)
  • v2.3 — floats to latest v2.3.x (force-updated)
  • v2 — floats to latest v2.x.x (force-updated)

📥 Inputs

InputDescriptionDefault
tokenGitHub token for authentication${{ github.token }}
prefixTag prefixv
default-bumpBump when no conventional type found. One of patch, minor, major, none, false. Both none and false skip the release when nothing is detected.patch
initial-versionStarting version when no tags exist. Must be valid semver.0.1.0
prereleaseEnable prerelease modefalse
prerelease-suffixPrerelease identifier (e.g., beta)Branch name
branchesComma-separated release branch patterns (regex, anchored to full name). Ignored when prerelease is true.main,master
create-releaseCreate a GitHub Releasefalse
release-draftCreate release as draftfalse
release-titleRelease title template ({{version}}, {{tag}}){{tag}}
annotatedCreate annotated tagstrue
commit-shaOverride the commit SHA. Must be a full 40-character hex SHA.GITHUB_SHA
dry-runCalculate without creating anythingfalse
custom-rulesCustom commit type rules''
include-body-in-changelogInclude commit body in changelogfalse
major-tagCreate/update floating major tag (v2 → latest v2.x.x)false
minor-tagCreate/update floating minor tag (v2.3 → latest v2.3.x)false

📤 Outputs

OutputDescriptionExample
versionNew version without prefix1.2.3
tagNew tag with prefixv1.2.3
previous-versionPrevious version1.2.2
previous-tagPrevious tagv1.2.2
bumpBump type appliedminor
changelogGenerated changelog (markdown)See below
release-urlURL to GitHub Releasehttps://...
releasedWhether a new version tag was created. true regardless of whether a GitHub Release was made — check release-url for that.true

Conventional Commits

Release Pilot follows the Conventional Commits specification:

<type>[optional scope][!]: <description>
[optional body]
[optional footer(s)]

Default Commit Types → Bumps

TypeBumpChangelog Section
featminor🚀 Features
fixpatch🐛 Bug Fixes
perfpatch⚡ Performance
revertpatch⏪ Reverts
docsnone📚 Documentation
stylenone💄 Styling
refactornone♻️ Refactoring
testnone✅ Tests
buildnone📦 Build
cinone🔧 CI/CD
chorenone🧹 Chores

Breaking Changes → major

Breaking changes are detected via:

feat!: remove deprecated API

or:

feat: refactor authentication
BREAKING CHANGE: The auth token format has changed.

How It Works

graph TD
A[Push to release branch] --> B[Fetch existing tags]
B --> C[Get commits since last tag]
C --> D[Parse conventional commits]
D --> E[Determine bump type]
E --> F{Bump detected?}
F -->|Yes| G[Calculate next version]
F -->|No + default=false| H[Skip — no release]
F -->|No + default=patch| G
G --> I[Generate changelog]
I --> J{Dry run?}
J -->|Yes| K[Output results only]
J -->|No| L[Create git tag]
L --> M{Create release?}
M -->|Yes| N[Create GitHub Release]
M -->|No| O[Done]
N --> O
K --> O
O --> P[Write Job Summary ✈️]
Loading

Requirements

  • Checkout with full history: Always use fetch-depth: 0 in your checkout step. A shallow clone will cause the action to fail immediately with a clear error message.
  • Write permissions: The token needs contents: write permission.
  • Node.js 24: This action runs on Node.js 24 (handled automatically by GitHub Actions).

Notes

[skip ci] commits are excluded

Commits containing [skip ci] (case-insensitive; also [skip-ci] and [skip_ci]) are excluded from bump detection and the changelog. Release Pilot's own auto-commits (e.g. build: auto-update dist/ [skip ci]) use this convention to avoid triggering new releases.

Branch patterns are fully anchored regex

Each pattern in branches is wrapped as ^pattern$ before matching. Use release/.* to match all branches starting with release/. Invalid regex patterns fall back to exact string comparison.

Floating tags use force-push

major-tag and minor-tag are force-updated on each release. This is intentional — it is the standard GitHub Actions versioning pattern (actions/checkout@v4 does the same thing).

Changelog

See Releases for the full version history and changelogs.

License

MIT © Jonas Ranerfors

About

✈️ The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases — powered by Conventional Commits.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

✈️ Release Pilot

The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases.

Powered by Conventional Commits · Zero config · One action does it all


Why Release Pilot?

Most release workflows require chaining 3+ actions together, configuring complex tools, or relying on deprecated projects. Release Pilot replaces all of that with a single, modern action:

FeatureRelease PilotOthers
Conventional Commits parsing✅ Native🔗 External dep
Version calculation✅ Built-in
Changelog generation✅ Built-in🔗 Needs another action
GitHub Release creation✅ Built-in🔗 Needs another action
Job Summary✅ Beautiful
Breaking change detection (! + footer)✅ Both⚠️ Varies
Pre-release support⚠️ Varies
Floating major/minor tags (v2, v2.3)✅ Built-in❌ Manual scripts
Node.js 24 + ESM✅ Modern❌ Legacy
Zero dependencies*

* Only uses official @actions/* packages and semver.

Quick Start

name: Releaseon:
push:
branches: [main]permissions:
contents: writejobs:
release:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v6with:
fetch-depth: 0
- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}

That's it. Zero config. Release Pilot will:

  1. Parse all commits since the last tag using Conventional Commits
  2. Determine the next SemVer version
  3. Create an annotated git tag
  4. Output the version, tag, and changelog

Examples

Tag + GitHub Release

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-title: 'Release {{tag}}'

Draft Release (Review Before Publishing)

Create releases as drafts so you can review, edit, or batch multiple releases before they go public:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-draft: truerelease-title: 'Release {{tag}}'

Drafts are invisible to users until you publish them. Go to Releases → Edit draft → Publish release when you're ready. The Conventional Commits changelog is already written for you.

Tag + Use Version Downstream

- name: ✈️ Release Pilotid: releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: 🐳 Build Docker Imageif: steps.release.outputs.released == 'true'run: | docker build -t myapp:${{ steps.release.outputs.version }} . docker push myapp:${{ steps.release.outputs.version }}

Pre-release from Feature Branches

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}prerelease: trueprerelease-suffix: beta # omit to use the branch name as identifier

When prerelease-suffix is omitted, the branch name is sanitised and used as the identifier. For example, a branch named feature/my-api produces 1.2.0-my-api.1, 1.2.0-my-api.2, etc.

Dry Run (CI Validation)

- name: ✈️ Preview Releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}dry-run: true

Custom Release Rules

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}custom-rules: 'hotfix:patch:🔥 Hotfixes,improvement:minor:💡 Improvements'

Floating Major/Minor Tags (GitHub Action Style)

Automatically maintain v2 → latest v2.x.x and v2.3 → latest v2.3.x tags, just like actions/checkout@v6 does:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}major-tag: true # v2 always points to latest v2.x.xminor-tag: true # v2.3 always points to latest v2.3.x

When you release v2.3.1, Release Pilot creates three tags:

  • v2.3.1 — the exact version (immutable)
  • v2.3 — floats to latest v2.3.x (force-updated)
  • v2 — floats to latest v2.x.x (force-updated)

📥 Inputs

InputDescriptionDefault
tokenGitHub token for authentication${{ github.token }}
prefixTag prefixv
default-bumpBump when no conventional type found. One of patch, minor, major, none, false. Both none and false skip the release when nothing is detected.patch
initial-versionStarting version when no tags exist. Must be valid semver.0.1.0
prereleaseEnable prerelease modefalse
prerelease-suffixPrerelease identifier (e.g., beta)Branch name
branchesComma-separated release branch patterns (regex, anchored to full name). Ignored when prerelease is true.main,master
create-releaseCreate a GitHub Releasefalse
release-draftCreate release as draftfalse
release-titleRelease title template ({{version}}, {{tag}}){{tag}}
annotatedCreate annotated tagstrue
commit-shaOverride the commit SHA. Must be a full 40-character hex SHA.GITHUB_SHA
dry-runCalculate without creating anythingfalse
custom-rulesCustom commit type rules''
include-body-in-changelogInclude commit body in changelogfalse
major-tagCreate/update floating major tag (v2 → latest v2.x.x)false
minor-tagCreate/update floating minor tag (v2.3 → latest v2.3.x)false

📤 Outputs

OutputDescriptionExample
versionNew version without prefix1.2.3
tagNew tag with prefixv1.2.3
previous-versionPrevious version1.2.2
previous-tagPrevious tagv1.2.2
bumpBump type appliedminor
changelogGenerated changelog (markdown)See below
release-urlURL to GitHub Releasehttps://...
releasedWhether a new version tag was created. true regardless of whether a GitHub Release was made — check release-url for that.true

Conventional Commits

Release Pilot follows the Conventional Commits specification:

<type>[optional scope][!]: <description>
[optional body]
[optional footer(s)]

Default Commit Types → Bumps

TypeBumpChangelog Section
featminor🚀 Features
fixpatch🐛 Bug Fixes
perfpatch⚡ Performance
revertpatch⏪ Reverts
docsnone📚 Documentation
stylenone💄 Styling
refactornone♻️ Refactoring
testnone✅ Tests
buildnone📦 Build
cinone🔧 CI/CD
chorenone🧹 Chores

Breaking Changes → major

Breaking changes are detected via:

feat!: remove deprecated API

or:

feat: refactor authentication
BREAKING CHANGE: The auth token format has changed.

How It Works

graph TD
A[Push to release branch] --> B[Fetch existing tags]
B --> C[Get commits since last tag]
C --> D[Parse conventional commits]
D --> E[Determine bump type]
E --> F{Bump detected?}
F -->|Yes| G[Calculate next version]
F -->|No + default=false| H[Skip — no release]
F -->|No + default=patch| G
G --> I[Generate changelog]
I --> J{Dry run?}
J -->|Yes| K[Output results only]
J -->|No| L[Create git tag]
L --> M{Create release?}
M -->|Yes| N[Create GitHub Release]
M -->|No| O[Done]
N --> O
K --> O
O --> P[Write Job Summary ✈️]
Loading

Requirements

  • Checkout with full history: Always use fetch-depth: 0 in your checkout step. A shallow clone will cause the action to fail immediately with a clear error message.
  • Write permissions: The token needs contents: write permission.
  • Node.js 24: This action runs on Node.js 24 (handled automatically by GitHub Actions).

Notes

[skip ci] commits are excluded

Commits containing [skip ci] (case-insensitive; also [skip-ci] and [skip_ci]) are excluded from bump detection and the changelog. Release Pilot's own auto-commits (e.g. build: auto-update dist/ [skip ci]) use this convention to avoid triggering new releases.

Branch patterns are fully anchored regex

Each pattern in branches is wrapped as ^pattern$ before matching. Use release/.* to match all branches starting with release/. Invalid regex patterns fall back to exact string comparison.

Floating tags use force-push

major-tag and minor-tag are force-updated on each release. This is intentional — it is the standard GitHub Actions versioning pattern (actions/checkout@v4 does the same thing).

Changelog

See Releases for the full version history and changelogs.

License

MIT © Jonas Ranerfors

About

✈️ The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases — powered by Conventional Commits.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

✈️ Release Pilot

The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases.

Powered by Conventional Commits · Zero config · One action does it all


Why Release Pilot?

Most release workflows require chaining 3+ actions together, configuring complex tools, or relying on deprecated projects. Release Pilot replaces all of that with a single, modern action:

FeatureRelease PilotOthers
Conventional Commits parsing✅ Native🔗 External dep
Version calculation✅ Built-in
Changelog generation✅ Built-in🔗 Needs another action
GitHub Release creation✅ Built-in🔗 Needs another action
Job Summary✅ Beautiful
Breaking change detection (! + footer)✅ Both⚠️ Varies
Pre-release support⚠️ Varies
Floating major/minor tags (v2, v2.3)✅ Built-in❌ Manual scripts
Node.js 24 + ESM✅ Modern❌ Legacy
Zero dependencies*

* Only uses official @actions/* packages and semver.

Quick Start

name: Releaseon:
push:
branches: [main]permissions:
contents: writejobs:
release:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v6with:
fetch-depth: 0
- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}

That's it. Zero config. Release Pilot will:

  1. Parse all commits since the last tag using Conventional Commits
  2. Determine the next SemVer version
  3. Create an annotated git tag
  4. Output the version, tag, and changelog

Examples

Tag + GitHub Release

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-title: 'Release {{tag}}'

Draft Release (Review Before Publishing)

Create releases as drafts so you can review, edit, or batch multiple releases before they go public:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-draft: truerelease-title: 'Release {{tag}}'

Drafts are invisible to users until you publish them. Go to Releases → Edit draft → Publish release when you're ready. The Conventional Commits changelog is already written for you.

Tag + Use Version Downstream

- name: ✈️ Release Pilotid: releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: 🐳 Build Docker Imageif: steps.release.outputs.released == 'true'run: | docker build -t myapp:${{ steps.release.outputs.version }} . docker push myapp:${{ steps.release.outputs.version }}

Pre-release from Feature Branches

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}prerelease: trueprerelease-suffix: beta # omit to use the branch name as identifier

When prerelease-suffix is omitted, the branch name is sanitised and used as the identifier. For example, a branch named feature/my-api produces 1.2.0-my-api.1, 1.2.0-my-api.2, etc.

Dry Run (CI Validation)

- name: ✈️ Preview Releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}dry-run: true

Custom Release Rules

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}custom-rules: 'hotfix:patch:🔥 Hotfixes,improvement:minor:💡 Improvements'

Floating Major/Minor Tags (GitHub Action Style)

Automatically maintain v2 → latest v2.x.x and v2.3 → latest v2.3.x tags, just like actions/checkout@v6 does:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}major-tag: true # v2 always points to latest v2.x.xminor-tag: true # v2.3 always points to latest v2.3.x

When you release v2.3.1, Release Pilot creates three tags:

  • v2.3.1 — the exact version (immutable)
  • v2.3 — floats to latest v2.3.x (force-updated)
  • v2 — floats to latest v2.x.x (force-updated)

📥 Inputs

InputDescriptionDefault
tokenGitHub token for authentication${{ github.token }}
prefixTag prefixv
default-bumpBump when no conventional type found. One of patch, minor, major, none, false. Both none and false skip the release when nothing is detected.patch
initial-versionStarting version when no tags exist. Must be valid semver.0.1.0
prereleaseEnable prerelease modefalse
prerelease-suffixPrerelease identifier (e.g., beta)Branch name
branchesComma-separated release branch patterns (regex, anchored to full name). Ignored when prerelease is true.main,master
create-releaseCreate a GitHub Releasefalse
release-draftCreate release as draftfalse
release-titleRelease title template ({{version}}, {{tag}}){{tag}}
annotatedCreate annotated tagstrue
commit-shaOverride the commit SHA. Must be a full 40-character hex SHA.GITHUB_SHA
dry-runCalculate without creating anythingfalse
custom-rulesCustom commit type rules''
include-body-in-changelogInclude commit body in changelogfalse
major-tagCreate/update floating major tag (v2 → latest v2.x.x)false
minor-tagCreate/update floating minor tag (v2.3 → latest v2.3.x)false

📤 Outputs

OutputDescriptionExample
versionNew version without prefix1.2.3
tagNew tag with prefixv1.2.3
previous-versionPrevious version1.2.2
previous-tagPrevious tagv1.2.2
bumpBump type appliedminor
changelogGenerated changelog (markdown)See below
release-urlURL to GitHub Releasehttps://...
releasedWhether a new version tag was created. true regardless of whether a GitHub Release was made — check release-url for that.true

Conventional Commits

Release Pilot follows the Conventional Commits specification:

<type>[optional scope][!]: <description>
[optional body]
[optional footer(s)]

Default Commit Types → Bumps

TypeBumpChangelog Section
featminor🚀 Features
fixpatch🐛 Bug Fixes
perfpatch⚡ Performance
revertpatch⏪ Reverts
docsnone📚 Documentation
stylenone💄 Styling
refactornone♻️ Refactoring
testnone✅ Tests
buildnone📦 Build
cinone🔧 CI/CD
chorenone🧹 Chores

Breaking Changes → major

Breaking changes are detected via:

feat!: remove deprecated API

or:

feat: refactor authentication
BREAKING CHANGE: The auth token format has changed.

How It Works

graph TD
A[Push to release branch] --> B[Fetch existing tags]
B --> C[Get commits since last tag]
C --> D[Parse conventional commits]
D --> E[Determine bump type]
E --> F{Bump detected?}
F -->|Yes| G[Calculate next version]
F -->|No + default=false| H[Skip — no release]
F -->|No + default=patch| G
G --> I[Generate changelog]
I --> J{Dry run?}
J -->|Yes| K[Output results only]
J -->|No| L[Create git tag]
L --> M{Create release?}
M -->|Yes| N[Create GitHub Release]
M -->|No| O[Done]
N --> O
K --> O
O --> P[Write Job Summary ✈️]
Loading

Requirements

  • Checkout with full history: Always use fetch-depth: 0 in your checkout step. A shallow clone will cause the action to fail immediately with a clear error message.
  • Write permissions: The token needs contents: write permission.
  • Node.js 24: This action runs on Node.js 24 (handled automatically by GitHub Actions).

Notes

[skip ci] commits are excluded

Commits containing [skip ci] (case-insensitive; also [skip-ci] and [skip_ci]) are excluded from bump detection and the changelog. Release Pilot's own auto-commits (e.g. build: auto-update dist/ [skip ci]) use this convention to avoid triggering new releases.

Branch patterns are fully anchored regex

Each pattern in branches is wrapped as ^pattern$ before matching. Use release/.* to match all branches starting with release/. Invalid regex patterns fall back to exact string comparison.

Floating tags use force-push

major-tag and minor-tag are force-updated on each release. This is intentional — it is the standard GitHub Actions versioning pattern (actions/checkout@v4 does the same thing).

Changelog

See Releases for the full version history and changelogs.

License

MIT © Jonas Ranerfors

About

✈️ The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases — powered by Conventional Commits.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

✈️ Release Pilot

The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases.

Powered by Conventional Commits · Zero config · One action does it all


Why Release Pilot?

Most release workflows require chaining 3+ actions together, configuring complex tools, or relying on deprecated projects. Release Pilot replaces all of that with a single, modern action:

FeatureRelease PilotOthers
Conventional Commits parsing✅ Native🔗 External dep
Version calculation✅ Built-in
Changelog generation✅ Built-in🔗 Needs another action
GitHub Release creation✅ Built-in🔗 Needs another action
Job Summary✅ Beautiful
Breaking change detection (! + footer)✅ Both⚠️ Varies
Pre-release support⚠️ Varies
Floating major/minor tags (v2, v2.3)✅ Built-in❌ Manual scripts
Node.js 24 + ESM✅ Modern❌ Legacy
Zero dependencies*

* Only uses official @actions/* packages and semver.

Quick Start

name: Releaseon:
push:
branches: [main]permissions:
contents: writejobs:
release:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v6with:
fetch-depth: 0
- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}

That's it. Zero config. Release Pilot will:

  1. Parse all commits since the last tag using Conventional Commits
  2. Determine the next SemVer version
  3. Create an annotated git tag
  4. Output the version, tag, and changelog

Examples

Tag + GitHub Release

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-title: 'Release {{tag}}'

Draft Release (Review Before Publishing)

Create releases as drafts so you can review, edit, or batch multiple releases before they go public:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-draft: truerelease-title: 'Release {{tag}}'

Drafts are invisible to users until you publish them. Go to Releases → Edit draft → Publish release when you're ready. The Conventional Commits changelog is already written for you.

Tag + Use Version Downstream

- name: ✈️ Release Pilotid: releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: 🐳 Build Docker Imageif: steps.release.outputs.released == 'true'run: | docker build -t myapp:${{ steps.release.outputs.version }} . docker push myapp:${{ steps.release.outputs.version }}

Pre-release from Feature Branches

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}prerelease: trueprerelease-suffix: beta # omit to use the branch name as identifier

When prerelease-suffix is omitted, the branch name is sanitised and used as the identifier. For example, a branch named feature/my-api produces 1.2.0-my-api.1, 1.2.0-my-api.2, etc.

Dry Run (CI Validation)

- name: ✈️ Preview Releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}dry-run: true

Custom Release Rules

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}custom-rules: 'hotfix:patch:🔥 Hotfixes,improvement:minor:💡 Improvements'

Floating Major/Minor Tags (GitHub Action Style)

Automatically maintain v2 → latest v2.x.x and v2.3 → latest v2.3.x tags, just like actions/checkout@v6 does:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}major-tag: true # v2 always points to latest v2.x.xminor-tag: true # v2.3 always points to latest v2.3.x

When you release v2.3.1, Release Pilot creates three tags:

  • v2.3.1 — the exact version (immutable)
  • v2.3 — floats to latest v2.3.x (force-updated)
  • v2 — floats to latest v2.x.x (force-updated)

📥 Inputs

InputDescriptionDefault
tokenGitHub token for authentication${{ github.token }}
prefixTag prefixv
default-bumpBump when no conventional type found. One of patch, minor, major, none, false. Both none and false skip the release when nothing is detected.patch
initial-versionStarting version when no tags exist. Must be valid semver.0.1.0
prereleaseEnable prerelease modefalse
prerelease-suffixPrerelease identifier (e.g., beta)Branch name
branchesComma-separated release branch patterns (regex, anchored to full name). Ignored when prerelease is true.main,master
create-releaseCreate a GitHub Releasefalse
release-draftCreate release as draftfalse
release-titleRelease title template ({{version}}, {{tag}}){{tag}}
annotatedCreate annotated tagstrue
commit-shaOverride the commit SHA. Must be a full 40-character hex SHA.GITHUB_SHA
dry-runCalculate without creating anythingfalse
custom-rulesCustom commit type rules''
include-body-in-changelogInclude commit body in changelogfalse
major-tagCreate/update floating major tag (v2 → latest v2.x.x)false
minor-tagCreate/update floating minor tag (v2.3 → latest v2.3.x)false

📤 Outputs

OutputDescriptionExample
versionNew version without prefix1.2.3
tagNew tag with prefixv1.2.3
previous-versionPrevious version1.2.2
previous-tagPrevious tagv1.2.2
bumpBump type appliedminor
changelogGenerated changelog (markdown)See below
release-urlURL to GitHub Releasehttps://...
releasedWhether a new version tag was created. true regardless of whether a GitHub Release was made — check release-url for that.true

Conventional Commits

Release Pilot follows the Conventional Commits specification:

<type>[optional scope][!]: <description>
[optional body]
[optional footer(s)]

Default Commit Types → Bumps

TypeBumpChangelog Section
featminor🚀 Features
fixpatch🐛 Bug Fixes
perfpatch⚡ Performance
revertpatch⏪ Reverts
docsnone📚 Documentation
stylenone💄 Styling
refactornone♻️ Refactoring
testnone✅ Tests
buildnone📦 Build
cinone🔧 CI/CD
chorenone🧹 Chores

Breaking Changes → major

Breaking changes are detected via:

feat!: remove deprecated API

or:

feat: refactor authentication
BREAKING CHANGE: The auth token format has changed.

How It Works

graph TD
A[Push to release branch] --> B[Fetch existing tags]
B --> C[Get commits since last tag]
C --> D[Parse conventional commits]
D --> E[Determine bump type]
E --> F{Bump detected?}
F -->|Yes| G[Calculate next version]
F -->|No + default=false| H[Skip — no release]
F -->|No + default=patch| G
G --> I[Generate changelog]
I --> J{Dry run?}
J -->|Yes| K[Output results only]
J -->|No| L[Create git tag]
L --> M{Create release?}
M -->|Yes| N[Create GitHub Release]
M -->|No| O[Done]
N --> O
K --> O
O --> P[Write Job Summary ✈️]
Loading

Requirements

  • Checkout with full history: Always use fetch-depth: 0 in your checkout step. A shallow clone will cause the action to fail immediately with a clear error message.
  • Write permissions: The token needs contents: write permission.
  • Node.js 24: This action runs on Node.js 24 (handled automatically by GitHub Actions).

Notes

[skip ci] commits are excluded

Commits containing [skip ci] (case-insensitive; also [skip-ci] and [skip_ci]) are excluded from bump detection and the changelog. Release Pilot's own auto-commits (e.g. build: auto-update dist/ [skip ci]) use this convention to avoid triggering new releases.

Branch patterns are fully anchored regex

Each pattern in branches is wrapped as ^pattern$ before matching. Use release/.* to match all branches starting with release/. Invalid regex patterns fall back to exact string comparison.

Floating tags use force-push

major-tag and minor-tag are force-updated on each release. This is intentional — it is the standard GitHub Actions versioning pattern (actions/checkout@v4 does the same thing).

Changelog

See Releases for the full version history and changelogs.

License

MIT © Jonas Ranerfors

About

✈️ The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases — powered by Conventional Commits.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

✈️ Release Pilot

The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases.

Powered by Conventional Commits · Zero config · One action does it all


Why Release Pilot?

Most release workflows require chaining 3+ actions together, configuring complex tools, or relying on deprecated projects. Release Pilot replaces all of that with a single, modern action:

FeatureRelease PilotOthers
Conventional Commits parsing✅ Native🔗 External dep
Version calculation✅ Built-in
Changelog generation✅ Built-in🔗 Needs another action
GitHub Release creation✅ Built-in🔗 Needs another action
Job Summary✅ Beautiful
Breaking change detection (! + footer)✅ Both⚠️ Varies
Pre-release support⚠️ Varies
Floating major/minor tags (v2, v2.3)✅ Built-in❌ Manual scripts
Node.js 24 + ESM✅ Modern❌ Legacy
Zero dependencies*

* Only uses official @actions/* packages and semver.

Quick Start

name: Releaseon:
push:
branches: [main]permissions:
contents: writejobs:
release:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v6with:
fetch-depth: 0
- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}

That's it. Zero config. Release Pilot will:

  1. Parse all commits since the last tag using Conventional Commits
  2. Determine the next SemVer version
  3. Create an annotated git tag
  4. Output the version, tag, and changelog

Examples

Tag + GitHub Release

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-title: 'Release {{tag}}'

Draft Release (Review Before Publishing)

Create releases as drafts so you can review, edit, or batch multiple releases before they go public:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}create-release: truerelease-draft: truerelease-title: 'Release {{tag}}'

Drafts are invisible to users until you publish them. Go to Releases → Edit draft → Publish release when you're ready. The Conventional Commits changelog is already written for you.

Tag + Use Version Downstream

- name: ✈️ Release Pilotid: releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: 🐳 Build Docker Imageif: steps.release.outputs.released == 'true'run: | docker build -t myapp:${{ steps.release.outputs.version }} . docker push myapp:${{ steps.release.outputs.version }}

Pre-release from Feature Branches

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}prerelease: trueprerelease-suffix: beta # omit to use the branch name as identifier

When prerelease-suffix is omitted, the branch name is sanitised and used as the identifier. For example, a branch named feature/my-api produces 1.2.0-my-api.1, 1.2.0-my-api.2, etc.

Dry Run (CI Validation)

- name: ✈️ Preview Releaseuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}dry-run: true

Custom Release Rules

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}custom-rules: 'hotfix:patch:🔥 Hotfixes,improvement:minor:💡 Improvements'

Floating Major/Minor Tags (GitHub Action Style)

Automatically maintain v2 → latest v2.x.x and v2.3 → latest v2.3.x tags, just like actions/checkout@v6 does:

- name: ✈️ Release Pilotuses: ranarn/release-pilot@v1with:
token: ${{ secrets.GITHUB_TOKEN }}major-tag: true # v2 always points to latest v2.x.xminor-tag: true # v2.3 always points to latest v2.3.x

When you release v2.3.1, Release Pilot creates three tags:

  • v2.3.1 — the exact version (immutable)
  • v2.3 — floats to latest v2.3.x (force-updated)
  • v2 — floats to latest v2.x.x (force-updated)

📥 Inputs

InputDescriptionDefault
tokenGitHub token for authentication${{ github.token }}
prefixTag prefixv
default-bumpBump when no conventional type found. One of patch, minor, major, none, false. Both none and false skip the release when nothing is detected.patch
initial-versionStarting version when no tags exist. Must be valid semver.0.1.0
prereleaseEnable prerelease modefalse
prerelease-suffixPrerelease identifier (e.g., beta)Branch name
branchesComma-separated release branch patterns (regex, anchored to full name). Ignored when prerelease is true.main,master
create-releaseCreate a GitHub Releasefalse
release-draftCreate release as draftfalse
release-titleRelease title template ({{version}}, {{tag}}){{tag}}
annotatedCreate annotated tagstrue
commit-shaOverride the commit SHA. Must be a full 40-character hex SHA.GITHUB_SHA
dry-runCalculate without creating anythingfalse
custom-rulesCustom commit type rules''
include-body-in-changelogInclude commit body in changelogfalse
major-tagCreate/update floating major tag (v2 → latest v2.x.x)false
minor-tagCreate/update floating minor tag (v2.3 → latest v2.3.x)false

📤 Outputs

OutputDescriptionExample
versionNew version without prefix1.2.3
tagNew tag with prefixv1.2.3
previous-versionPrevious version1.2.2
previous-tagPrevious tagv1.2.2
bumpBump type appliedminor
changelogGenerated changelog (markdown)See below
release-urlURL to GitHub Releasehttps://...
releasedWhether a new version tag was created. true regardless of whether a GitHub Release was made — check release-url for that.true

Conventional Commits

Release Pilot follows the Conventional Commits specification:

<type>[optional scope][!]: <description>
[optional body]
[optional footer(s)]

Default Commit Types → Bumps

TypeBumpChangelog Section
featminor🚀 Features
fixpatch🐛 Bug Fixes
perfpatch⚡ Performance
revertpatch⏪ Reverts
docsnone📚 Documentation
stylenone💄 Styling
refactornone♻️ Refactoring
testnone✅ Tests
buildnone📦 Build
cinone🔧 CI/CD
chorenone🧹 Chores

Breaking Changes → major

Breaking changes are detected via:

feat!: remove deprecated API

or:

feat: refactor authentication
BREAKING CHANGE: The auth token format has changed.

How It Works

graph TD
A[Push to release branch] --> B[Fetch existing tags]
B --> C[Get commits since last tag]
C --> D[Parse conventional commits]
D --> E[Determine bump type]
E --> F{Bump detected?}
F -->|Yes| G[Calculate next version]
F -->|No + default=false| H[Skip — no release]
F -->|No + default=patch| G
G --> I[Generate changelog]
I --> J{Dry run?}
J -->|Yes| K[Output results only]
J -->|No| L[Create git tag]
L --> M{Create release?}
M -->|Yes| N[Create GitHub Release]
M -->|No| O[Done]
N --> O
K --> O
O --> P[Write Job Summary ✈️]
Loading

Requirements

  • Checkout with full history: Always use fetch-depth: 0 in your checkout step. A shallow clone will cause the action to fail immediately with a clear error message.
  • Write permissions: The token needs contents: write permission.
  • Node.js 24: This action runs on Node.js 24 (handled automatically by GitHub Actions).

Notes

[skip ci] commits are excluded

Commits containing [skip ci] (case-insensitive; also [skip-ci] and [skip_ci]) are excluded from bump detection and the changelog. Release Pilot's own auto-commits (e.g. build: auto-update dist/ [skip ci]) use this convention to avoid triggering new releases.

Branch patterns are fully anchored regex

Each pattern in branches is wrapped as ^pattern$ before matching. Use release/.* to match all branches starting with release/. Invalid regex patterns fall back to exact string comparison.

Floating tags use force-push

major-tag and minor-tag are force-updated on each release. This is intentional — it is the standard GitHub Actions versioning pattern (actions/checkout@v4 does the same thing).

Changelog

See Releases for the full version history and changelogs.

License

MIT © Jonas Ranerfors

About

✈️ The definitive GitHub Action for semantic versioning, tagging, changelog generation, and releases — powered by Conventional Commits.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages