Open-source project
semantic-release/github avatar
semantic-release/github

@semantic-release/github: publishing releases and closing issues

:octocat: semantic-release plugin to publish a GitHub release and comment on released Pull Requests/Issues

540 stars148 forksJavaScriptMIT

At a glance

What is it?
This semantic-release plugin does the GitHub half of a release: it creates the GitHub release, uploads assets, flips the pre-release flag for a channel, comments on the issues and pull requests the release resolved, and opens an issue when the release fails. It suits a team already running semantic-release, and it is not a release tool on its own, since without the core it computes no version and publishes nothing.
Who is it for?
Adopt @semantic-release/github if your release pipeline already runs semantic-release and you want GitHub releases, uploaded assets and automatic issue closing to come from the same run. Do not install it as a standalone release tool, because it computes no version and needs the core plugin, and the README warns that declaring it as a direct dependency of your own project can conflict when semantic-release is upgraded.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The five steps a semantic-release plugin exposes

The README documents the plugin through the lifecycle steps semantic-release calls, and reading that table tells you exactly what this package does and, just as importantly, what it leaves to other plugins.

`verifyConditions` verifies the presence and the validity of the authentication, set via environment variables, and the assets option configuration. So the plugin checks early that a token exists and that the asset paths are sane, before anything is published.

`publish` publishes a GitHub release, optionally uploading file assets.

`addChannel` updates a GitHub release's `pre-release` field. This is the step that matters for a project with channels such as `beta` or `next`, because a release published to a channel is marked as a pre-release rather than as the current stable one.

`success` adds a comment to each GitHub Issue or Pull Request resolved by the release, and closes issues previously opened by the `fail` step. So a successful release leaves a trace on every issue whose closing keyword the commit messages contained, and removes the issue that a failed release opened.

`fail` opens or updates a GitHub Issue with information about the errors that caused the release to fail.

The two ends of that lifecycle are worth pairing up in your head. A failed release is not a silent no-op, it becomes a GitHub issue that someone can see and act on, and the same issue is closed when a later release succeeds. That is the part of this plugin that is more than convenience, because a release pipeline that fails quietly is a release pipeline nobody notices is broken.

Authentication: GH_TOKEN, scopes, and the GITHUB_TOKEN trap

The GitHub authentication configuration is required, and it is set through environment variables. The token has to be made available in the CI environment via the `GH_TOKEN` environment variable, and the user associated with the token must have push permission to the repository.

The minimum required scopes differ by repository visibility. A private repository needs the `repo` scope, and a public repository needs `public_repo`.

The GitHub Actions case is where most teams get this wrong, and the README explains why. You can use the default token provided in the `GITHUB_TOKEN` secret, but releases done with that token will NOT trigger release events to start other workflows. If you have actions that trigger on newly created releases, you need a generated token for that, stored in the repository's secrets under any name other than `GITHUB_TOKEN`.

That is a genuine platform limitation rather than a plugin choice. The default token exists so that a workflow cannot escalate its own permissions, and a release created with it is attributed to the workflow rather than to a user, so the events it would fire are not emitted. The consequence for a team is concrete: if your release is meant to trigger a downstream workflow, publishing a release, an npm publication, a container build, the default token will silently do the first step and not the second.

With the `GITHUB_TOKEN`, the minimum required permissions are `contents: write` to publish a GitHub release, `issues: write` to comment on released issues, and `pull-requests: write` to comment on released pull requests.

The environment variable table also covers self-hosted GitHub. `GITHUB_TOKEN` or `GH_TOKEN` is the required token, `GITHUB_URL` or `GH_URL` is the GitHub server endpoint, `GITHUB_PREFIX` or `GH_PREFIX` is the GitHub API prefix relative to that URL, and `GITHUB_API_URL` is the API endpoint, which overwrites the prefix.

Configuring the plugin inside a semantic-release config

The plugin is configured in the semantic-release configuration file, and the README's example shows it in the plugins array alongside the two plugins that do the version analysis and the release notes.

json
{
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    [
      "@semantic-release/github",
      {
        "assets": [
          { "path": "dist/asset.min.css", "label": "CSS distribution" },
          { "path": "dist/asset.min.js", "label": "JS distribution" }
        ]
      }
    ]
  ]
}

The tuple form, plugin name followed by an options object, is how a plugin is given configuration. Here the options are the `assets` array, and each asset is a path and a label, so the file `dist/asset.min.css` appears in the release with the label `CSS distribution`. With this example, GitHub releases will be published with the file `dist/asset.min.css` and `dist/asset.min.js`.

The `assets` option is also what `verifyConditions` checks, so a mistyped path is caught before the release is created rather than half way through it.

The options table in the README also gives the endpoint overrides as configuration rather than only as environment variables. `githubUrl` is the GitHub server endpoint and defaults to the `GH_URL` or `GITHUB_URL` environment variable, and `githubApiPathPrefix` is the GitHub API prefix relative to `githubUrl`.

The dependency list in the package manifest explains how the plugin talks to GitHub and how it decides what to attach. It uses the Octokit core with the paginate-rest, retry and throttling plugins, and the retry and throttling plugins are the reason a release step survives a flaky API call instead of failing the whole run. `issue-parser` and `p-filter` handle the issue and pull request scanning that the `success` step needs, `dir-glob` and `tinyglobby` expand the asset globs, `mime` works out content types for the uploaded files, and `undici` with the proxy agents is the HTTP layer.

Installing it, or rather not installing it

The install command is one line, and the README immediately follows it with a tip that changes the advice.

bash
$ npm install @semantic-release/github -D

You do not need to directly depend on this package if you are using `semantic-release`. `semantic-release` already depends on this package, and defining your own direct dependency can result in conflicts when you update `semantic-release`.

