diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md new file mode 100644 index 0000000..5dfe318 --- /dev/null +++ b/.claude/skills/release/SKILL.md @@ -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). + +## 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 +``` + +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"}` + +### 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 + + + + 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 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..285dc71 --- /dev/null +++ b/CHANGELOG.md @@ -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 diff --git a/Makefile b/Makefile index e2723e8..31fe46a 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ -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 && \ + git push origin "$$tag" && \ + gh release create "$$tag" --title "v$$tag" --notes "$$(sed -n "/## \[$$tag\]/,/## \[/p" CHANGELOG.md | sed '$$d' | tail -n +2)" && \ + echo "Released and tagged as $$tag"