Skip to main content

release

Cut a Ducko release: update CHANGELOG.md on main, then push the X.Y.Z tag that triggers the release.yml CI workflow (build, sign, notarize, DMG, Sparkle appcast, GitHub Release). Use when the user asks to "cut a release", "publish a new version", "prepare a release", "tag a release", or "ship it".

소스 정보

저장소
tobihagemann/ducko
최근 소스 활동
2026년 9월 22일 15:06
감지된 SKILL.md 언어
영어
스타
3
포크
0

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
2 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
release
description
Cut a Ducko release: update CHANGELOG.md on main, then push the X.Y.Z tag that triggers the release.yml CI workflow (build, sign, notarize, DMG, Sparkle appcast, GitHub Release). Use when the user asks to "cut a release", "publish a new version", "prepare a release", "tag a release", or "ship it".
# Release Cut a release straight from `main`: update the changelog, then push an `X.Y.Z` tag. CI does the build — the tag triggers `.github/workflows/release.yml`, which builds, signs, notarizes, packages a DMG, regenerates the Sparkle appcast, and publishes a GitHub Release whose body comes from `CHANGELOG.md`. Signing and Sparkle secrets are already configured in CI. Releases are built exclusively via GitHub Actions — never locally. There is no version file to bump — the marketing version comes from the tag, and `CHANGELOG.md` is the only prep artifact (`version.env` holds names, not the version; the build number is the commit count). ## Step 1: Determine the version If the user did not give one, infer the next `X.Y.Z` from the latest tag and propose a patch/minor/major bump, then confirm: ```bash git tag --sort=-v:refname | head -1 ``` ## Step 2: Update the changelog Make sure `main` is clean and current (`git checkout main && git pull origin main`). `CHANGELOG.md` keeps a running `## [Unreleased]` section, so a release completes that section and then promotes it to a version heading. **Scope: user-facing only, framed as a net delta.** `CHANGELOG.md` becomes the GitHub Release notes and the Sparkle update notes, so it must describe only what a user experiences between releases. Before promoting, review every `[Unreleased]` entry and: - **Drop non-user changes.** CI, scripts, skills, tests, and refactors don't ship to users. `DuckoCLI`-only changes belong only when the CLI is part of what the release ships. - **State the net delta from the last released version, not the development history.** The `[Unreleased]` section accumulates commit-by-commit, so it collects entries that only make sense relative to an intermediate unreleased build (e.g. "no longer does X" / "removed the Y glitch" where X or the Y bug never shipped). Rewrite or drop those so each entry reads as a change the previous release's users will actually notice. - **Check each entry's subject against the last tag, don't just scan the phrasing.** A positively-phrased entry hides the same trap: a fix for a feature that arrived in this same cycle reads like something users would notice, but nobody on the previous release ever hit that bug. For every entry, confirm the thing it changes or fixes existed at the last tag — `git show <last-tag>:<path>`, plus `git log --follow -- <path>` when the file moved. When it did not, fold the entry into whatever introduced the feature, or drop it. One entry per net user-visible change, not one per commit. - **Describe the experience, not the mechanism.** "Rooms remember their bookmark autojoin flag" is what a user sees; the stanza or storage detail belongs in the commit, not the release notes. 1. **Complete `[Unreleased]` via `/update-changelog`.** Run it to capture anything missing, then double-check completeness against `git log <last-tag>..HEAD --oneline` — that range always includes the prior `Update appcast.xml for <last>` commit (CI pushes it to `main` after the tag) as noise, and real changes can land *after* it, so don't stop scanning there. 2. **Promote** by inserting the version heading (`## [X.Y.Z] - YYYY-MM-DD`, today's date) under the kept-empty `## [Unreleased]` heading so the accumulated entries fall under the new version, add the `[X.Y.Z]: https://github.com/tobihagemann/ducko/compare/<last-tag>...X.Y.Z` link reference (`.../releases/tag/X.Y.Z` for the first release), and repoint `[Unreleased]` to `compare/X.Y.Z...HEAD` (mirror the previous `Prepare release X.Y.Z` commit's changelog diff). `release.yml` extracts this version section as the GitHub Release notes. ## Step 3: Commit and push to main ```bash git add CHANGELOG.md git commit -m "Prepare release X.Y.Z" git push origin main ``` The tag must land on a `main` commit: `release.yml` signs only commits reachable from `origin/main`, and it reads `CHANGELOG.md` at the tagged commit. ## Step 4: Tag and trigger the release Dispatch a dry run and wait for it to pass before tagging when the signing or packaging inputs changed since the last tag: ```bash git diff --stat <last-tag>..HEAD -- Package.swift Resources/Entitlements.plist Scripts/package_app.sh Scripts/create_dmg.sh Scripts/release.sh .github/workflows/release.yml ``` **Skip** the dry run when that diff is empty. It is the only check that signs, notarizes, and staples the real arm64 build, since CI's release smoke test stubs those tools: ```bash gh workflow run release.yml --ref main -f version=X.Y.Z -f dry_run=true gh run list --workflow release.yml --limit 1 # run ID, when the dispatch prints no URL gh run watch <run-id> --exit-status ``` If the dry run fails, fix the cause on `main`, push, and dispatch again. Tag only after a dry run passes. A passing dry run uploads its artifacts for inspection. Tag the latest `main` commit (the changelog commit, or a later dry-run fix) and push the tag — this is what starts CI: ```bash git tag -a X.Y.Z -m "X.Y.Z" git push origin X.Y.Z ``` `release.yml` then builds, signs, notarizes, generates the appcast, creates the `X.Y.Z` GitHub Release (notes from the changelog section), and commits the updated `appcast.xml` back to `main`. ## Step 5: Finish ```bash git pull origin main # pick up the appcast commit CI pushed gh release view X.Y.Z # verify the Release and its assets gh run list --commit "$(git rev-parse X.Y.Z^{commit})" # every workflow on the tagged commit ``` `ci.yml` runs on the tagged commit from its push to `main`, separately from `release.yml`, so a passing release run does not cover it. Report the release as done only once both have passed, waiting on an unfinished run with `gh run watch <run-id> --exit-status`. When the CI run failed, name the failure and fix it on `main`. ## Notes - Reserve each `X.Y.Z` for one set of artifacts — re-tagging a published version reuses the Release and appcast URLs for different content. - The Sparkle EdDSA private key lives in the login Keychain under `generate_keys --account ducko` and is backed up in 1Password. `.build/artifacts/sparkle/Sparkle/bin/generate_keys --account ducko -p` must print the `SUPublicEDKey` in `Scripts/package_app.sh`. GitHub cannot show the `SPARKLE_PRIVATE_KEY` secret, so when its contents are in doubt, re-set it from that key (`generate_keys --account ducko -x <file>`, then `gh secret set SPARKLE_PRIVATE_KEY < <file>` and delete the file) rather than generating a new pair. A mismatched pair still publishes a release, but every later update fails signature verification for its users. - If CI is unavailable, see [references/local-release.md](references/local-release.md) for the manual release procedure.
GitHub에서 보기