Skip to content

Commit 903702a

Browse files
Add release agent skill (#66)
* Add release skill for agent-assisted releases Agent Skills (agentskills.io open standard) folder with step-by-step release procedure: version bump, test, Clojars deploy, tagging, GitHub Release, CI verification, and troubleshooting guidance. * Address PR review feedback on release skill - Clarify prerequisites: version bump via PR, release runs from master - Fix CI trigger descriptions: clojure.yml triggers on push to master (not on release), sync-upstream and native-image on release:published - Fix reflection check command: use require instead of -m to avoid runtime error from missing CLI arguments * Use jq instead of python3 for Clojars verification Replace python3 JSON parsing with jq, which is already a more common CLI dependency and doesn't need to be listed separately. * Remove hard-coded assertion count from release skill The count drifts as upstream uap-core fixtures change. * Use clojure -M:test in release skill to match CI and README clojure -T:build test just shells out to clojure -M:test anyway. Using the same invocation as CI avoids divergence and confusion. * Distinguish CI (merge-triggered) from post-release workflows Reorder and reword step 8 to make clear that clojure.yml already ran on the PR merge to master, while sync-upstream and native-image trigger on the release:published event. * Address remaining review comments - Add submodule init to prerequisites (tests depend on uap-core) - Add jq to compatibility header and prerequisites - Clarify GitHub Release body is conditional (changelog link only when a previous tag exists) - Replace 'See PR #64' with in-repo reference to core.clj comments * Replace line-number reference with function/comment description Line numbers go stale; reference the with-open reflection comment in -main by name instead. * Clarify UPSTREAM_PUSH_TOKEN is only for post-release CI workflows The local release command only needs Clojure CLI, gh auth, and CLOJARS credentials. UPSTREAM_PUSH_TOKEN is used by sync-upstream and native-image workflows, not by clojure -T:build release. * Add git and curl to compatibility prerequisites Both are used in the procedure: git for branching/tagging/submodules, curl for Clojars verification. * Document Agent Skills in README and CLAUDE.md - README: add Agent Skills section with table of available skills - CLAUDE.md: add Skills section pointing to release skill
1 parent 88ccc76 commit 903702a

3 files changed

Lines changed: 154 additions & 0 deletions

File tree

.github/skills/release/SKILL.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
name: release
3+
description: "Release uap-clj to Clojars and GitHub. Use when bumping version, publishing a release, deploying to Clojars, creating a GitHub Release, or cutting a new version. Covers version bump, testing, Clojars deploy, tagging, GitHub Release, native-image CI verification, and upstream sync."
4+
compatibility: "Requires Clojure CLI, git, gh CLI, curl, jq, CLOJARS_USERNAME and CLOJARS_PASSWORD env vars. UPSTREAM_PUSH_TOKEN GitHub secret is needed only for post-release upstream mirroring (sync-upstream/native-image workflows)."
5+
metadata:
6+
author: russellwhitaker
7+
version: "1.0"
8+
---
9+
10+
# Release Workflow
11+
12+
Complete procedure for releasing a new version of uap-clj.
13+
14+
## Prerequisites
15+
16+
- Version bump done on a feature branch via PR (never commit directly to `master`)
17+
- Clean working tree on `master` after the version-bump PR is merged
18+
- `gh` CLI authenticated
19+
- `jq` installed (used for Clojars verification)
20+
- `CLOJARS_USERNAME` and `CLOJARS_PASSWORD` environment variables set
21+
- uap-core submodule initialized (`git submodule update --init --recursive`)
22+
- All tests passing
23+
24+
## Procedure
25+
26+
### 1. Bump the version
27+
28+
Edit `build.clj` and update the `version` string:
29+
30+
```clojure
31+
(def version "X.Y.Z")
32+
```
33+
34+
This single value drives the JAR filename, POM version, git tag, and GitHub Release name.
35+
36+
### 2. Run the full test suite
37+
38+
```sh
39+
clojure -M:test
40+
```
41+
42+
All tests must pass. Do not proceed if any fail.
43+
44+
### 3. Check formatting
45+
46+
```sh
47+
clojure -M:cljstyle-check
48+
```
49+
50+
Fix any issues with `clojure -M:cljstyle-fix` before continuing.
51+
52+
### 4. Check for outdated dependencies
53+
54+
```sh
55+
clojure -T:build outdated
56+
```
57+
58+
Address any critical updates before releasing.
59+
60+
### 5. Commit the version bump
61+
62+
```sh
63+
git add build.clj
64+
git commit -m "Bump version to X.Y.Z"
65+
git push origin <branch>
66+
```
67+
68+
### 6. Merge to master
69+
70+
Create a PR, get it merged, then pull master locally:
71+
72+
```sh
73+
git checkout master
74+
git pull origin master
75+
```
76+
77+
### 7. Run the release task
78+
79+
```sh
80+
clojure -T:build release
81+
```
82+
83+
This single command performs:
84+
1. **Pre-flight checks**: fetches remote tags, verifies the tag doesn't already exist
85+
2. **Clojars deploy**: builds the JAR and deploys to Clojars (uses `-Djava.net.preferIPv4Stack=true` from `:build` alias)
86+
3. **Git tag**: creates annotated tag `vX.Y.Z`
87+
4. **Push tag**: pushes the tag to origin
88+
5. **GitHub Release**: creates a release (includes a "Full Changelog" compare link when a previous tag exists; otherwise uses a plain "Release vX.Y.Z" body)
89+
90+
### 8. Verify CI and post-release workflows
91+
92+
#### CI (`clojure.yml`) — already ran on merge to `master`
93+
- Triggered when the version-bump PR was merged (push to `master`)
94+
- Runs tests, formatting check, and outdated check
95+
- Should have passed (you already verified locally in steps 2-4)
96+
97+
After the release task creates the GitHub Release, two additional workflows trigger:
98+
99+
#### Upstream sync (`sync-upstream.yml`) — triggers on `release: published`
100+
- Mirrors the tag and GitHub Release to `ua-parser/uap-clj`
101+
- Check: `gh api repos/ua-parser/uap-clj/releases/tags/vX.Y.Z --jq '.html_url'`
102+
103+
#### Native image (`native-image.yml`) — triggers on `release: published`
104+
- Builds binaries for Linux (amd64) and macOS (arm64)
105+
- Uploads them to the GitHub Release on both origin and upstream
106+
- Polls up to 5 minutes for the upstream release to appear
107+
- Check: look for `uap-clj-linux-amd64` and `uap-clj-macos-arm64` assets on the release page
108+
109+
### 9. Verify Clojars
110+
111+
Confirm the artifact is available:
112+
113+
```sh
114+
curl -s "https://clojars.org/api/artifacts/uap-clj/uap-clj" | jq -r '.latest_release'
115+
```
116+
117+
Expected output: `X.Y.Z`
118+
119+
## Troubleshooting
120+
121+
### Clojars deploy fails with "Broken pipe"
122+
The `:build` alias already includes `-Djava.net.preferIPv4Stack=true` in its JVM opts. If you still see this, verify the setting is present in `deps.edn` under `:build :jvm-opts`.
123+
124+
### Tag already exists
125+
The release task checks for existing tags before deploying. If you need to re-release, delete the tag first:
126+
```sh
127+
git tag -d vX.Y.Z
128+
git push origin :refs/tags/vX.Y.Z
129+
```
130+
Then also delete the GitHub Release via the web UI or `gh release delete vX.Y.Z`.
131+
132+
### Native image build fails with reflection errors
133+
Check for reflection warnings:
134+
```sh
135+
clojure -M -e "(set! *warn-on-reflection* true) (require 'uap-clj.core)" 2>&1 | grep "Reflection warning"
136+
```
137+
This loads the namespace without invoking `-main` (which requires CLI arguments). Add type hints (`^String`, `^java.io.Writer`) to resolve any warnings. See the `with-open` native-image reflection comment in `uap-clj.core/-main` for precedent.
138+
139+
### Upstream release not found by native-image workflow
140+
The `native-image.yml` workflow polls for the upstream release (created by `sync-upstream.yml`) for up to 5 minutes. If `sync-upstream.yml` is slow or fails, check its run logs and re-run if needed. The native-image workflow will emit a warning and skip upstream upload if the release never appears.

CLAUDE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,7 @@
2323

2424
- Clojars deploy requires `-Djava.net.preferIPv4Stack=true` (already in `:build` alias JVM opts)
2525
- Use `clojure -T:build deploy` to publish to Clojars
26+
27+
## Skills
28+
29+
- Release workflow: `.github/skills/release/SKILL.md`

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,16 @@ $ clojure -M:bench
9696

9797
This benchmarks `browser`, `os`, `device`, and `useragent` lookups individually and in batch.
9898

99+
### Agent Skills
100+
101+
This project includes [Agent Skills](https://agentskills.io/) — machine-readable workflow instructions that AI coding agents (Claude Code, VS Code Copilot, Roo Code, and others) discover and follow automatically. Skills live in `.github/skills/` and encode project-specific procedural knowledge so agents can execute complex workflows (like releasing to Clojars) without manual prompting.
102+
103+
Available skills:
104+
105+
| Skill | Description |
106+
|-------|-------------|
107+
| [`release`](.github/skills/release/SKILL.md) | Full release workflow: version bump, testing, Clojars deploy, tagging, GitHub Release, and post-release CI verification |
108+
99109
### Specs
100110

101111
`clojure.spec` definitions for all parsed output maps are available in `uap-clj.spec`. To use them:

0 commit comments

Comments
 (0)