# Release

> Create and safely publish a new GitHub release for this repository through a verified draft build, then confirm the related GitHub Actions run. Use when the user says "/release", "make a release", "create a GitHub release", "publish a release", "tag a release", "major release", "feature release", "minor release", "patch release", "start the release action", or wants firmware release assets generated by GitHub Actions.

- Skill: `jtenniswood/release` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add jtenniswood/release`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jtenniswood/release/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: jtenniswood (https://skillmd.com/u/jtenniswood)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/jtenniswood/release

---


# GitHub Release

Create a draft GitHub release from `main`, dispatch the `Build Release` action,
and watch it build and verify the complete firmware distribution. The workflow
uploads assets while the release is private and publishes it only after every
asset has been checked.

## Release Flow

### 1. Check Readiness

Use `main` unless the user explicitly asks for a different branch.

```bash
git status --short --branch
git fetch origin --tags --prune
git switch main
git pull --ff-only origin main
gh auth status
```

Do not create a release from uncommitted or unpushed local changes. If local
changes are release-related, commit and push them first. If unrelated changes
are present, explain that they block a clean release and ask how to proceed.

Check recent releases and tags before choosing the new version:

```bash
gh release list --limit 10
git tag --list 'v*' --sort=-v:refname | head -20
```

### 2. Choose the Version

Use long semantic version tags: `vMAJOR.MINOR.PATCH`.

Do not create short release tags such as `v1.0` or `v1.1`. If the user gives a
short tag, normalize it to the long form and confirm before publishing:

```text
v1.0 -> v1.0.0
v1.1 -> v1.1.0
```

If the user gives a full tag like `v1.2.3`, use that exact tag. If the user
asks for a release type instead of a tag, calculate the next version from the
latest stable `vX.Y.Z` tag and confirm the calculated tag before publishing.
Ignore pre-release tags such as `v1.2.3-beta.1` when choosing the stable base.

Version bump rules:

```text
major            v1.2.3 -> v2.0.0
feature or minor v1.2.3 -> v1.3.0
patch            v1.2.3 -> v1.2.4
```

If there is no existing stable release, treat the first stable release as
`v1.0.0` unless the user asks for a different full tag.

### 3. Prepare Web-Asset Compatibility

Before creating the tag, add it to the immutable web bundle's compatibility
list. This makes the hosted bridge select the declared bundle for the new
firmware instead of falling back to the editor embedded in the device.

Set the selected tag before preparing the bundle, for example:

```bash
# Stable release:
TAG="vX.Y.Z"
# Pre-release: TAG="vX.Y.Z-beta.N"
```

```bash
git switch -c "jtenniswood/prepare-web-assets-${TAG#v}"
python3 scripts/prepare_release_web_assets.py "$TAG"
python3 scripts/build.py
python3 scripts/build.py --check
npm run check:release-preflight
git add scripts/build.py docs/public/webserver
git commit -m "Prepare web assets for $TAG"
git push -u origin HEAD
gh pr create --base main --title "Prepare web assets for $TAG" --body "Adds $TAG to the declared immutable web bundle compatibility list."
```

Do this before tagging: the tag must point at the source revision that already
declares its compatible web bundle. Have that preparation PR reviewed and
merged, then return to `main` and pull the merged revision before creating the
release tag. The helper retains `dev`, the five current stable releases, and
the latest pre-release only.

### 4. Create the Tag and Draft Release

Push that exact release tag so every workflow job can check out one immutable
source revision. Then create a draft release for the existing tag. Do not
publish the draft manually: the workflow owns that final transition.

```bash
git tag -a "$TAG" -m "Release $TAG" origin/main
git push origin "$TAG"
```

Stable release:

```bash
gh release create "$TAG" \
  --verify-tag \
  --draft \
  --notes "Firmware is being built and verified. This draft will publish automatically when complete." \
  --fail-on-no-commits
```

Pre-release:

```bash
gh release create "$TAG" \
  --verify-tag \
  --draft \
  --notes "Firmware is being built and verified. This draft will publish automatically when complete." \
  --fail-on-no-commits \
  --prerelease \
  --latest=false
```

If a draft already exists, reuse it only after confirming it is still a draft
and its tag exists on the remote:

```bash
git ls-remote --exit-code --tags origin "refs/tags/$TAG"
gh release view "$TAG" --json isDraft,tagName,url
```

Do not close GitHub issues as part of this workflow unless the user explicitly
asks; they prefer to test before issues are closed.

### 5. Start and Watch the Release Action

Dispatch `Build Release` with the draft tag. Manual dispatch is the only
supported release path because a draft release must remain private during the
build.

```bash
gh workflow run release.yml --ref "$TAG" -f release_tag="$TAG"
gh run list \
  --workflow release.yml \
  --event workflow_dispatch \
  --limit 10 \
  --json databaseId,status,conclusion,createdAt,headBranch,displayTitle,url
```

Find the newest run for the release tag, then watch it:

```bash
gh run watch <run-id> --exit-status
```

If the workflow fails, the release should remain a draft. Report the failed job
and do not publish it manually.

### 6. Verify Outputs

After `Build Release` succeeds, confirm the release is public and has the
expected assets:

```bash
gh release view "$TAG" \
  --json tagName,url,isDraft,isPrerelease,assets \
  --jq '{tagName, url, isDraft, isPrerelease, assets: [.assets[].name]}'
```

Expected release assets:

```text
esp32-p4-86.factory.bin
esp32-p4-86.manifest.json
esp32-p4-86.ota.bin
guition-esp32-p4-jc1060p470.factory.bin
guition-esp32-p4-jc1060p470.manifest.json
guition-esp32-p4-jc1060p470.ota.bin
guition-esp32-p4-jc1060p470-v2.factory.bin
guition-esp32-p4-jc1060p470-v2.manifest.json
guition-esp32-p4-jc1060p470-v2.ota.bin
guition-esp32-p4-jc4880p443.factory.bin
guition-esp32-p4-jc4880p443.manifest.json
guition-esp32-p4-jc4880p443.ota.bin
guition-esp32-p4-jc8012p4a1.factory.bin
guition-esp32-p4-jc8012p4a1.manifest.json
guition-esp32-p4-jc8012p4a1.ota.bin
guition-esp32-p4-jc8012p4a1-v2.factory.bin
guition-esp32-p4-jc8012p4a1-v2.manifest.json
guition-esp32-p4-jc8012p4a1-v2.ota.bin
guition-esp32-s3-4848s040.factory.bin
guition-esp32-s3-4848s040.manifest.json
guition-esp32-s3-4848s040.ota.bin
```

The `Deploy Docs` workflow is configured to run after a successful
`Build Release`. Check it if the user asks about docs or firmware download
availability:

```bash
gh run list --workflow pages.yml --event workflow_run --limit 5
```

## Report Back

Summarize in plain language:

- Release tag and GitHub release URL
- `Build Release` run URL and current result
- Whether the verified draft was published and release notes were installed
- Whether the expected firmware assets are attached
- Any docs deployment run URL if checked
- Any action needed from the user, especially if a run failed

