Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
name: release
description: Prepare and publish a new release. Updates CHANGELOG.md and README.md with version information, analyzes git commits to determine semantic version, and guides through the release process.
---

# Release Skill

You are helping to prepare a new release of the elli_openapi library.

## Step 1: Detect Current Version

Read the CHANGELOG.md to determine the current version (the most recent version listed at the top).

Comment on lines +12 to +13

Copilot AI Mar 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This skill instructs the user to read/update CHANGELOG.md, but the repo currently doesn't include that file. Either add a CHANGELOG.md to the repository (and define its expected format) or adjust the skill to use whatever release notes source this repo actually maintains.

Copilot uses AI. Check for mistakes.
## Step 2: Analyze Changes

Check recent git commits since the last release:
```bash
git log --oneline --since="$(git log -1 --format=%ai $(git describe --tags --abbrev=0 2>/dev/null || echo HEAD))" 2>/dev/null || git log --oneline -20

Copilot AI Mar 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The git command in Step 2 is overly complex and uses --since=<date of last tag commit>, which can include unrelated commits (e.g., if commits were authored before that date but merged after). A more reliable way to list changes since the last tag is a range like <last_tag>..HEAD (with a fallback when no tags exist).

Suggested change
git log --oneline --since="$(git log -1 --format=%ai $(git describe --tags --abbrev=0 2>/dev/null || echo HEAD))" 2>/dev/null || git log --oneline -20
git describe --tags --abbrev=0 >/dev/null 2>&1 && git log --oneline "$(git describe --tags --abbrev=0)"..HEAD || git log --oneline -20

Copilot uses AI. Check for mistakes.
```

Also check git diff to see what files have changed:
```bash
git diff $(git describe --tags --abbrev=0 2>/dev/null || echo HEAD~20)..HEAD --stat
```

Analyze the commits and changes to understand:
- Are there breaking changes?
- Are there new features?
- Are there bug fixes?
- Are there documentation updates?

## Step 3: Suggest Version Bump

Based on semantic versioning:
- **Major** (X.0.0): Breaking changes, incompatible API changes
- **Minor** (0.X.0): New features, backwards-compatible functionality
- **Patch** (0.0.X): Bug fixes, backwards-compatible fixes

Use the AskUserQuestion tool to present three options showing what the new version would be for each choice (major, minor, or patch). Indicate which one you recommend based on the changes.

## Step 4: Generate Changelog Entries

Based on the git commits and changes, draft changelog entries organized into sections:
- **Added**: New features
- **Changed**: Changes in existing functionality
- **Fixed**: Bug fixes
- **Deprecated**: Soon-to-be removed features
- **Removed**: Removed features
- **Security**: Security fixes

Only include sections that have entries. Keep entries concise and user-focused.

## Step 5: Update Files

After the user selects a version bump, update these files:

### README.md
- Find and update the installation example: `{elli_openapi, "~> X.Y.Z"}`

Copilot AI Mar 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README guidance here doesn't match the current README.md in this repo: there is no Hex dependency example like {elli_openapi, "~> X.Y.Z"} (README currently shows a git dependency). Update the skill to match the actual installation instructions you want users to maintain.

Suggested change
- Find and update the installation example: `{elli_openapi, "~> X.Y.Z"}`
- Find and update the installation example that uses a git dependency, for example:
```elixir
{:elli_openapi, github: "elli-lib/elli_openapi", tag: "vX.Y.Z"}

Copilot uses AI. Check for mistakes.

### CHANGELOG.md
- Add a new version section at the top (after the header, before the previous version)
- Use today's date in ISO format (YYYY-MM-DD)
- Include the changelog entries you generated
- Format:
```markdown
## [X.Y.Z] - YYYY-MM-DD

### Added
- New feature 1
- New feature 2

### Changed
- Change 1
- Change 2

### Fixed
- Bug fix 1

```

## Step 6: Create Release Branch and PR

Use the AskUserQuestion tool to ask whether to create a new release branch or commit to the current branch:
- **New branch** (`release/X.Y.Z`): creates a clean branch for the release PR
- **Current branch**: commits directly to whatever branch is currently checked out

Then proceed based on the answer:

**If new branch:**
1. Create and switch to a release branch:
```bash
git checkout -b release/X.Y.Z
```

**Either way:**
2. Stage and commit the updated files:
```bash
git add CHANGELOG.md README.md
git commit -m "Prepare release X.Y.Z"
```

**If new branch:**
3. Push and create a PR:
```bash
git push -u origin release/X.Y.Z
gh pr create --title "Release X.Y.Z" --body "$(cat <<'EOF'
## Release X.Y.Z

<paste the changelog entries for this version here>

