Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions config/examples/casbin-release-queue.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"schemaVersion": "1",
"name": "Casbin release queue",
"items": [
{
"id": "casbin",
"displayName": "Apache Casbin",
"repository": "https://github.com/apache/casbin.git",
"adapter": "casbin-go",
"releaseConfig": "casbin-go.json",
"state": "queued"
},
{
"id": "casbin-sqlx-adapter",
"displayName": "Casbin SQLX adapter",
"repository": "https://github.com/apache/casbin-sqlx-adapter.git",
"adapter": "go",
"state": "blocked",
"note": "Add and test a Go adapter before IRA can run this repository."
}
]
}
30 changes: 30 additions & 0 deletions config/release-queue.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/EmryZhang/incubator-release-assistant/blob/main/config/release-queue.schema.json",
"title": "IRA ordered release queue",
"type": "object",
"additionalProperties": false,
"required": ["schemaVersion", "name", "items"],
"properties": {
"schemaVersion": {"const": "1"},
"name": {"type": "string", "minLength": 1},
"items": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "displayName", "repository", "adapter", "state"],
"properties": {
"id": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"},
"displayName": {"type": "string", "minLength": 1},
"repository": {"type": "string", "format": "uri"},
"adapter": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"},
"releaseConfig": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._/\\-]*\\.json$"},
"state": {"enum": ["queued", "blocked", "manual", "complete"]},
"note": {"type": "string"}
}
}
}
}
}
12 changes: 9 additions & 3 deletions docs/adapter-contract.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# Adapter contract

The current code in `internal/release` implements the Casbin Go policy directly
while the project has one adapter. The next adapter extraction must preserve the
following contract.
`internal/release` has an explicit adapter registry. The current `casbin-go`
adapter owns Casbin-specific validation; the engine owns shared state,
evidence, queue, signing, staging, and public-byte verification. The next
adapter must preserve the following contract.

An adapter provides:

- a stable adapter ID and the exact project identities it supports;
- a short operator-facing description exposed by `ira adapters`;
- canonical upstream and ASF distribution locations;
- archive naming and required-file rules;
- typed build/test commands expressed as executable plus argument vector;
Expand All @@ -24,3 +26,7 @@ Before registration, an adapter needs tests for valid configuration, hostile
paths, incorrect official endpoints, missing legal files, resume behavior, and
the exact host command and working directory. At least one end-to-end test must
use a local fixture repository without signing or network mutation.

The current release JSON schema remains a Casbin Go schema. Registering a
second adapter also requires the next version of the reviewed configuration
schema; do not weaken the Casbin rules merely to make an adapter name parse.
90 changes: 90 additions & 0 deletions docs/adapter-research/casbin-go.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Casbin Go adapter research

Preparation date: 2026-08-06
Status: Active design evidence for the `casbin-go` adapter

## What the upstream repository actually does

