17 KiB
name, description, triggers, role
| name | description | triggers | role | |||||
|---|---|---|---|---|---|---|---|---|
| release | Use ONLY when ready to cut a release. The Release Manager (Worker) inspects project state (git log, version files, build) to determine the semver bump, generate the changelog, tag, and produce a release artifact. Works on any project state — no pipeline artifacts required. |
|
Producer |
Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按
core/adapters/TERMINOLOGY.md绑定到具体实例。
Release
Prepare and tag a release based on project facts — git history since the last
tag, current version, build status, and dependency health. No dependency on .artifacts/{slug}/
(deprecated — SDLC artifacts now live in the Gitea wiki at {slug}/...;
see _shared/gitea-read-patterns.md) or any pipeline skill outputs.
This SKILL does not deploy. Deployment is project-specific (K8s, Docker, npm publish, etc.) and varies too widely to template. The release artifact produced here is the input to project-specific deployment workflows.
Agent Role
The release is owned and executed by the Release Manager (Worker).
Context compaction: release is a pipeline stage boundary. The main session
compacts at this clean boundary ONLY when a capacity/projection trigger holds,
per core/rules/compact.md §"Stage-boundary compaction" (long multi-stage
runs — DAG Epic orchestration — keep the legacy every-boundary compaction;
short runs — bugfix / DAG task — and standalone runs default to NOT
compacting). The Release Manager itself is single-phase and
artifact-driven (version files, changelog, tag); a mid-run compaction loses
nothing — re-read the release checklist state and git log to resume.
Preconditions
Before starting the release:
- Working tree is clean (
git statushas no uncommitted changes). core/checklists/release.mdis accessible.
Phase 1 — Pre-release Gate
Run each check against the live project. Stop and report failures.
- Clean workspace:
git statusmust show nothing to commit. - Branch: confirm the current branch. Default assumption: release from
mainormaster. If on another branch, note it. - Build: run the project's build command. Must pass.
- Typecheck + Lint: run the project's typecheck and lint. Must pass.
- Tests: run the project's test suite. Must pass.
- Dependency audit: run the project's vulnerability scanner
(e.g.
npm audit,bun audit,pip-audit,cargo audit).- No new HIGH or CRITICAL CVEs block the release.
- Pre-existing HIGH/CRITICAL CVEs do NOT block but MUST be filed as tech-debt before the release lands. File them in TWO TIERS (batching rule, [org-internal #3846] — a single release audit once fanned out to 14 TDs → 14 PRs → 56-84 CI runs on a saturated runner pool): Tier A below is the general mechanical-change batching rule; Tier B and the escape hatch follow it.
Tier A — 通用机械变更并单规则 / Universal mechanical-change batching rule
Origin [org-internal #3846] (audit-batch pilot, generalized in place): the rule covers ANY single-source fan-out of mechanical changes, not just dependency audits. Two source types today: (a) dependency bump audits; (b) docs 批量修订 (batch docs revisions).
规则正文 / Rule text
- Core: mechanical changes of ONE class fanning out from a single source are filed as ONE batch issue + ONE batch PR — never N×issue + N×PR. The batch body keeps one row per item (per-CVE / per-doc mapping), so per-item traceability is unchanged; work the batch as a single PR.
- (a) Dependency bump audits (the [org-internal #3846] origin case): advisories whose
remediation is a plain version bump (lockfile-only diff, no semver-major
jump, no overrides/catalog surgery, no API or adapter fallout) are filed
together via
工单 API(见 TERMINOLOGY)createwithtitle="[{origin}] audit-batch@{version}: mechanical bumps for {N} advisories (TD-{NNN})"and labels[tech_debt_label_id, severity_label_id, kind_bug_label_id](severity = highest in the batch). One row per advisory — CVE/GHSA ID, affected package + from→to version, severity (CVSS), advisory URL, recommended remediation, and a per-advisory Reactivation Trigger ("resolved whennpm audit/bun auditreports no HIGH/CRITICAL for this advisory") — so the release-notesTD-NNN → #NNNNmapping stays per-advisory. - (b) Docs 批量修订 (batch docs revisions): many small same-class docs
corrections discovered in one pass (e.g. a terminology sweep) file as ONE
issue via the same flow,
title="[{origin}] docs-batch: {class} revisions for {N} files", with one row per file (path, correction, reason); same ONE-batch-PR landing. - Landing: when members live on separate branches, compose the single
batch PR via the
land-batchskill (core/skills/land-batch/SKILL.md— cross-branch batch composition, topology B). For dependency-bump batches passbatch-compose --convergent bun.lock(script.gitea/scripts/batch-compose.ts): the regenerable lockfile is exempt from path-overlap admission and lockfile-only conflicts are surgically resolved to the running head's version.
适用边界 / Applicability boundary
- 非mechanical(含判断成分的变更,不并单) — items requiring judgment (wording decisions, behavior/API changes, review-dependent edits) are never batched; file each as its own issue.
- 路径重叠(batch 成员触碰相同文件 → 不并单;唯一豁免 =
可再生共享文件
bun.lock经batch-compose--convergent收敛) — the sole path-overlap exemption is the regenerable shared lockfile under--convergent; every other same-file collision stays un-batched. - semver-major 跳变 / overrides·catalog 手术 / adapter·peer 联动(→ Tier B 独立单,维持 per-package) — these stay per-package Tier B issues. Tier B and the escape hatch (below) survive this generalization unchanged.
降级路径 / Degradation path
- 并单 PR CI 失败 → 按锁文件 hunk 拆包回退(bisect): for dependency batches, bisect by splitting lockfile hunks per package back into per-package PRs — the original Tier A bisect semantics carried over verbatim (同语义,泛化措辞: every mechanical batch degrades the same way).
- Docs batches: split per file back into per-file PRs.
- land-batch composition fallback (exit codes per
.gitea/scripts/batch-compose.ts):3path-overlap → split the batch along the reported pairs (or land the overlapping member 1:1) and re-run per group;4merge conflict /5transport/git error → fall back to 1:1 PRs for the whole batch. - Preflight hedge ([org-internal #3846]): run
bun install --dry-runbefore opening a dependency-batch PR — the resolver accepting the composed version set is a cheap pre-CI rejection of impossible bump combinations.
Tier B and the escape hatch (unchanged by the generalization):
- Tier B — surgery, one issue per package: semver-major jumps,
adapter/peer fallout, or overrides/catalog surgery keep the
per-package issue:
title="[{origin}] {CVE-ID/GHSA-ID} in {package} (TD-{NNN})"with the same labels; body requirements match the Tier A rows. - Escape hatch: either tier may carve a single advisory into its own issue when same-day remediation is required (urgent HIGH/CRITICAL).
De-duplicate against open tech-debt issues (match by
CVE/GHSA ID) before creating. Record the TD-NNN → #NNNN mapping in the
release notes. See verify Phase 5.5 for the tech-debt promotion body
template and _shared/gitea-write-patterns.md Pattern 3.
## Pre-release Gate
| Check | Status |
| ----------------- | ----------------------------------------------------- |
| Clean workspace | ✅/ ❌ |
| Branch | {branch name} |
| Build | ✅/ ❌ |
| Typecheck + Lint | ✅/ ❌ |
| Tests | ✅/ ❌(N passed, M failed) |
| Dependency audit | ✅/ ⚠️ N known CVEs (pre-existing) / ❌N new CVEs |
If any gate fails except known CVEs, stop and report what failed.
Phase 2 — Version Bump
Determine the new version by inspecting git history since the last tag.
-
Find last tag:
git describe --tags --abbrev=0(orgit tag --sort=-v:refname | head -1).- No previous tag? This is the first tracked release. Use the initial commit as
baseline:
git rev-list --max-parents=0 HEAD. After tagging this release, create a retroactive baseline tag (v{base-version}) on the initial commit so future cycles have a clean{tag}..{tag}range. Document the gap in the release report.
- No previous tag? This is the first tracked release. Use the initial commit as
baseline:
-
Read commits since last tag:
git log <last-tag>..HEAD --oneline. -
Read current version from the project's version file (
package.jsonversion,Cargo.toml,VERSION, etc.). -
Categorize commits by change type:
Conventional prefix Semver Examples BREAKING CHANGE:/!:MAJOR API removal, schema change feat:MINOR New feature, new endpoint fix:PATCH Bug fix only perf:PATCH Performance improvement refactor:PATCH Internal restructuring docs:/chore:(skip) Not user-visible If no conventional prefix found, infer from the subject line:
- "add", "implement", "introduce" → MINOR
- "fix", "resolve", "correct" → PATCH
- "remove", "drop", "rename" (public API) → MAJOR
-
Compute bump:
- If any MAJOR commit → bump MAJOR.
- Else if any MINOR commit → bump MINOR.
- Else → bump PATCH.
## Version Bump
**Last tag**: {tag}
**Current version**: {old version}
**New version**: {new version}
**Type**: MAJOR / MINOR / PATCH
**Commits since last tag**: {N}
**Reason**: {justification — e.g. "2 feat + 3 fix → MINOR"}
Phase 3 — Changelog
Generate the changelog from git log <last-tag>..HEAD:
- List all commits. For each, extract:
- Type (from prefix or inferred).
- Scope (if present, e.g.
feat(auth):). - Description (the subject line, past tense, human-readable).
- Group by type:
- Added —
feat:commits. - Changed — modifications to existing behavior (non-breaking).
- Fixed —
fix:commits. - Breaking —
BREAKING CHANGE:or!:commits.
- Added —
- Deduplicate: squash multiple commits for the same change into one entry where it makes narrative sense.
- Read the existing root
CHANGELOG.md(if any) and prepend this release.
Write to root CHANGELOG.md (prepend section).
## [{version}] — {YYYY-MM-DD}
### Added
- {feature} ({commit hash short})
### Changed
- {change} ({hash})
### Fixed
- {bugfix} ({hash})
### Breaking
- {breaking change} ({hash}) — see migration notes above
Phase 4 — Tag & Finalize
- Update version file — write the new version to the project's version manifest.
- Commit:
git addversion file + changelog file, commit with message:release: {version} - Tag:
git tag v{version}(adjust prefix per project convention — check existing tags withgit tag -l). - Verify tag:
git tag -l v{version}confirms the tag exists.
Do not push — the user must explicitly request pushing to remote.
## Release Artifact
- **Version**: {version}
- **Commit**: {commit hash}
- **Tag**: v{version}
- **Changelog**: CHANGELOG.md updated
### Files Changed
| File | Change |
| -------------- | ------------------------- |
| {version file} | {old} → {new} |
| CHANGELOG.md | Prepended {version} |
Phase 4b — Publish Release Artifacts (octopus project, manual)
Octopus-specific. Other projects: substitute your own artifact pipeline — the goal is identical (turn the tag into downloadable assets).
The CI publish pipeline (.gitea/workflows/publish.yml) was retired by
[org-internal #2003] (138/138 historical runs cancelled; the pipeline sat unused for 35+
days). Releasing octopus is now a manual local process. All former CI
steps live in repo scripts, runnable from a maintainer machine with the right
credentials in the environment.
Prerequisites
- Clean checkout of the release commit (tag pushed or about to be pushed).
- Credentials in env:
GITEA_TOKEN— PAT withwrite:repository(release create/undraft, tag push) andwrite:package(container registry). TheCI_PATsecret value is the canonical token.NODE_AUTH_TOKEN— only if publishing to npm.AUR_KEY— only if pushing the AUR package.- Docker logged in to the Gitea container registry:
echo "$GITEA_TOKEN" | docker login <instance-registry-host> -u <user> --password-stdin.
Steps
-
Version + draft release (idempotent — skips if already published):
GITEA_TOKEN=<pat> GH_REPO=Octopus/octopus bun script/version.tsCreates (or refines) the draft Gitea release for
v{version}. -
Build the 12 platform binaries + archives (longest step):
OCTOPUS_VERSION={version} OCTOPUS_RELEASE={release-id} \ GH_REPO=Octopus/octopus GITEA_TOKEN=<pat> \ bun <harness-package>/script/build.tsbuild.tsattaches the 12 release assets to the draft release. -
Publish npm / docker / AUR + git sync:
OCTOPUS_VERSION={version} OCTOPUS_RELEASE={release-id} \ GITEA_TOKEN=<pat> GH_REPO=Octopus/octopus \ NODE_AUTH_TOKEN=<npm-token> \ bun script/publish.tsSet
OCTOPUS_GITEA_ONLY=trueto skip npm/docker/AUR/homebrew. -
Undraft the release (makes it public):
curl -fsS -X PATCH \ "<instance-base-url>/api/v1/repos/Octopus/octopus/releases/{release-id}" \ -H "Authorization: token $GITEA_TOKEN" \ -H "Content-Type: application/json" -d '{"draft": false}' -
Verify assets — the release must carry 12 assets:
curl -sS "<instance-base-url>/api/v1/repos/Octopus/octopus/releases/tags/v{version}" \ -H "Authorization: token $GITEA_TOKEN" | jq '.assets | length' -
Sync the public mirror repo (
Octopus/octopus-release):CI_PAT=<pat> VERSION={version} bash script/sync-public-install.sh CI_PAT=<pat> VERSION={version} bash script/sync-public-assets.sh CI_PAT=<pat> VERSION={version} bash script/verify-public-sync.sh
Notes
- Windows code-signing is not provisioned (no Windows runner / Azure Trusted Signing); the CLI ships unsigned, as before ([org-internal #252]).
- Rollback: if a step fails mid-release, the draft release + tag can be deleted and re-run; every script above is idempotent or safely re-runnable.
- If a future CI pipeline replaces this manual flow, update this section and reference [org-internal #2003] for the retirement rationale.
Phase 5 — Rollback Plan
Document how to undo this release:
- Git rollback:
git tag -d v{version}(if not yet pushed).git revert {commit_hash}(if already merged).- If the release includes DB migrations, confirm the
downmigration exists.
- Data rollback (if applicable):
- For each migration, confirm the
downmigration exists and has been tested. - If the release changes data format without a reversible migration, mark
⚠️ IRREVERSIBLE DATA CHANGE.
- For each migration, confirm the
## Rollback Plan
### Git Rollback
git tag -d v{version}
# or: git revert {hash}
### Data Rollback
- Migration `{name}`: down {exists / NOT FOUND}
- {additional risks}
### Rollback Triggers
| Condition | Threshold | Duration |
| ------------------ | ------------ | -------- |
| P99 latency spike | 2x baseline | 5 min |
| Error rate spike | 1% | 1 min |
| Critical bug | Data loss / security breach | immediate |
Phase 6 — Post-release Smoke Test
- Run the project's build command on the tagged commit. Must pass.
- Run the test suite. Must pass.
- Return to the branch:
git checkout <original-branch>.
Phase 7 — Report
## Release Report
**Version**: {old} → {new} (MAJOR / MINOR / PATCH)
**Tag**: v{version}
**Commits**: {N} since last tag ({feat} features, {fix} fixes)
**Typecheck**: ✅| Lint: ✅| Tests: ✅
### Changelog
{paste changelog section}
### Release Artifacts
- Commit: {hash} — `release: {version}`
- Tag: v{version}
- Changelog: CHANGELOG.md updated
---
**Deploy**: {manual step — "merge to main triggers CI", etc.}
**Rollback**: see Phase 5 above
**Next**: `core/skills/retrospective/SKILL.md` (optional — run on any project state)
References
core/checklists/release.md— Release checklist- Semver spec: https://semver.org
- Conventional Commits: https://www.conventionalcommits.org