Skip to content
Back to skills

Github Release

ASecurity

Run an end-to-end GitHub library release workflow with git and gh: inspect tags, classify public API changes, choose a SemVer bump, update CHANGELOG.md, create release/vX.Y.Z, push, and open a release PR. Use when asked to cut a release, bump a version, generate a changelog, create a release branch, or publish a new GitHub repository version.

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 4, 2026
ai-agentsgoshellbashgitapisecurityperformance

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned September 4, 2026

npx -y skills add paulasilvatech/awesome-harness-primitives --skill github-release --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Github Release?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Github Release
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/paulasilvatech-github-release/badge)](https://www.skillsdirectory.com/skills/paulasilvatech-github-release)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: github-release
description: >-
  Run an end-to-end GitHub library release workflow with git and gh: inspect tags, classify public
  API changes, choose a SemVer bump, update CHANGELOG.md, create release/vX.Y.Z, push, and open a
  release PR. Use when asked to cut a release, bump a version, generate a changelog, create a
  release branch, or publish a new GitHub repository version.
metadata:
  compatibility: "requires: gh CLI and git"
---

<!-- Generated from harness/github-copilot/plugins/git-github-workflow/skills/github-release/SKILL.md by harness/claude-code/scripts/convert_from_copilot.py. Edit the source, not this file. -->

# GitHub release

Release a single-package GitHub repository by reading tags and public diffs, selecting the next SemVer version, updating `CHANGELOG.md`, creating a release branch, and opening a pull request with `gh`. Keep steps 1 through 4 read-only; write only after the version is confirmed.

## When to invoke

- "Cut a new GitHub release."
- "Bump the version and generate a changelog."
- "Create a release branch and PR."
- "Let's ship a new version."
- "Publish a new version from this repository."

## Prerequisites and context

- Requires `gh` authenticated with `gh auth status` and a working `git` checkout inside a GitHub repository.
- Verify `gh repo view --json nameWithOwner` succeeds before release work.
- Start from a clean working tree unless the user explicitly directs how to handle local changes.
- Ask for the public-facing source directory once and store it as `PUBLIC_PATH`; examples include `src/`, `lib/`, `pkg/`, or `cmd/`; if empty, use `.`.
- Exclude `tests/`, `test/`, `spec/`, `__tests__/`, `docs/`, `*.lock`, `*-lock.json`, `*.sum`, generated files with a do-not-edit header, and build artifacts from public API classification.

## Procedure

1. Ensure `main` is current:

   ```bash
   git checkout main
   git pull origin main
   ```

2. Fetch and identify the latest version tag from git tags, not `gh release list`:

   ```bash
   git fetch --tags
   PREV_TAG=$(git tag --sort=-version:refname | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+' | head -1)
   echo "Latest tag: $PREV_TAG"
   git ls-remote --tags origin | grep "refs/tags/$PREV_TAG$"
   PREV_SHA=$(git rev-list -n 1 "$PREV_TAG" 2>/dev/null || git rev-list --max-parents=0 HEAD)
   ```

   `PREV_TAG` must preserve the tag spelling exactly, for example `v1.4.2`, while arithmetic strips a leading `v`. If no tag exists, set `PREV_TAG` to `(none)`, set `PREV_SHA` to `git rev-list --max-parents=0 HEAD`, default `NEXT_VERSION` to `1.0.0`, and skip SemVer arithmetic. If the tag is local-only or orphaned, warn before continuing.

3. Analyze the code diff as the primary signal:

   ```bash
   git diff "$PREV_SHA"..HEAD -- "$PUBLIC_PATH" \
     ':(exclude)tests/' ':(exclude)test/' ':(exclude)spec/' \
     ':(exclude)__tests__/' ':(exclude)docs/' \
     ':(exclude)*.lock' ':(exclude)*-lock.json' ':(exclude)*.sum'
   ```

   If the diff is huge, triage with `git diff "$PREV_SHA"..HEAD --stat -- "$PUBLIC_PATH"` and then focus on public interface files such as `index.*`, `api.*`, `exports.*`, `public.*`, `mod.*`, and `__init__.*`.

4. Read commit intent as the secondary signal:

   ```bash
   git log "$PREV_SHA"..HEAD --oneline --no-merges
   ```

   Use the bundled `references/commit-classification.md` only to interpret messages such as `feat: new API`, `fix: typo`, `chore: refactor`, or a one-line security fix whose intent is not self-explanatory from code; prefer the code diff when signals conflict.

5. Determine the highest SemVer bump and present the proposed `NEXT_VERSION` with evidence. Ask for confirmation before writing. Compute from `MAJOR.MINOR.PATCH` and format as `vMAJOR.MINOR.PATCH`; highest precedence wins: `MAJOR > MINOR > PATCH`.

6. Create and push the release branch only after confirmation:

   ```bash
   git checkout -b release/vX.Y.Z
   git push -u origin release/vX.Y.Z
   ```

7. Update or create `CHANGELOG.md` in Keep a Changelog format using today's `YYYY-MM-DD` date, show the proposed section to the user, then write it. Add or update the comparison link: `https://github.com/OWNER/REPO/compare/vPREV...vNEXT`.

8. Commit and push:

   ```bash
   git add CHANGELOG.md
   git commit -m "chore: release vX.Y.Z"
   git push origin release/vX.Y.Z
   ```

9. Open the release PR. Always use `--body-file`, not inline `--body`; inline `\n` can render literally in PowerShell. Create `release_pr_body.md` or an equivalent repo-local scratch file, then run:

   ```bash
   gh pr create --base main --head release/vX.Y.Z --title "Release vX.Y.Z" --body-file release_pr_body.md
   ```

10. Hand off tagging after merge:

   ```bash
   git tag vX.Y.Z <merge-commit-sha>
   git push origin vX.Y.Z
   ```

## SemVer classification

| Evidence from public diff | Bump | Changelog section |
| --- | --- | --- |
| Removed symbols, changed signatures, or breaking behavior changes | `MAJOR` | `Removed` or `Changed` with breaking note |
| New exported symbols or user-visible features such as `NewClient` or `WithTimeout` in `src/client.go` | `MINOR` | `Added` |
| Bug/logic fix, off-by-one correction, performance improvement, security fix, docs, chore only | `PATCH` | `Fixed` or `Security`; omit purely internal work |
| No commits since last tag | none | Report nothing to release |

When a commit says `fix: typo` but the diff removes a public method, classify as `MAJOR`. When a commit says `feat: new API` but only private internals changed, classify as `PATCH`. Document every conflict in the changelog review.

Treat HTTP API surface changes as public when the library exposes handlers, middleware, generated clients, or route contracts. Keep `PATH` changes only when they alter public CLI behavior.

## Changelog rules

Insert directly below `# Changelog`:

```markdown
## [X.Y.Z] - YYYY-MM-DD

### Added
- ...

### Changed
- ...

### Deprecated
- ...

### Removed
- ...

### Fixed
- ...

### Security
- ...
```

Omit empty headings. Write plain English from the user's perspective, not raw commit messages. Map a new exported symbol to Added, a breaking removal to Removed, a breaking existing API change to Changed, a bug or perf fix to Fixed, and a security fix to Security.

## PowerShell equivalents

Use PowerShell-safe forms on Windows:

```PowerShell
git fetch --tags
$prevTag = git tag --sort='-version:refname' | Select-String '^[vV]?\d+\.\d+\.\d+' | Select-Object -First 1 -ExpandProperty Line
if ($prevTag) { $prevSha = git rev-list -n 1 $prevTag } else { $prevSha = git rev-list --max-parents=0 HEAD }
git diff "$($prevSha)..HEAD" -- $publicPath ':(exclude)tests/' ':(exclude)test/' ':(exclude)spec/' ':(exclude)__tests__/' ':(exclude)docs/' ':(exclude)*.lock' ':(exclude)*-lock.json' ':(exclude)*.sum'
```

For the PR body, use a here-string, write with `Out-File -FilePath release_pr_body.md -Encoding utf8 -NoNewline`, and pass `--body-file release_pr_body.md`. If `gh` usage appears unexpectedly, check `Get-Command gh`, `gh --version`, `git fetch --tags`, and `git diff --name-only $prevSha..HEAD -- src/`.

## Progressive disclosure and bundled resources

Read bundled references only when the release decision needs deeper rules:

- `references/semver-rules.md`: extended SemVer edge cases for `MAJOR`, `MINOR`, and `PATCH`.
- `references/commit-classification.md`: commit-message heuristics for secondary signal classification.

## Troubleshooting

| Situation | Resolution |
| --- | --- |
| `gh auth status` fails | Stop and tell the user to run `gh auth login`. |
| Not inside a git repo | Stop and tell the user to `cd` into the repository. |
| Working tree is dirty | Warn and ask whether to stash, commit, or abort. |
| No commits since last tag | Tell the user there is nothing to release. |
| Tag exists locally but not remotely | Warn that the tag appears local-only and ask whether to push or continue. |
| Tag points to no commit | Use `git rev-list --max-parents=0 HEAD` as the fallback diff base and warn. |
| Diff is empty for `PUBLIC_PATH` but commits exist | Warn that changes may be internal and ask whether to proceed. |
| `git push` fails | Report the error verbatim and suggest checking protected branch rules. |

## Gotchas

- **Do not use `gh release list` as source of truth**: releases are optional; tags are authoritative.
- **Do not create `release/vX.Y.Z` before version confirmation**: branch names must match the final version.
- **Do not use inline `--body` for multiline PR text**: use `--body-file` so markdown line breaks survive Bash and PowerShell.
- **Do not classify from commits alone**: code diff is primary; commit log is context.
- **IMPORTANT** defaults: represent no previous tag as `(none)`, default the first release to `1.0.0`, and use `git tag` only after the release PR merges.

## Output template

```markdown
## GitHub release result

**Status:** PR opened | ready for PR | blocked
**Previous tag:** `<PREV_TAG>`
**Previous SHA:** `<PREV_SHA>`
**Next version:** `<NEXT_VERSION>`
**Public path:** `<PUBLIC_PATH>`

### Classification
| Evidence | Bump impact | Changelog entry |
| --- | --- | --- |
| `<file or commit evidence>` | `MAJOR | MINOR | PATCH` | `<entry or omitted>` |

### Commands run
- `gh auth status`
- `gh repo view --json nameWithOwner`
- `git status`
- `git fetch --tags`
- `git diff <PREV_SHA>..HEAD -- <PUBLIC_PATH>`
- `git log <PREV_SHA>..HEAD --oneline --no-merges`
- `git checkout -b release/vX.Y.Z`
- `git push -u origin release/vX.Y.Z`
- `gh pr create --base main --head release/vX.Y.Z --title "Release vX.Y.Z" --body-file release_pr_body.md`

### Handoff
After merge, create the tag:
`git tag vX.Y.Z <merge-commit-sha>`
`git push origin vX.Y.Z`
```

## Quality gate

- [ ] `gh auth status`, `gh repo view --json nameWithOwner`, and `git status` were checked before changes.
- [ ] `PREV_TAG`, `PREV_SHA`, `PUBLIC_PATH`, and `NEXT_VERSION` are recorded.
- [ ] Public diff and commit log were both read; conflicts favor code diff.
- [ ] The proposed SemVer bump cites concrete public API evidence.
- [ ] `CHANGELOG.md` follows Keep a Changelog, omits empty sections, and includes the compare URL.
- [ ] The release branch is named `release/vX.Y.Z` and was created only after confirmation.
- [ ] The PR uses `--body-file` and includes the changelog section or a clear placeholder.
- [ ] The final handoff tells the user to tag the merge commit and push the tag.

## References

- [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
- [GitHub compare URL pattern](https://github.com/OWNER/REPO/compare/vPREV...vNEXT)

Files in this skill

  • SKILL.md10.3 KB
  • references/commit-classification.md2.8 KB
  • references/semver-rules.md2.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.