That is a specific and correct piece of advice about npm's dependency resolution. If your project pins `@semantic-release/github` and the core package pins a different version, you can end up with two copies of the plugin loaded, and the one that runs is the one the core resolves, not necessarily the one you configured. The fix is to add the plugin to the `plugins` array by name and let the core bring the implementation along.

For reference, the package manifest gives the shape of the thing. The package is an ES module, its files entry point is `lib` and `index.js`, and the engines field requires Node `^22.14.0 || >= 24.10.0`. The tests run on ava, with the integration test file excluded from the default set, and the tooling includes c8 for coverage, prettier, publint for checking what would be published, lockfile-lint, and the semantic-release core itself as a development dependency at 25.0.9.

The top level layout is small and typical for a published package: `index.js` at the root, `lib/` for the implementation, `test/` for the tests, a `package.json`, a `package-lock.json`, a `LICENSE`, and an `.nvmrc` that pins the Node version the project develops against.

What the plugin does not do, and where a release actually comes from

The limits of this package are worth stating plainly, because the name invites a misunderstanding.

It computes no version. The version comes from the commit analyzer, and the release notes come from the release notes generator, both of which are separate plugins in the example configuration. The `publish` step here creates the GitHub release with whatever version and notes the rest of the pipeline produced, and the `addChannel` step marks it as a pre-release when it belongs to a channel.

It also does not publish to a package registry. The GitHub release is a distribution artefact and a changelog, and for a library that ships to npm the registry publication is a different plugin entirely. The README's example configuration has three plugins and none of them is a registry publisher, which is consistent with the plugin's scope being GitHub only.

The `success` step has a precondition that is easy to miss. It comments on each issue or pull request resolved by the release, and a GitHub issue is only marked resolved if the commit message that closed it contains the closing keyword. If your commit convention does not include those keywords, the step runs and finds nothing to comment on, and the release will look successful while leaving issues open. That is a configuration coupling between your commit message format and this plugin's behaviour, and it is the kind of thing that only shows up when you check whether the issues actually closed.

A second limitation is the fail step's behaviour. It opens or updates a GitHub Issue with information about the errors that caused the release to fail, which is useful, but it means that a broken release pipeline creates issues in your tracker, and a pipeline that is broken in a way that fails before the `fail` step runs creates nothing at all. The step's reach is bounded by how far the run got.

Alternative approaches and the cost of the plugin model

There are two realistic alternatives to this plugin, and the difference is in what does the work.

The first is doing it in the CI workflow. A GitHub Actions job can create a release with the GitHub CLI and post comments on issues itself, driven by a script that reads the version out of a file. The difference in approach is that the logic lives in your repository and is written by you, rather than living in a versioned npm package with a defined step contract. You get exactly the behaviour you want and you own the maintenance, and you lose the retry and throttling handling, the asset glob expansion and the issue parsing that the Octokit based plugin gives you for free.

The second is choosing a different semantic-release plugin for the same job, such as one that publishes to GitLab or to a different hosting platform, if you are not on GitHub. The step contract is the same, so the configuration shape carries over, but the details of each step, in particular how issues are matched and commented on, differ per platform, and the endpoint options in this README exist precisely because those details are platform specific.

The broader trade-off the plugin model introduces is worth naming. A semantic-release plugin is a package with a major version, and a major version bump can change behaviour in a way that changes your release output. The releases in this repository move in step, with v12.0.10 on 2026-09-21, v12.0.9 on 2026-07-01 and v12.0.8 on 2026-05-08, and the last push to the tree was on 2026-10-19, so the package is being maintained. But a team that has built a release process on a plugin is taking a dependency on the plugin's release cadence, and that is the cost of the model.

The repository is not archived, it is MIT licensed, and the licence file sits at the root alongside the README and the package manifest.

Editorial conclusion

Adopt @semantic-release/github if your release pipeline already runs semantic-release and you want GitHub releases, uploaded assets and automatic issue closing to come from the same run. Do not install it as a standalone release tool, because it computes no version and needs the core plugin, and the README warns that declaring it as a direct dependency of your own project can conflict when semantic-release is upgraded. Before enabling the `success` step, check that your token has `issues: write` and `pull-requests: write` as well as `contents: write`, and remember that a release made with the default `GITHUB_TOKEN` will not trigger other workflows that watch for release events.

Frequently asked questions

Do I need to install @semantic-release/github separately?

No. The README says semantic-release already depends on this package, and defining your own direct dependency can result in conflicts when you update semantic-release. You list the plugin by name in the plugins array instead.

Which token and permissions does @semantic-release/github need?

A token must be available via the `GH_TOKEN` environment variable, and the user behind it needs push permission. The minimum scopes are `repo` for a private repository and `public_repo` for a public one, or with `GITHUB_TOKEN` the permissions `contents: write`, `issues: write` and `pull-requests: write`.

Why do releases created with GITHUB_TOKEN not trigger other workflows?

The README states that releases done with the default `GITHUB_TOKEN` will not trigger release events to start other workflows. If you have actions that trigger on newly created releases, use a generated token stored in your repository secrets under a name other than `GITHUB_TOKEN`.

How do I upload build artefacts to the GitHub release with @semantic-release/github?

Use the `assets` option, an array of path and label pairs such as `{ "path": "dist/asset.min.css", "label": "CSS distribution" }`. The `publish` step publishes the GitHub release with those files attached, and `verifyConditions` checks the asset configuration before publishing.

What happens when a semantic-release run fails with @semantic-release/github?

The `fail` step opens or updates a GitHub Issue with information about the errors that caused the release to fail. That issue is closed by a later successful release, because the `success` step closes issues previously opened by the `fail` step.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. semantic-release/github on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/semantic-release-github.svg)](https://hysenlabs.com/projects/semantic-release-github)

Was this useful?