# Semantic Release
# What is a Release
A release is the distribution of a version of an application.
A software release may be either public or private and generally constitutes the initial generation of a new or upgraded application.
# What is Semantic Release
Semantic Release is an Open-Source Software tool for automatically versioning your software with Semantic Versions (opens new window) based on your Git commit messages.
It then releases/deploys the new version to the channel(s) you specify, for example, GitHub Release, NPM, PyPI, etc.
By default, Semantic Release expects commits to be in the Conventional Commit format (opens new window) .
# Why Semantic Release
semantic-release (opens new window) automates the whole package release workflow including:
- determining the next version number
- generating the release notes
- publishing the package
This removes the immediate connection between human emotions and version numbers, strictly following the Semantic Versioning specification.
semantic-release (opens new window) is using Conventional Commits (opens new window) and Semantic Versioning (opens new window)
# CI flow proposal
Before releasing a version and via github action:
- You need to check that your commit messages meet the Conventional Commits format
- You need to lint your commited code
- You need to run some unit testing
- You may also publish the released version
- You may also use the released version on Sentry
- You may also deploy the released version
# Setup
You need to
- add git tags (depends on your needs - optional)
- add GitHub repo Encrypted Secrets (depends on your needs - optional)
- set project as private or public
- add a configuration file for Semantic Release
- lint your commit messages with a CI action
- add a CI Action
- Install semantic-release and its plugins as dev dependencies on your project (depends on your needs - optional)
# Git tags
Semantic Versioning does not cover libraries tagged 0.*.*
. The first stable version is 1.0.0
.
If tag v1.0.0
has already been created make sure that it is tagged to a commit that belongs to the release branch.
For most cases Semantic release will create v1.0.0
tag automatically on last git commit (on first CI Action run)
You may create manually a v1.0.0
tag (release notes - changelog will note generated from commits that exist before of this tag)
# Examples
git-tag tutorial (opens new window)
# Create tag - git command
git tag <tagname>
# Create tag on last commit
git tag -a v1.0.0 -m "initial release"
# Create tag on specific commit
git tag -a v1.0.0 -m "initial release" 15027957951b64cf874c3557a0f3547bd83b3ff6
# Create tag without description
git tag -a v1.0.0
# Push tag - git command
git push origin <tag_name>
# Push tag to origin
git push origin v1.0.0
# GitHub repo Encrypted Secrets
GITHUB_TOKEN
does not have the required permissions to operate on protected branches.
If you are using semantic-release for protected branches, replace GITHUB_TOKEN
with Personal Access Token
.
# Creating Encrypted secrets for a repository
- github docs - create personal access token (opens new window)
- github docs - create encrypted secret (opens new window)
# create personal access token
- Verify your email address, if it hasn't been verified yet
- Ιn the upper-right corner of GitHub page, click your profile photo, then click Settings
- In the left sidebar, click Developer settings
- In the left sidebar, click Personal access tokens
- Click Generate new token
- Set Note as
ADMIN_GH_TOKEN
- Select scope
repo
-Full control of private repositories
- Click Generate token
- Copy generated token
# creating encrypted secret
- On GitHub, visit the main repository page
- Under your repository name, click Settings
- In the left sidebar, click Secrets
- Click New repository secret
- Type
ADMIN_GH_TOKEN
for your secret in the Name input box - Enter the previously generated token for your secret
- Click Add secret
# Bump Version but not publish
Just set private: true
into package.json
package.json
{
"name": "pitcher-project-name",
"private": true,
# Configuration File
configuration guide (opens new window)
- Create
release.config.js
- Use
@semantic-release/github
- Disable
successComment
,failComment
,labels
to avoid sending too much emails - Set
releaseLabels
as'released<%= nextRelease.channel ? ` on ${nextRelease.channel}@${nextRelease.version}` : "" %> from <%= branch.name %>'
- Disable
- Use
@semantic-release/release-notes-generator
plugin to generate release notes - Use
@semantic-release/changelog
plugin to create changelog - Use
@semantic-release/git
plugin to commit release assets to the project's git repository
# Main branch | publish - commit - no npm version
- The release branch is
main
- The release is published
- A commit is created with release assets
- npm version is NOT commited on git repository
- changelog is commited
- build files are commited
release.config.js
module.exports = {
branches: [{ name: 'main' }],
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
['@semantic-release/changelog', { changelogFile: 'CHANGELOG.md' }],
['@semantic-release/npm', { npmPublish: true, pkgRoot: '.' }],
[
'@semantic-release/github',
{
successComment: false,
failComment: false,
releasedLabels: [
// eslint-disable-next-line max-len
'released<%= nextRelease.channel ? ` on ${nextRelease.channel}@${nextRelease.version}` : "" %> from <%= branch.name %>',
],
},
],
[
'@semantic-release/git',
{
assets: ['build/**/*.js', 'CHANGELOG.md'],
message: 'chore(release): set `package.json` to ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
},
],
],
}
package.json
{
"private": false,
# Dev branch | publish - commit
- The release branch is
dev
- The release is NOT published
- A commit is created with release assets
- npm version is commited on git repository
- changelog is commited
release.config.js
module.exports = {
branches: [{ name: 'dev' }],
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
[
'@semantic-release/changelog',
{
changelogFile: 'docs/CHANGELOG.md',
},
],
['@semantic-release/npm', { npmPublish: false }],
[
'@semantic-release/github',
{
successComment: false,
failComment: false,
labels: false,
releasedLabels: [
'released<%= nextRelease.channel ? ` on ${nextRelease.channel}@${nextRelease.version}` : "" %> from <%= branch.name %>',
],
},
],
[
'@semantic-release/git',
{
assets: ['docs/CHANGELOG.md', 'package.json'],
message: 'chore(release): set `package.json` to ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
},
],
],
}
package.json
{
"private": true,
# Maintenance branches
- The release branch is
dev
- The maintenance branch is
firstVersion/dev
- The major version of the maintenance branch will always be
1
- A common commit for both release and maintenance branch should be tagged as
v1.0.0
- The major version of the maintenance branch will always be
- The release is NOT published
- A commit is created with release assets
- npm version is commited on git repository
- changelog is commited
release.config.js
module.exports = {
branches: [{ name: 'dev' }, { name: 'firstVersion/dev', range: '1.x' }],
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
[
'@semantic-release/changelog',
{
changelogFile: 'docs/CHANGELOG.md',
},
],
'@semantic-release/npm',
[
'@semantic-release/github',
{
successComment: false,
failComment: false,
labels: false,
releasedLabels: [
'released<%= nextRelease.channel ? ` on ${nextRelease.channel}@${nextRelease.version}` : "" %> from <%= branch.name %>',
],
},
],
[
'@semantic-release/git',
{
assets: ['docs/CHANGELOG.md', 'package.json'],
message: 'chore(release): set `package.json` to ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
},
],
],
}
package.json
{
"private": true,
# Github Actions
# Release using cycjimmy/semantic-release-action
Using cycjimmy/semantic-release-action (opens new window)
If using the @semantic-release/git plugin
for protected branches, avoid persisting credentials as part of actions/checkout@v2
by setting the parameter persist-credentials: false
.
This credential does not have the required permission to operate on protected branches
Note: No need to install semantic release or semantic release plugins as dev dependencies on your project
.github/workflows/release.yml
name: Release
on:
push:
branches:
- main
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
persist-credentials: false
- name: Install dependencies
run: npm install
- name: Semantic release
uses: cycjimmy/semantic-release-action@v2
id: semantic_release
with:
semantic_version: 17.4.2
extra_plugins: |
@semantic-release/changelog@5.0.1
@semantic-release/git@9.0.0
env:
GITHUB_TOKEN: ${{ secrets.ADMIN_GH_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# Release and publish - minimal configuration with a build running on Node 12
.github/workflows/release-and-publish.yml
name: Release and publish
on:
push:
branches:
- main
jobs:
release-and-publish:
name: Release and publish
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 12
- name: Install dependencies
run: npm ci
- name: Semantic release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release
# Build (commit_lint - lint - unit_test - release - sentry - deploy -no publish)
Note: kudu -u $KUDU_USER -p $KUDU_PASSWORD push -f 999999
set your own document id (instead of 999999)
.github/workflows/build.yml
name: CI
on:
push:
branches: [dev]
pull_request:
branches: [dev]
jobs:
commit_lint:
name: Commit Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: wagoid/commitlint-github-action@v2
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: |
**/node_modules
key: node-modules
- run: npm install
- name: Lint
run: npm run lint
unit_tests:
name: Unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: |
**/node_modules
key: node-modules
- run: npm install
- name: Unit testing with coverage report
run: npm run test:unit-coverage
- name: Coveralls GitHub Action
uses: coverallsapp/github-action@v1.1.2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
deployment:
name: Deploy dev branch
runs-on: ubuntu-latest
needs: [commit_lint, lint, unit_tests]
if: contains('refs/heads/dev', github.ref)
steps:
- name: Checkout
uses: actions/checkout@v2
with:
lfs: true
persist-credentials: false
- name: Semantic release
uses: cycjimmy/semantic-release-action@v2
id: semantic_release
with:
semantic_version: 17.4.2
extra_plugins: |
@semantic-release/changelog@5.0.1
@semantic-release/git@9.0.0
env:
GITHUB_TOKEN: ${{ secrets.ADMIN_GH_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Set up Python 2.7
uses: actions/setup-python@v1
with:
python-version: 2.7
- name: Install Kudu
run: |
python -m pip install --upgrade pip
pip install kudu
- name: Cache Node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: |
**/node_modules
key: node-modules
- name: Install Node modules
run: npm install
- name: Build
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
RELEASE_VERSION: ${{ steps.semantic_release.outputs.new_release_version }}
run: npm run build
- name: Deploy
env:
KUDU_USER: ${{ secrets.KUDU_USER }}
KUDU_PASSWORD: ${{ secrets.KUDU_PASSWORD }}
run: |
cd dist
kudu -u $KUDU_USER -p $KUDU_PASSWORD push -f 999999