EOF
)"
```
Return the PR URL to the user.

**If current branch:**
3. Push only — no PR needed since the release commit will be included in the branch's existing PR:
```bash
git push
```
Tell the user the release commit has been pushed to the current branch.

## Important Notes

- If `src/elli_openapi.app.src` uses a fixed version like `{vsn, "0.1.0"}`, update it to the new release version but do NOT change its versioning strategy (e.g. do not switch it to `{vsn, "git"}`) unless explicitly instructed by the user
- Do NOT modify `rebar.config` - it doesn't contain version information
- Do NOT run `make release` - the user will do that after the PR is merged
- Try to write clear, user-focused changelog entries
- Group related changes together
- Omit internal refactorings unless they significantly impact users
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.0] - 2026-03-27

### Added
- Type-safe HTTP API routing using Elli with automatic OpenAPI documentation generation
- Integration with [Spectra](https://github.com/andreashasse/spectra) for Erlang type-based request/response validation
- URL routing with path parameter extraction (e.g. `<<"/api/users/{userId}">>`)
- Support for multiple HTTP status codes per endpoint via union types in function specs
- Request and response body validation and encoding (JSON and text/plain)
- Request header validation against declared function specs
- Swagger UI served at `/api-docs`
- Redoc UI served at `/redoc`
- OpenAPI spec stored in `persistent_term` for fast in-memory access
32 changes: 31 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: all compile format test cover clean doc hank format_verify build-test dialyzer xref type_check check_app_calls hex
.PHONY: all compile format test cover clean doc hank format_verify build-test dialyzer xref type_check check_app_calls hex release

all: compile format test cover

Expand Down Expand Up @@ -50,3 +50,33 @@ doc:
hex:
rebar3 hex build
rebar3 hex publish

release:
@branch=$$(git rev-parse --abbrev-ref HEAD); \
if [ "$$branch" != "main" ]; then \
echo "Error: You must be on the main branch to release (currently on '$$branch')."; \
exit 1; \
fi
@git fetch origin main --quiet
@if [ "$$(git rev-parse HEAD)" != "$$(git rev-parse origin/main)" ]; then \
echo "Error: Local main is not in sync with origin/main. Please pull or push first."; \
git status -sb; \
exit 1; \
fi
@if [ -n "$$(git status --porcelain)" ]; then \
echo "Error: There are uncommitted changes. Please commit or stash them before releasing."; \
git status --short; \
exit 1; \
fi
@echo "Last 5 tags:"
@git tag --sort=-version:refname | head -n 5
@echo ""
@read -r -p "Enter the next tag (e.g., v1.0.0): " tag && [ -n "$$tag" ] || { echo "Tag cannot be empty. Aborted."; exit 1; }; \
read -r -p "Did you update the README install instructions _AND_ CHANGELOG.md? (Y/N) " a && [ "$$a" = "Y" ] || { echo "Aborted."; exit 1; }; \
git tag "$$tag" && \
rebar3 compile && \
rebar3 hex build && \
rebar3 hex publish && \
Comment thread
andreashasse marked this conversation as resolved.
Comment thread
andreashasse marked this conversation as resolved.
git push origin "$$tag" && \
gh release create "$$tag" --title "v$$tag" --notes "$$(sed -n "/## \[$$tag\]/,/## \[/p" CHANGELOG.md | sed '$$d' | tail -n +2)" && \
Comment thread
andreashasse marked this conversation as resolved.

Copilot AI Mar 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sed pattern that extracts the changelog section uses the raw tag in a regex (/## \[$$tag\]/), so tags containing regex metacharacters (notably . in semantic versions) can match unintended headings. Also, when the matching section is the last entry in the file (no following ## [), sed '$d' will drop the final line of the notes. Consider a parser that treats the tag literally and handles EOF correctly (e.g., awk with exact string match, or perl -pe), or rely on gh release create --notes-file/--generate-notes.

Suggested change
gh release create "$$tag" --title "v$$tag" --notes "$$(sed -n "/## \[$$tag\]/,/## \[/p" CHANGELOG.md | sed '$$d' | tail -n +2)" && \
gh release create "$$tag" --title "v$$tag" --notes "$$(awk -v tag="$$tag" ' \
$$0 == "## [" tag "]" {found=1; next} \
found && /^## \[/ {exit} \
found {print} \
' CHANGELOG.md)" && \

Copilot uses AI. Check for mistakes.

Copilot AI Mar 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tag prompt suggests entering something like v1.0.0, but the release title is set to v$$tag, which will produce titles like vv1.0.0. Decide whether tags include the leading v and keep it consistent across the prompt, the tag name, and the GitHub release title.

Suggested change
gh release create "$$tag" --title "v$$tag" --notes "$$(sed -n "/## \[$$tag\]/,/## \[/p" CHANGELOG.md | sed '$$d' | tail -n +2)" && \
gh release create "$$tag" --title "$$tag" --notes "$$(sed -n "/## \[$$tag\]/,/## \[/p" CHANGELOG.md | sed '$$d' | tail -n +2)" && \

Copilot uses AI. Check for mistakes.
echo "Released and tagged as $$tag"
Loading