release-tool 0.3.1

Configuration-driven release lifecycle for computed-parameter repositories
Documentation
# `release.toml` reference

## Top-level concepts

```toml
required_version = "0.1.0" # minimum release-tool SemVer,类似 cmake_minimum_required

[repository]
github = "owner/name"      # 必须与 origin identity 一致
branch = "main"            # release 只能从该 branch 的 remote HEAD 执行

[hooks]
preflight = ["just", "verify"] # 可空;argv,不经过 shell
```

```text
current release-tool crate < required_version
  -> 在完整 shape parsing 前拒绝,并要求更新 Cargo dependency/Cargo.lock

current release-tool crate >= required_version
  -> 按已定义字段语义读取;unknown field 仍拒绝,防止拼写错误
```

没有 `schema_version`、`version_file`、`RELEASE_TAG` 或 lifecycle command list。

## Command values

所有 project hooks 是 argv array:

```toml
build = ["just", "_build-release-image", "{image}"] # 一个 program + 两个 args

# 禁止:shell = "just build && just test"
```

```text
Supported placeholders
  {version} # resolved calendar tag
  {image}   # Docker target 的 resolved image(仅 build/local_check)
```

工具直接执行 argv,不调用 `sh -c`,因此空格、`$()`、`;` 不会被二次解释。

`preflight`、Docker `build` / `local_check` 的 native stdout/stderr 会实时继承到调用方。
接收 tool-injected credentials 的 `remote_check` 仍由工具 capture,并在错误输出前 redaction。

## GitHub Release publisher

```toml
[publishers.release]
kind = "github_release"
title = "CPE {version}"
prerelease = true
```

remote identity 是 GitHub Release tag + asset name。一个 Release 可以承载多个 target 的 assets。

## Docker archive target

```toml
[[targets]]
name = "sc"
kind = "docker_archive"
publisher = "release"
default = true
platform = "linux/amd64"
image = "cpe:{version}"
asset = "cpe-{version}-linux-amd64.tar.xz"
build = ["just", "_build-release-image", "sc", "{image}"]
local_check = ["just", "_check-release-image", "sc", "{image}"]
```

`asset` 必须是单一安全文件名。adapter 校验 image platform,执行 `docker save` + `xz`,并生成
GNU-compatible SHA-256 checksum file。

## GitHub Maven publisher

```toml
[publishers.packages]
kind = "github_maven"
settings = ".mvn/settings.xml" # repository-owned;只引用 env credentials
server_id = "github"
```

settings example:

```xml
<settings>
  <servers>
    <server>
      <id>github</id>
      <username>${env.GITHUB_ACTOR}</username>
      <password>${env.GITHUB_TOKEN}</password>
    </server>
  </servers>
</settings>
```

token 由工具从 `gh` 读取,仅注入 Maven/remote-check child environment。

## Maven reactor target

```toml
[[targets]]
name = "maven"
kind = "maven_reactor"
publisher = "packages"
default = true
wrapper = "./mvnw"
pom = "pom.xml"
projects = ["jobs/cpe/cpe-job-model", "jobs/skycross/skycross-job-model"]
also_make = true
remote_check = ["just", "_check-published-maven", "{version}"]
```

```text
POM reactor model
  -> selected projects + internal dependencies when also_make=true
  -> derived groupId/artifactId/version/packaging
  -> planned jar/pom identities
```

配置不复制 `coordinates`。第一期为保持 adapter 小而可证明,接受以下 reactor subset:

```text
- selected modules 的 GAV 可从 module/parent direct fields 得到
- ${revision} 是 GAV 中唯一支持的 expression
- packaging: jar | pom
- main artifact + POM;不猜测 classifiers/attached artifacts
- duplicate GAV、unresolved expression 或其它 packaging 立即失败
```

这不是静默近似 Maven effective model:无法确定 exact deploy inventory 时停止,后续按真实 adopter
需求扩展解析能力。

完整 adopter 配置见 [`examples/`](../examples)。

## OCI image targets

每个 image 是一个普通 target。`kind = "oci_image"` 隐式选择 registry publisher,因此没有
`[publishers.registry]`,也不配置 `publisher = "registry"`:

