---
name: create_release
description: Cut a new release of the semver_dialects gem. Creates a GitLab release for the current version; the tag pipeline's `publish gem` CI job then pushes the gem to RubyGems automatically. Invoke when the user asks to "release", "cut a release", "publish the gem", or "create a release".
---

# Release semver_dialects

This skill cuts a release of the `semver_dialects` gem. It assumes the version bump has **already been merged to `master`** (i.e. `lib/semver_dialects/version.rb`, `CHANGELOG.md`, and `Gemfile.lock` are in sync on `master`). If they are not, stop and tell the user to land the version-bump MR first.

Use `glab` for every GitLab interaction — never the web UI, never raw `git push` to create tags.
If `glab` is not available, then fail loudly and point user towards install with `mise install`.
This is already configured in mise.toml.

The `publish gem` CI job is configured (in `.gitlab-ci.yml`) to run **automatically on `v*.*.*` tag pipelines**. Creating the GitLab release creates the tag, the tag pipeline spawns automatically, and `publish gem` runs as part of that pipeline. No master pipeline is involved in the publish step; no manual play step is required.

## Preconditions to verify

Run these checks first. If any fails, stop and report to the user.

1. **On master, clean, up to date.**
   ```
   git status                                 # (or: jj st if you use jj colocated)
   glab repo view --output json | jq -r .default_branch    # confirm default branch is master
   ```
2. **You can create tags on `gitlab-org/ruby/gems/semver_dialects`.** `vX.Y.Z` tags are typically protected, which means you need Maintainer (or higher) on this project to run `glab release create`. Verify with `glab auth status` and your project role. If you can't create the tag, stop and hand off the entire release to a Maintainer — once the tag exists, the gem push is automatic.
3. **Version triple is in sync.** Run the repo's own check:
   ```
   ruby scripts/version_verification.rb
   ```
4. **Read the version to release** from `lib/semver_dialects/version.rb`. Call this `VERSION` (e.g. `1.2.3`); the tag is `vVERSION` (e.g. `v1.2.3`).
5. **No existing release/tag** for that version:
   ```
   glab release view "v$VERSION"   # should error "not found"
   ```
   If a release already exists, stop — the version probably wasn't bumped.
6. **Extract the changelog section** for this version from `CHANGELOG.md` (the lines under `## vVERSION` up to the next `##` heading) into a temp file:
   ```
   awk "/^## v$VERSION\$/{flag=1;next}/^## /{flag=0}flag" CHANGELOG.md \
     > /tmp/release-notes-v$VERSION.md
   test -s /tmp/release-notes-v$VERSION.md   # fail if empty
   ```
   If the file is empty, stop — the changelog wasn't updated for this version.

## Steps

Confirm the plan with the user (show the version, tag, and the changelog excerpt) before running any of the mutating commands below. Do NOT run `glab release create` until the user has approved.

### 1. Create the GitLab release

```
glab release create "v$VERSION" \
  --name "v$VERSION" \
  --ref master \
  --notes-file /tmp/release-notes-v$VERSION.md
```

`glab release create` creates the tag if it doesn't exist (tagging the latest commit on `master` via `--ref master`). Do not create the tag separately.

Creating the tag triggers the tag pipeline automatically, which runs `publish gem` (the deploy job) without any further action. There is no manual play step.

### 2. Find and watch the tag pipeline

The pipeline should appear within a few seconds of `glab release create` returning. Find its id:

```
glab ci list --per-page 5   # look for the row with Ref = v$VERSION
```

Record its pipeline id as `PIPELINE_ID`. Sanity-check that the pipeline contains `publish gem` (should appear with status `created`/`pending`/`running`):

```
glab ci list --pipeline-id "$PIPELINE_ID" | grep 'publish gem'
```

Attach to the running job:

```
glab ci trace "publish gem" --pipeline-id "$PIPELINE_ID"
```

`glab ci trace` attaches to the running job and streams output until it finishes. If the job hasn't been scheduled yet, it will wait.

### 3. Verify the gem is on RubyGems

```
curl -sf "https://rubygems.org/api/v1/gems/semver_dialects.json" | jq -r .version
# should print $VERSION
```

Report success or failure to the user. On success the gem is live on rubygems.org as `semver_dialects-$VERSION`.

## Notes

- The `publish gem` job runs `gem build -o semver_dialects.gem semver_dialects.gemspec && gem push semver_dialects.gem` after running `ruby scripts/version_verification.rb "$CI_COMMIT_TAG"`, which asserts that the version triple is in sync **and** that the tag matches `SemverDialects::VERSION`. CI provides the RubyGems credentials — do not attempt to `gem push` locally.
- If the user asks to release a version that has *not* been bumped on master yet, do not invent a bump — stop and ask them to open a version-bump MR first (per the "Releasing" section of `CLAUDE.md`).
- Never use `--no-verify`, `git push --tags`, or any flow that bypasses the `version verification` CI check.

## Rollback

If `publish gem` fails (tag/version mismatch, `gem build` error, RubyGems outage) and you want to undo the release before retrying, **first check whether the gem actually made it to RubyGems**:

```
curl -sf "https://rubygems.org/api/v1/gems/semver_dialects.json" | jq -r .version
```

- If the output is **not** `$VERSION`, the push didn't land — safe to roll back the release and tag, then re-tag from a fresh commit (or fix the mismatch and re-run the skill).
- If the output **is** `$VERSION`, the gem is published. Do not roll back — leave the release/tag in place and cut a new patch version to ship any follow-up fix. RubyGems yanks are a separate, discouraged operation.

To roll back release + tag in one step:

```
glab release delete "v$VERSION" --yes --with-tag

# Verify both are gone:
glab release view "v$VERSION"                                                       # should error "not found"
glab api "/projects/$(glab repo view --output json | jq -r .id)/repository/tags/v$VERSION"   # should 404
```

`glab release delete` alone leaves the tag in place; `--with-tag` (or `-t`) deletes both.
