2026-09-15 08:41:51 +08:00
|
|
|
|
---
|
|
|
|
|
|
name: release
|
|
|
|
|
|
description: >
|
|
|
|
|
|
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
|
2026-09-16 13:13:18 +08:00
|
|
|
|
project state — no pipeline artifacts required.
|
2026-09-15 08:41:51 +08:00
|
|
|
|
triggers:
|
|
|
|
|
|
- release
|
|
|
|
|
|
- cut release
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- 发版
|
2026-09-15 08:41:51 +08:00
|
|
|
|
- publish release
|
|
|
|
|
|
- ship release
|
|
|
|
|
|
role: Producer
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
> Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按 `core/adapters/TERMINOLOGY.md` 绑定到具体实例。
|
2026-09-15 08:41:51 +08:00
|
|
|
|
# Release
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
Prepare and tag a release based on project facts — git history since the last
|
2026-09-15 08:41:51 +08:00
|
|
|
|
tag, current version, build status, and dependency health. No dependency on `.artifacts/{slug}/`
|
2026-09-16 13:13:18 +08:00
|
|
|
|
(**deprecated** — SDLC artifacts now live in the Gitea wiki at `{slug}/...`;
|
2026-09-15 08:41:51 +08:00
|
|
|
|
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,
|
2026-09-16 13:13:18 +08:00
|
|
|
|
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
|
2026-09-15 08:41:51 +08:00
|
|
|
|
compacting). The Release Manager itself is single-phase and
|
|
|
|
|
|
artifact-driven (version files, changelog, tag); a mid-run compaction loses
|
2026-09-16 13:13:18 +08:00
|
|
|
|
nothing — re-read the release checklist state and git log to resume.
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Preconditions
|
|
|
|
|
|
|
|
|
|
|
|
Before starting the release:
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] Working tree is clean (`git status` has no uncommitted changes).
|
|
|
|
|
|
- [ ] `core/checklists/release.md` is accessible.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
## Phase 1 — Pre-release Gate
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
Run each check against the live project. Stop and report failures.
|
|
|
|
|
|
|
|
|
|
|
|
1. **Clean workspace**: `git status` must show nothing to commit.
|
|
|
|
|
|
2. **Branch**: confirm the current branch. Default assumption: release from
|
|
|
|
|
|
`main` or `master`. If on another branch, note it.
|
|
|
|
|
|
3. **Build**: run the project's build command. Must pass.
|
|
|
|
|
|
4. **Typecheck + Lint**: run the project's typecheck and lint. Must pass.
|
|
|
|
|
|
5. **Tests**: run the project's test suite. Must pass.
|
|
|
|
|
|
6. **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
|
2026-09-16 13:13:18 +08:00
|
|
|
|
rule, [org-internal #3846] — a single release audit once fanned out to 14 TDs → 14 PRs →
|
2026-09-15 08:41:51 +08:00
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
|
|
<a id="phase-1-tier-a"></a>
|
2026-09-16 13:13:18 +08:00
|
|
|
|
### Tier A — 通用机械变更并单规则 / Universal mechanical-change batching rule
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
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.
|
2026-09-16 13:13:18 +08:00
|
|
|
|
Two source types today: (a) dependency bump audits; (b) docs 批量修订
|
2026-09-15 08:41:51 +08:00
|
|
|
|
(batch docs revisions).
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
#### 规则正文 / Rule text
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
- **Core**: mechanical changes of ONE class fanning out from a single
|
2026-09-16 13:13:18 +08:00
|
|
|
|
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),
|
2026-09-15 08:41:51 +08:00
|
|
|
|
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
|
2026-09-16 13:13:18 +08:00
|
|
|
|
together via `工单 API(见 TERMINOLOGY)create` with
|
2026-09-15 08:41:51 +08:00
|
|
|
|
`title="[{origin}] audit-batch@{version}: mechanical bumps for {N} advisories (TD-{NNN})"`
|
|
|
|
|
|
and labels `[tech_debt_label_id, severity_label_id, kind_bug_label_id]`
|
2026-09-16 13:13:18 +08:00
|
|
|
|
(severity = highest in the batch). One row per advisory — CVE/GHSA ID,
|
|
|
|
|
|
affected package + from→to version, severity (CVSS), advisory URL,
|
2026-09-15 08:41:51 +08:00
|
|
|
|
recommended remediation, and a per-advisory Reactivation Trigger
|
|
|
|
|
|
("resolved when `npm audit` / `bun audit` reports no HIGH/CRITICAL for
|
2026-09-16 13:13:18 +08:00
|
|
|
|
this advisory") — so the release-notes `TD-NNN → #NNNN` mapping stays
|
2026-09-15 08:41:51 +08:00
|
|
|
|
per-advisory.
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- **(b) Docs 批量修订 (batch docs revisions)**: many small same-class docs
|
2026-09-15 08:41:51 +08:00
|
|
|
|
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-batch` skill (`core/skills/land-batch/SKILL.md`
|
2026-09-16 13:13:18 +08:00
|
|
|
|
— cross-branch batch composition, topology B). For dependency-bump
|
2026-09-15 08:41:51 +08:00
|
|
|
|
batches pass `batch-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.
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
#### 适用边界 / Applicability boundary
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- **非mechanical(含判断成分的变更,不并单)** — items requiring judgment
|
2026-09-15 08:41:51 +08:00
|
|
|
|
(wording decisions, behavior/API changes, review-dependent edits) are
|
|
|
|
|
|
never batched; file each as its own issue.
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- **路径重叠(batch 成员触碰相同文件 → 不并单;唯一豁免 =
|
|
|
|
|
|
可再生共享文件`bun.lock` 经batch-compose `--convergent` 收敛)** — the sole
|
2026-09-15 08:41:51 +08:00
|
|
|
|
path-overlap exemption is the regenerable shared lockfile under
|
|
|
|
|
|
`--convergent`; every other same-file collision stays un-batched.
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- **semver-major 跳变 / overrides·catalog 手术 / adapter·peer 联动(→
|
|
|
|
|
|
Tier B 独立单,维持 per-package)** — these stay per-package Tier B
|
2026-09-15 08:41:51 +08:00
|
|
|
|
issues. Tier B and the escape hatch (below) survive this generalization
|
|
|
|
|
|
unchanged.
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
#### 降级路径 / Degradation path
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- **并单 PR CI 失败 → 按锁文件 hunk 拆包回退(bisect)**: for dependency
|
2026-09-15 08:41:51 +08:00
|
|
|
|
batches, bisect by splitting lockfile hunks per package back into
|
2026-09-16 13:13:18 +08:00
|
|
|
|
per-package PRs — the original Tier A bisect semantics carried over
|
|
|
|
|
|
verbatim (同语义,泛化措辞: every mechanical batch degrades the same way).
|
2026-09-15 08:41:51 +08:00
|
|
|
|
- **Docs batches**: split per file back into per-file PRs.
|
|
|
|
|
|
- **land-batch composition fallback** (exit codes per
|
2026-09-16 13:13:18 +08:00
|
|
|
|
`.gitea/scripts/batch-compose.ts`): `3` path-overlap → split the batch
|
2026-09-15 08:41:51 +08:00
|
|
|
|
along the reported pairs (or land the overlapping member 1:1) and re-run
|
2026-09-16 13:13:18 +08:00
|
|
|
|
per group; `4` merge conflict / `5` transport/git error → fall back to
|
2026-09-15 08:41:51 +08:00
|
|
|
|
1:1 PRs for the whole batch.
|
|
|
|
|
|
- **Preflight hedge ([org-internal #3846])**: run `bun install --dry-run` before opening
|
2026-09-16 13:13:18 +08:00
|
|
|
|
a dependency-batch PR — the resolver accepting the composed version set
|
2026-09-15 08:41:51 +08:00
|
|
|
|
is a cheap pre-CI rejection of impossible bump combinations.
|
|
|
|
|
|
|
|
|
|
|
|
Tier B and the escape hatch (unchanged by the generalization):
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- **Tier B — surgery, one issue per package**: semver-major jumps,
|
2026-09-15 08:41:51 +08:00
|
|
|
|
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
|
2026-09-16 13:13:18 +08:00
|
|
|
|
CVE/GHSA ID) before creating. Record the `TD-NNN → #NNNN` mapping in the
|
2026-09-15 08:41:51 +08:00
|
|
|
|
release notes. See `verify` Phase 5.5 for the tech-debt promotion body
|
|
|
|
|
|
template and `_shared/gitea-write-patterns.md` Pattern 3.
|
|
|
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
## Pre-release Gate
|
|
|
|
|
|
|
|
|
|
|
|
| Check | Status |
|
|
|
|
|
|
| ----------------- | ----------------------------------------------------- |
|
2026-09-16 13:13:18 +08:00
|
|
|
|
| Clean workspace | ✅/ ❌ |
|
2026-09-15 08:41:51 +08:00
|
|
|
|
| Branch | {branch name} |
|
2026-09-16 13:13:18 +08:00
|
|
|
|
| Build | ✅/ ❌ |
|
|
|
|
|
|
| Typecheck + Lint | ✅/ ❌ |
|
|
|
|
|
|
| Tests | ✅/ ❌(N passed, M failed) |
|
|
|
|
|
|
| Dependency audit | ✅/ ⚠️ N known CVEs (pre-existing) / ❌N new CVEs |
|
2026-09-15 08:41:51 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
If any gate fails except known CVEs, stop and report what failed.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
## Phase 2 — Version Bump
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
Determine the new version by inspecting git history since the last tag.
|
|
|
|
|
|
|
|
|
|
|
|
1. **Find last tag**: `git describe --tags --abbrev=0` (or `git 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.
|
|
|
|
|
|
2. **Read commits since last tag**: `git log <last-tag>..HEAD --oneline`.
|
|
|
|
|
|
3. **Read current version** from the project's version file (`package.json`
|
|
|
|
|
|
`version`, `Cargo.toml`, `VERSION`, etc.).
|
|
|
|
|
|
4. **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:
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- "add", "implement", "introduce" → MINOR
|
|
|
|
|
|
- "fix", "resolve", "correct" → PATCH
|
|
|
|
|
|
- "remove", "drop", "rename" (public API) → MAJOR
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
5. **Compute bump**:
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- If any MAJOR commit → bump MAJOR.
|
|
|
|
|
|
- Else if any MINOR commit → bump MINOR.
|
|
|
|
|
|
- Else → bump PATCH.
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
## Version Bump
|
|
|
|
|
|
|
|
|
|
|
|
**Last tag**: {tag}
|
|
|
|
|
|
**Current version**: {old version}
|
|
|
|
|
|
**New version**: {new version}
|
|
|
|
|
|
**Type**: MAJOR / MINOR / PATCH
|
|
|
|
|
|
**Commits since last tag**: {N}
|
2026-09-16 13:13:18 +08:00
|
|
|
|
**Reason**: {justification — e.g. "2 feat + 3 fix → MINOR"}
|
2026-09-15 08:41:51 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
## Phase 3 — Changelog
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
Generate the changelog from `git log <last-tag>..HEAD`:
|
|
|
|
|
|
|
|
|
|
|
|
1. **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).
|
|
|
|
|
|
2. **Group by type**:
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- **Added** — `feat:` commits.
|
|
|
|
|
|
- **Changed** — modifications to existing behavior (non-breaking).
|
|
|
|
|
|
- **Fixed** — `fix:` commits.
|
|
|
|
|
|
- **Breaking** — `BREAKING CHANGE:` or `!:` commits.
|
2026-09-15 08:41:51 +08:00
|
|
|
|
3. **Deduplicate**: squash multiple commits for the same change into one entry
|
|
|
|
|
|
where it makes narrative sense.
|
|
|
|
|
|
4. Read the existing root `CHANGELOG.md` (if any) and prepend this release.
|
|
|
|
|
|
|
|
|
|
|
|
Write to root `CHANGELOG.md` (prepend section).
|
|
|
|
|
|
|
|
|
|
|
|
```markdown
|
2026-09-16 13:13:18 +08:00
|
|
|
|
## [{version}] — {YYYY-MM-DD}
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
### Added
|
|
|
|
|
|
|
|
|
|
|
|
- {feature} ({commit hash short})
|
|
|
|
|
|
|
|
|
|
|
|
### Changed
|
|
|
|
|
|
|
|
|
|
|
|
- {change} ({hash})
|
|
|
|
|
|
|
|
|
|
|
|
### Fixed
|
|
|
|
|
|
|
|
|
|
|
|
- {bugfix} ({hash})
|
|
|
|
|
|
|
|
|
|
|
|
### Breaking
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- {breaking change} ({hash}) — see migration notes above
|
2026-09-15 08:41:51 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
## Phase 4 — Tag & Finalize
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
1. **Update version file** — write the new version to the project's version
|
2026-09-15 08:41:51 +08:00
|
|
|
|
manifest.
|
|
|
|
|
|
2. **Commit**: `git add` version file + changelog file, commit with message:
|
|
|
|
|
|
```
|
|
|
|
|
|
release: {version}
|
|
|
|
|
|
```
|
2026-09-16 13:13:18 +08:00
|
|
|
|
3. **Tag**: `git tag v{version}` (adjust prefix per project convention —
|
2026-09-15 08:41:51 +08:00
|
|
|
|
check existing tags with `git tag -l`).
|
|
|
|
|
|
4. **Verify tag**: `git tag -l v{version}` confirms the tag exists.
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
**Do not push** — the user must explicitly request pushing to remote.
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
## Release Artifact
|
|
|
|
|
|
|
|
|
|
|
|
- **Version**: {version}
|
|
|
|
|
|
- **Commit**: {commit hash}
|
|
|
|
|
|
- **Tag**: v{version}
|
|
|
|
|
|
- **Changelog**: CHANGELOG.md updated
|
|
|
|
|
|
|
|
|
|
|
|
### Files Changed
|
|
|
|
|
|
|
|
|
|
|
|
| File | Change |
|
|
|
|
|
|
| -------------- | ------------------------- |
|
2026-09-16 13:13:18 +08:00
|
|
|
|
| {version file} | {old} → {new} |
|
2026-09-15 08:41:51 +08:00
|
|
|
|
| CHANGELOG.md | Prepended {version} |
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
## Phase 4b — Publish Release Artifacts (octopus project, manual)
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
> Octopus-specific. Other projects: substitute your own artifact pipeline —
|
2026-09-15 08:41:51 +08:00
|
|
|
|
> 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:
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- `GITEA_TOKEN` — PAT with `write:repository` (release create/undraft,
|
2026-09-15 08:41:51 +08:00
|
|
|
|
tag push) and `write:package` (container registry). The `CI_PAT` secret
|
|
|
|
|
|
value is the canonical token.
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- `NODE_AUTH_TOKEN` — only if publishing to npm.
|
|
|
|
|
|
- `AUR_KEY` — only if pushing the AUR package.
|
2026-09-15 08:41:51 +08:00
|
|
|
|
- Docker logged in to the Gitea container registry:
|
|
|
|
|
|
`echo "$GITEA_TOKEN" | docker login <instance-registry-host> -u <user> --password-stdin`.
|
|
|
|
|
|
|
|
|
|
|
|
### Steps
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
1. **Version + draft release** (idempotent — skips if already published):
|
2026-09-15 08:41:51 +08:00
|
|
|
|
```bash
|
|
|
|
|
|
GITEA_TOKEN=<pat> GH_REPO=Octopus/octopus bun script/version.ts
|
|
|
|
|
|
```
|
|
|
|
|
|
Creates (or refines) the draft Gitea release for `v{version}`.
|
|
|
|
|
|
|
|
|
|
|
|
2. **Build the 12 platform binaries + archives** (longest step):
|
|
|
|
|
|
```bash
|
|
|
|
|
|
OCTOPUS_VERSION={version} OCTOPUS_RELEASE={release-id} \
|
|
|
|
|
|
GH_REPO=Octopus/octopus GITEA_TOKEN=<pat> \
|
|
|
|
|
|
bun <harness-package>/script/build.ts
|
|
|
|
|
|
```
|
|
|
|
|
|
`build.ts` attaches the 12 release assets to the draft release.
|
|
|
|
|
|
|
|
|
|
|
|
3. **Publish npm / docker / AUR + git sync**:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
OCTOPUS_VERSION={version} OCTOPUS_RELEASE={release-id} \
|
|
|
|
|
|
GITEA_TOKEN=<pat> GH_REPO=Octopus/octopus \
|
|
|
|
|
|
NODE_AUTH_TOKEN=<npm-token> \
|
|
|
|
|
|
bun script/publish.ts
|
|
|
|
|
|
```
|
|
|
|
|
|
Set `OCTOPUS_GITEA_ONLY=true` to skip npm/docker/AUR/homebrew.
|
|
|
|
|
|
|
|
|
|
|
|
4. **Undraft the release** (makes it public):
|
|
|
|
|
|
```bash
|
|
|
|
|
|
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}'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
5. **Verify assets** — the release must carry 12 assets:
|
2026-09-15 08:41:51 +08:00
|
|
|
|
```bash
|
|
|
|
|
|
curl -sS "<instance-base-url>/api/v1/repos/Octopus/octopus/releases/tags/v{version}" \
|
|
|
|
|
|
-H "Authorization: token $GITEA_TOKEN" | jq '.assets | length'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
6. **Sync the public mirror repo** (`Octopus/octopus-release`):
|
|
|
|
|
|
```bash
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
## Phase 5 — Rollback Plan
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
Document how to undo this release:
|
|
|
|
|
|
|
|
|
|
|
|
1. **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 `down` migration exists.
|
|
|
|
|
|
2. **Data rollback** (if applicable):
|
|
|
|
|
|
- For each migration, confirm the `down` migration exists and has been tested.
|
|
|
|
|
|
- If the release changes data format without a reversible migration, mark
|
2026-09-16 13:13:18 +08:00
|
|
|
|
`⚠️ IRREVERSIBLE DATA CHANGE`.
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
## 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 |
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
## Phase 6 — Post-release Smoke Test
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
1. Run the project's build command on the tagged commit. Must pass.
|
|
|
|
|
|
2. Run the test suite. Must pass.
|
|
|
|
|
|
3. Return to the branch: `git checkout <original-branch>`.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
## Phase 7 — Report
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
|
## Release Report
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
**Version**: {old} → {new} (MAJOR / MINOR / PATCH)
|
2026-09-15 08:41:51 +08:00
|
|
|
|
**Tag**: v{version}
|
|
|
|
|
|
**Commits**: {N} since last tag ({feat} features, {fix} fixes)
|
2026-09-16 13:13:18 +08:00
|
|
|
|
**Typecheck**: ✅| Lint: ✅| Tests: ✅
|
2026-09-15 08:41:51 +08:00
|
|
|
|
|
|
|
|
|
|
### Changelog
|
|
|
|
|
|
|
|
|
|
|
|
{paste changelog section}
|
|
|
|
|
|
|
|
|
|
|
|
### Release Artifacts
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- Commit: {hash} — `release: {version}`
|
2026-09-15 08:41:51 +08:00
|
|
|
|
- Tag: v{version}
|
|
|
|
|
|
- Changelog: CHANGELOG.md updated
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
**Deploy**: {manual step — "merge to main triggers CI", etc.}
|
2026-09-15 08:41:51 +08:00
|
|
|
|
**Rollback**: see Phase 5 above
|
2026-09-16 13:13:18 +08:00
|
|
|
|
**Next**: `core/skills/retrospective/SKILL.md` (optional — run on any project state)
|
2026-09-15 08:41:51 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## References
|
|
|
|
|
|
|
2026-09-16 13:13:18 +08:00
|
|
|
|
- `core/checklists/release.md` — Release checklist
|
2026-09-15 08:41:51 +08:00
|
|
|
|
- Semver spec: https://semver.org
|
|
|
|
|
|
- Conventional Commits: https://www.conventionalcommits.org
|