Installing semantic-release along with its core plugins, then putting together release.config.cjs with the main and staging rc branches. Including custom commit analyzer rules for mapping commit types to release types and the package.json configuration alternative.

In episode 4 we mapped out the branching strategy: staging for release candidates and main for stable. Now it's time to turn that map into real configuration. Semantic-release is controlled by a single config file — release.config.cjs at the repository root — which determines which branches get released, the tag format, and which plugins are used.
The good news: you don't need to write plugins from scratch. Semantic-release already ships core plugins that cover almost everything: analyzing commits, assembling changelogs, publishing packages, creating tags, and releasing to GitHub. We just install them and wire them together.
Semantic-release and the core plugins are installed as development dependencies. Run this at the project root:
npm install -D semantic-release @semantic-release/commit-analyzer @semantic-release/release-notes-generator @semantic-release/github @semantic-release/git @semantic-release/npmRequirements: Node.js version 18 or higher (stable at 20 or 22). The @semantic-release/npm plugin is optional — if your project isn't a published package, this plugin can be configured to only update package.json without publishing to the registry.
Semantic-release reads configuration from the release.config.cjs file or the release key in package.json. Here's a complete example with the main and staging branches:
module.exports = {
branches: [
"main",
{ name: "staging", prerelease: "rc" },
],
tagFormat: "v${version}",
repositoryUrl: "https://github.com/akun/proyek.git",
plugins: [
[
"@semantic-release/commit-analyzer",
{
preset: "conventionalcommits",
releaseRules: [
{ breaking: true, release: "major" },
{ type: "feat", release: "minor" },
{ type: "fix", release: "patch" },
{ type: "hotfix", release: "patch" },
{ type: "perf", release: "patch" },
{ type: "refactor", release: "patch" },
{ type: "chore", release: "patch" },
{ type: "docs", release: false },
{ type: "test", release: false },
{ type: "ci", release: false },
],
},
],
"@semantic-release/release-notes-generator",
"@semantic-release/npm",
"@semantic-release/git",
"@semantic-release/github",
],
};Let's break down the important parts:
branches: the list of branches allowed to release. "main" produces a full SemVer; the staging object with prerelease: "rc" produces 1.2.0-rc.N.tagFormat: the release tag template. A v prefix followed by the version is a common convention.releaseRules: rules mapping commit types to release types. Rules are evaluated in order; the first match wins.The releaseRules section is the mapping brain. A few patterns worth noting:
{ breaking: true, release: "major" } — whatever the type, a commit marked breaking bumps major.{ type: "feat", release: "minor" } — a new feature bumps minor.{ type: "fix", release: "patch" } — a bug fix bumps patch.{ type: "docs", release: false } — documentation triggers no release at all.Warning
Be careful with release: false for types you consider important. A ci, docs, or test commit that triggers a release will only bump the version without any benefit to users — and flood the changelog with noise.
If you'd rather have one fewer file, the configuration can live under the release key in package.json:
{
"release": {
"branches": ["main", { "name": "staging", "prerelease": "rc" }],
"tagFormat": "v${version}"
}
}Its advantage: all project configuration lives in one file. Its drawback: the package.json file can become large and dense, especially when releaseRules grows. For projects with complex rules, a separate release.config.cjs file is easier to read and review.
Before running a real release, make sure the configuration behaves as expected:
npx semantic-release --dry-run --no-ci--dry-run makes semantic-release compute the version and changelog without creating tags, commits, or publishes.--no-ci skips the CI environment check — useful when running on a local machine.Tip
On a local machine, don't forget to add --no-ci. Without that flag, semantic-release refuses to run outside CI and immediately exits with the message "This command can only be run in a CI environment".
Watch the output for three things: the computed version, the generated changelog, and the branch being released. If everything matches expectations, the configuration is ready to hand over to GitHub Actions — which we'll build in episode 6.
In episode 5 you:
release.config.cjs with the main and staging rc branches.releaseRules to map commit types to release types.release key in package.json.--dry-run --no-ci.In episode 6 we'll run this configuration in GitHub Actions: set up push and pull request triggers, separate the ci and release jobs, and manage secrets like GITHUB_TOKEN and NPM_TOKEN. See you in episode 6!