Set up or align GitHub repo settings, branch/ruleset policy, templates, Actions hardening, Environments, release workflows, and deploy workflows for continuously publishable or deployable repositories.
97
100%
Does it follow best practices?
Impact
96%
1.35xAverage score across 7 eval scenarios
Passed
No known issues
Use this reference for the semantic-release configuration and the commit conventions that drive it.
Semantic-release derives the next version from commit messages. The repo must commit to Conventional Commits before this pipeline is reliable.
feat: → minor bumpfix: → patch bumpfeat!: / fix!: / BREAKING CHANGE: footer → major bumpchore:, docs:, refactor:, test:, build:, ci: → no release (unless flagged via release rules)Enforce locally with a commit-msg hook (commitlint for Node, convco for Go, etc.) so PRs cannot land non-conforming subjects. The release pipeline assumes the convention; it does not enforce it.
Order matters — semantic-release runs plugins in declaration order. Canonical order for an npm package:
@semantic-release/commit-analyzer — decides next version from commit history@semantic-release/release-notes-generator — builds the release notes body@semantic-release/changelog — writes/updates CHANGELOG.md (optional)@semantic-release/npm, @semantic-release/exec, etc.@semantic-release/git — commits version-bumped files back with [skip ci]@semantic-release/github — creates the GitHub Release and uploads assetsPlace @semantic-release/git before @semantic-release/github so the bump commit exists when the GitHub Release is created (the Release points at that commit's tag).
Always pass the same preset to both analyzer and notes generator:
["@semantic-release/commit-analyzer", { "preset": "conventionalcommits" }],
["@semantic-release/release-notes-generator", { "preset": "conventionalcommits" }]Mismatched presets produce inconsistent version decisions and notes.
@semantic-release/git Configuration["@semantic-release/git", {
"assets": ["package.json", "package-lock.json", "CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}]assets is the explicit list of files the bump commit includes. Add pnpm-lock.yaml, Package.swift, <podname>.podspec, or other manifests as needed.[skip ci] token in the message; it gates re-triggering.{ "branches": ["main"] }Add prerelease channels only when the repo actually publishes them:
{
"branches": [
"main",
{ "name": "next", "prerelease": true },
{ "name": "beta", "prerelease": true }
]
}A prerelease branch creates a separate npm dist-tag and GitHub Release. Enable it when the repo has an actual prerelease lane.
.releaserc.json at repo root, or a "release" block in package.json. Pick one..releaserc.json next to the package, paired with working_directory: on the action..releaserc.json at root works fine — semantic-release is a Node tool but only needs Node available on the runner.Before the first real release, dry-run on a topic branch:
GITHUB_TOKEN=… npx semantic-release --dry-run --no-ci --branches=$(git branch --show-current)The dry-run prints the computed version and notes without tagging or publishing. Confirm both look right before merging the first feat: commit to main.
Use cycjimmy/semantic-release-action@<full-sha> with an exact same-line version comment, preserving the repo's current major unless there is a concrete migration reason. For new workflows, start from the current major and keep it on Dependabot. Inputs worth knowing:
working_directory — for monorepo packages.extra_plugins — install CI/CD-only release plugins without polluting the repo's runtime or dev dependency graph. Pin every entry to an exact version such as @semantic-release/npm@13.1.5; use exact package specs in secret-bearing release jobs.semantic_version — pin the semantic-release major to keep release behavior reproducible.Keep semantic-release dependencies in package.json only when the repo intentionally owns local release execution, such as a documented release script developers run or a lockfile-owned release wrapper. For normal GitHub Actions release jobs, keep release-only plugins in the action's extra_plugins input and pin them there.