The authoritative upstream workflow is
[`apache/casbin/.github/workflows/release.yml`](https://github.com/apache/casbin/blob/master/.github/workflows/release.yml).
It is triggered only by a manually pushed tag; merging a pull request does not
publish a release.

- `vX.Y.Z-rcN` creates a GitHub **pre-release**.
- `vX.Y.Z` creates the GitHub release and requests publication of the matching
Go module version through `proxy.golang.org`.
- The workflow creates source `.tar.gz` and `.zip` archives from the exact tag,
writes SHA-512 files, verifies those checksums, and attaches the output to the
GitHub release.
- The normal CI workflow runs `make test` on pushes and pull requests. IRA must
check the selected commit's already-run CI; it must not execute target code
merely to duplicate it.

The Apache source-release portion is governed separately: a source artifact is
reviewed, signed, staged for voting, and only then becomes an official release.
See [Apache Release Policy](https://apache.org/dev/release),
[Apache release publishing guidance](https://infra.apache.org/release-publishing.html),
and [ASF signing guidance](https://infra.apache.org/release-signing.html).

## Adapter lifecycle

The Go Casbin adapter must represent both release surfaces without claiming that
a tag alone is an Apache approval:

```text
selected commit
-> verify upstream CI evidence
-> prepare signed Apache source RC
-> vote and approve (human/community work)
-> push final vX.Y.Z tag (explicit human authority)
-> upstream GitHub workflow publishes GitHub Release and Go module proxy
-> verify tag, GitHub assets, checksums, and proxy availability
```

RC tags and final tags are deliberately different. The final tag must not be
created by a queue traversal or by a JSON command string. A release manager
must explicitly authorize it after the required Apache process has completed.

## What IRA owns in this phase

1. An adapter registry: only a registered adapter can run from the queue.
2. Project-specific validation inside the adapter, rather than in the generic
engine entrypoint.
3. Ordered queue state: the current repository, its next action, and the next
incomplete repository.
4. Existing immutable source-candidate, signing, staging, and public-byte
verification controls for the Casbin Apache source release.

## Compatibility with the existing RC workflow

The adapter extraction does **not** change the existing Casbin source-RC
configuration schema, `RunID`, artifact name, state-file digest, or the
`validate -> prepare -> sign -> stage -> verify-public` command path. Existing
prepared candidates therefore continue to use their matching `.ira/runs/`
state. The queue and `adapters` commands are additive.

The current upstream GitHub workflow uses the archive basename
`apache-casbin-incubating-<version>-src`, while the existing ASF source-RC
workflow preserves its already-tried configuration-derived archive name. This
is a pre-existing difference between the two release surfaces, not a change
introduced by adapter extraction. Do not rename an existing RC to match the
GitHub workflow: changed bytes require a new RC. The future final-publication
verifier must model the GitHub artifact naming separately.

## Deliberately deferred work

- A new generic configuration schema that can express npm, Maven, PyPI, and
GitHub-only release targets without Casbin fields.
- Read-only verification of the final tag's GitHub Release assets and Go module
proxy availability.
- An explicit, human-confirmed final-tag action.
- Additional adapters. Each must begin with the target repository's actual
release workflow and package-registry rules, not a guessed generic command.

## Design implication

The generic engine must never interpret a tracker row as executable shell.
It accepts structured adapter data, then the registered adapter supplies the
reviewed checks, artifact names, and publication-verification rules. This keeps
the queue useful for every Casbin repository while making unsupported releases
visible rather than pretending they were published.
10 changes: 10 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ strict config + project policy
+--> stage domain ---> RC confirmation --> ASF dist --> public verification
```

The engine finds the project policy through a registered adapter ID. This keeps
the ordered queue generic: it can show every repository, but it only runs an
item when its adapter is registered and its release configuration validates.

## Current adapter

`casbin-go` is the only accepted adapter. Its policy fixes:
Expand All @@ -34,6 +38,12 @@ strict config + project policy
This intentional narrowness makes the first implementation executable without
pretending that unimplemented repositories are supported.

Casbin's upstream tag workflow is a separate final-publication surface:
`vX.Y.Z-rcN` produces a GitHub pre-release, while `vX.Y.Z` produces the GitHub
release and triggers Go module proxy publication. See
`docs/adapter-research/casbin-go.md`. IRA records this as adapter policy but
does not yet create final tags or verify those published surfaces.

## State and resumption

Each candidate has one ignored `.ira/runs/<project>-<version>-rc<n>/` directory:
Expand Down
4 changes: 3 additions & 1 deletion ira.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
[CmdletBinding()]
param(
[Parameter(Position = 0, Mandatory = $true)]
[ValidateSet("doctor", "validate", "plan", "prepare", "sign", "stage", "verify-public", "version")]
[ValidateSet("doctor", "validate", "plan", "prepare", "sign", "stage", "verify-public", "queue-status", "queue-prepare", "adapters", "version")]
[string]$Command,

[string]$Config,

[string]$Queue,

[string]$Confirm,

[string]$SecretDirectory,
Expand Down
4 changes: 4 additions & 0 deletions scripts/sync-skill-assets.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@ Copy-Item -LiteralPath (Join-Path $root "config\release.schema.json") `
-Destination (Join-Path $skillAssets "release.schema.json") -Force
Copy-Item -LiteralPath (Join-Path $root "config\examples\casbin-go.json") `
-Destination (Join-Path $skillAssets "examples\casbin-go.json") -Force
Copy-Item -LiteralPath (Join-Path $root "config\release-queue.schema.json") `
-Destination (Join-Path $skillAssets "release-queue.schema.json") -Force
Copy-Item -LiteralPath (Join-Path $root "config\examples\casbin-release-queue.json") `
-Destination (Join-Path $skillAssets "examples\casbin-release-queue.json") -Force

Write-Host "Skill assets synchronized."
8 changes: 7 additions & 1 deletion scripts/validate-repository.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ $handbookSkill = Join-Path $root "skills\apache-incubator-handbook"

$pairs = @(
@((Join-Path $root "config\release.schema.json"), (Join-Path $releaseSkill "assets\release.schema.json")),
@((Join-Path $root "config\examples\casbin-go.json"), (Join-Path $releaseSkill "assets\examples\casbin-go.json"))
@((Join-Path $root "config\examples\casbin-go.json"), (Join-Path $releaseSkill "assets\examples\casbin-go.json")),
@((Join-Path $root "config\release-queue.schema.json"), (Join-Path $releaseSkill "assets\release-queue.schema.json")),
@((Join-Path $root "config\examples\casbin-release-queue.json"), (Join-Path $releaseSkill "assets\examples\casbin-release-queue.json"))
)

foreach ($pair in $pairs) {
Expand All @@ -20,6 +22,8 @@ foreach ($pair in $pairs) {

Get-Content -LiteralPath (Join-Path $root "config\release.schema.json") -Raw | ConvertFrom-Json | Out-Null
Get-Content -LiteralPath (Join-Path $root "config\examples\casbin-go.json") -Raw | ConvertFrom-Json | Out-Null
Get-Content -LiteralPath (Join-Path $root "config\release-queue.schema.json") -Raw | ConvertFrom-Json | Out-Null
Get-Content -LiteralPath (Join-Path $root "config\examples\casbin-release-queue.json") -Raw | ConvertFrom-Json | Out-Null
Get-Content -LiteralPath (Join-Path $releaseSkill "evals\evals.json") -Encoding UTF8 -Raw | ConvertFrom-Json | Out-Null
Get-Content -LiteralPath (Join-Path $handbookSkill "evals\evals.json") -Encoding UTF8 -Raw | ConvertFrom-Json | Out-Null

Expand All @@ -36,6 +40,8 @@ foreach ($required in @(
"scripts\run.sh",
"assets\release.schema.json",
"assets\examples\casbin-go.json",
"assets\release-queue.schema.json",
"assets\examples\casbin-release-queue.json",
"assets\examples\key-metadata.example.json",
"assets\examples\doctor-report.example.json",
"references\configuration.md",
Expand Down
18 changes: 18 additions & 0 deletions skills/incubator-release-assistant/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,24 @@ bash <skill-directory>/scripts/run.sh plan --config <config>
Stop when the configuration does not validate. Tell the user exactly which
field must be supplied or corrected, then continue from the same step.

## Coordinate a release queue

When the user supplies several repositories from a tracker, do not attempt to
publish them together. Use an ordered queue configuration and show the user
the current repository, its next required action, and the next queued item:

```powershell
& <skill-directory>\scripts\run.ps1 queue-status -Queue <queue>
& <skill-directory>\scripts\run.ps1 queue-prepare -Queue <queue>
```

`queue-prepare` runs only the current item's `prepare` step. It must not sign,
stage, vote, or publish a later item. The queue contains repository data and
config paths only; never add command strings. Stop on a `blocked` or `manual`
item and state its note. An item may use an executable adapter only after that
adapter and its contract tests exist. Use `adapters` to determine which tracker
items IRA can actually run.

## Route incomplete setup through bundled knowledge

Do not leave a new release manager with a terse engine error. Use the code and
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"schemaVersion": "1",
"name": "Casbin release queue",
"items": [
{
"id": "casbin",
"displayName": "Apache Casbin",
"repository": "https://github.com/apache/casbin.git",
"adapter": "casbin-go",
"releaseConfig": "casbin-go.json",
"state": "queued"
},
{
"id": "casbin-sqlx-adapter",
"displayName": "Casbin SQLX adapter",
"repository": "https://github.com/apache/casbin-sqlx-adapter.git",
"adapter": "go",
"state": "blocked",
"note": "Add and test a Go adapter before IRA can run this repository."
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/EmryZhang/incubator-release-assistant/blob/main/config/release-queue.schema.json",
"title": "IRA ordered release queue",
"type": "object",
"additionalProperties": false,
"required": ["schemaVersion", "name", "items"],
"properties": {
"schemaVersion": {"const": "1"},
"name": {"type": "string", "minLength": 1},
"items": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["id", "displayName", "repository", "adapter", "state"],
"properties": {
"id": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"},
"displayName": {"type": "string", "minLength": 1},
"repository": {"type": "string", "format": "uri"},
"adapter": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"},
"releaseConfig": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._/\\-]*\\.json$"},
"state": {"enum": ["queued", "blocked", "manual", "complete"]},
"note": {"type": "string"}
}
}
}
}
}
21 changes: 21 additions & 0 deletions skills/incubator-release-assistant/references/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ tests during `prepare`; confirm the exact selected commit's GitHub CI separately
Never place passwords, private keys, tokens, cookies, SSH material, or private
mail in configuration.

## Ordered release queue

`assets/release-queue.schema.json` and
`assets/examples/casbin-release-queue.json` define an ordered worklist for
several repositories. Copy both the queue and each referenced release config to
the same ignored local directory. `releaseConfig` is a safe relative JSON path
resolved from the queue file's directory.

Each item records its repository identity, adapter, and one of four states:

- `queued`: has a release config and can be assessed against local IRA state;
- `blocked`: requires a non-empty explanation before the queue can continue;
- `manual`: records work IRA must not perform and requires a non-empty note;
- `complete`: a reviewed external completion record with a non-empty note.

`queue-status` reports the first incomplete item as current. It derives the
next action from the matching release state: `prepare`, `sign`, `stage`, or
complete. `queue-prepare` only prepares that current item; signing and staging
remain individual, explicitly confirmed commands. The queue does not accept
shell commands and does not make an unsupported adapter executable.

## External secret directory

The secret location is deliberately not a JSON field. Start the Agent from the
Expand Down
Loading
Loading