```toml
[[targets]]
name = "base"
kind = "oci_image"
image = "ghcr.io/example/base:{version}"
platform = "linux/amd64"
reuse_check = ["just", "_reuse-image", "base"]
build = ["just", "_build-image", "base"]

[[targets]]
name = "prepared"
kind = "oci_image"
default = true
depends_on = ["base"]
image = "ghcr.io/example/prepared:{version}"
platform = "linux/amd64"
reuse_check = ["just", "_reuse-image", "prepared"]
build = ["just", "_build-image", "prepared"]
```

选择 `prepared` 会自动包含 `base`。配置顺序不决定执行顺序;Resolve 展开 transitive closure,
然后按依赖关系排序,无依赖关系的 targets 按 name 稳定排序。unknown、self、duplicate dependency
和 cycle 都会在发布前失败。v1 只允许 `oci_image -> oci_image` dependencies。

### Project command environment

`reuse_check` 和 `build` 是 argv arrays,不经过 shell,也不需要在每个 command 中重复 placeholders。
工具注入:

```text
RELEASE_TARGET             current target name
RELEASE_VERSION            calendar release version/tag
RELEASE_COMMIT             exact source commit
RELEASE_IMAGE              local candidate / remote destination tag
RELEASE_DEPENDENCIES_JSON  prepared direct dependencies
```

`reuse_check` 额外收到:

```text
RELEASE_CANDIDATE_IMAGE    digest-pinned previous/current candidate
```

dependency JSON 按 target name 排序;每个 value 包含:

```json
{
  "base": {
    "reference": "ghcr.io/example/base@sha256:...",
    "config_digest": "sha256:..."
  }
}
```

Build dependency 尚未 push 时,`reference` 是已本地 Inspect 的 current candidate tag;
Reuse/Existing dependency 使用 digest-pinned remote reference。两种情况都提供已冻结的 OCI
config digest。

### Build / Reuse / Existing

release-tool 从较早的 sealed repository releases 由新到旧查找同一 target 的 image tag,并立即
解析为 exact digest。没有 previous candidate 时直接 Build;不需要 bootstrap 文件。

```text
reuse_check exit 0   candidate 满足当前 source/dependency context,Reuse
reuse_check exit 10  candidate 已过期,Build
其它 exit code       无法可靠判断,停止 release
```

freshness 规则属于项目。`reuse_check` 可以读取 project-owned labels、source facts、testsets 或
dependency digests;release-tool 不定义 `inputs`、fingerprint 或 external input schema。check 必须
只读,不得 login、push、retag、修改 local image 或 tracked source。

Prepare 后 target 的内部 action 是:

```text
Existing  sealed current tag 已存在且 reuse_check 通过;不写,只验证
Reuse     previous exact manifest 通过 reuse_check;remote-retag 到 current tag
Build     无 candidate 或 reuse_check 返回 10;build 生成 current local image
```

被依赖 target 始终保留在 plan 中;所谓“跳过”仅是不运行其 `build`。Prepare、Publish、Verify
都按同一稳定拓扑顺序执行。

publisher 不执行 `docker login`,只使用当前 Docker credential store。v1 只接受一个
`os/architecture` platform,不创建 multi-platform index。builder 只生成 current local image;
不得 push、retag 或修改 tracked source。

### Registry recovery 与 concurrency

publisher 对 current tag 执行 write-before/write-after Inspect;任何已存在但与 prepared
manifest/config/platform 不一致的 tag 都停止,且从不 delete、force 或 clobber。未 Seal candidate
出现 current tag 时视为 orphan publication。

write command 失败后重新 Inspect;只有 remote 与 prepared artifact 完全一致时,丢失响应才按
成功处理。普通 OCI tag write 没有通用 compare-and-swap,因此同一 release 必须由 single writer
串行发布,或由 registry 提供 immutable-tag enforcement;Doctor 将无法无写入证明的能力报告为
`UNVERIFIABLE`。

完整 Resources 配置见
[`release.toml`](../examples/computed-parameter-engine-resources/release.toml)。