Skip to content
Merged
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
28 changes: 25 additions & 3 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,11 @@ Rules for a roll-up job that is (or may become) required:

`reborn-release-compile.yml` is a compile-and-smoke preflight for the shipping
Reborn `ironclaw` binary. The tag-only `release.yml` calls it for matching
release tags and will not run its `host` job unless every target succeeds. The
preflight workflow can also run directly through `workflow_dispatch`; it is not
triggered by pull requests, so ordinary CLI and WebUI changes do not start the
release tags. Under #6160's temporary compile-only policy below, the legacy
`host` job stays disabled; if that job is restored, its dependency and success
gate still prevent it from running unless every target succeeds. The preflight
workflow can also run directly through `workflow_dispatch`; it is not triggered
by pull requests, so ordinary CLI and WebUI changes do not start the
seven-platform release matrix.

| Rust target | GitHub runner |
Expand Down Expand Up @@ -151,6 +153,26 @@ alerts can target dedicated channels.
When adding a new workflow that runs on `push` to `main`, add its workflow
`name:` to the watched list in `main-ci-slack-alerts.yml`.

## Reborn-only release validation policy

For #6160, `release.yml` temporarily keeps the legacy cargo-dist release chain
visible but disables its `plan` root with the impossible
`github.repository == ''` guard. Its dependent local/global artifact, WASM,
GitHub Release host, registry-checksum, and announcement jobs therefore skip.
The Reborn compile matrix merged in #6176 is the only intended active path.
This compile-only mode produces short-lived Actions evidence artifacts; it does
not create a GitHub Release or permanent downloadable release assets.

The release Docker caller retains its own impossible guard as an explicit
defense against image publication. The reusable `docker.yml` workflow is
unchanged, so its manual and hourly entry points remain available. Changes to
the release, Docker, or reusable Reborn compile workflow enter the Reborn CLI
smoke selector, and the required Code Style roll-up propagates that contract
result. To restore the non-Docker legacy release path, remove `plan`'s
impossible guard; the workflow's tag-only trigger already scopes it to release
tags. Restore the Docker caller's host-success condition separately only when
release image publication is intentionally re-enabled.

## Known accepted gaps (deliberate, revisit as needed)

- **Windows clippy** (`code_style.yml` `clippy-windows`) runs on push only;
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/code_style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:
echo "has_boundary_check=false" >> "$GITHUB_OUTPUT"
fi

if printf '%s\n' "$CHANGED_FILES" | grep -Eq '^(crates/ironclaw_runner/|crates/ironclaw_reborn_cli/|crates/ironclaw_reborn_config/|crates/ironclaw_architecture/tests/reborn_dependency_boundaries\.rs$|Cargo\.toml$|Cargo\.lock$|\.github/workflows/code_style\.yml$)'; then
if printf '%s\n' "$CHANGED_FILES" | grep -Eq '^(crates/ironclaw_runner/|crates/ironclaw_reborn_cli/|crates/ironclaw_reborn_config/|crates/ironclaw_architecture/tests/reborn_dependency_boundaries\.rs$|Cargo\.toml$|Cargo\.lock$|\.github/workflows/(code_style|release|docker|reborn-release-compile)\.yml$)'; then
echo "has_reborn_cli=true" >> "$GITHUB_OUTPUT"
else
echo "has_reborn_cli=false" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -477,6 +477,10 @@ jobs:
persist-credentials: false
- run: |
if [[ "${{ needs.changes.outputs.has_code }}" == "false" ]]; then
if [[ "${{ needs.changes.outputs.has_reborn_cli }}" == "true" && "${{ needs.reborn-cli-smoke.result }}" != "success" ]]; then
echo "Reborn CLI smoke failed: ${{ needs.reborn-cli-smoke.result }}"
exit 1
fi
echo "No code changes — style checks skipped correctly"
exit 0
fi
Expand Down
15 changes: 12 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
#
# Note that the GitHub Release will be created with a generated
# title/body based on your changelogs.
#
# Temporary #6160 policy: only the Reborn compile preflight below runs;
# the generated cargo-dist/WASM/GitHub Release and Docker jobs stay defined
# for rollback but are skipped.

name: Release
permissions:
Expand Down Expand Up @@ -53,8 +57,11 @@ jobs:
with:
ref: ${{ github.sha }}

# Run 'dist plan' (or host) to determine what tasks we need to do
# Keep the legacy cargo-dist/WASM/GitHub Release chain visible for rollback,
# but disable its root while #6160 makes this workflow Reborn-compile-only.
# The Reborn compile matrix above remains the only active release path.
plan:
if: github.repository == ''
runs-on: "ubuntu-22.04"
outputs:
val: ${{ steps.plan.outputs.manifest }}
Expand Down Expand Up @@ -481,10 +488,12 @@ jobs:
# shellcheck disable=SC2086 # PRERELEASE_FLAG is '--prerelease' or empty
gh release create "$RELEASE_TAG" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*

# Build and push Docker Hub images (:version, :latest, :sha-*) after the GitHub Release exists.
# Keep release-path Docker build/publish explicitly disabled for #6160 even
# though the legacy host chain is also gated. The reusable Docker workflow
# remains available through manual and scheduled triggers.
docker-image:
needs: host
if: ${{ always() && needs.host.result == 'success' }}
if: github.repository == ''
permissions:
contents: read
packages: read
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### CI / Release

- *(release)* compile the canonical Reborn `ironclaw` binary across the seven supported OS/CPU targets as a tag-driven release preflight ([#6160](https://github.com/nearai/ironclaw/issues/6160)).
- *(release)* compile the canonical Reborn `ironclaw` binary across the seven supported OS/CPU targets as a tag-driven preflight while temporarily skipping the legacy cargo-dist, WASM, GitHub Release, registry-update, announcement, and Docker jobs; manual and hourly Docker workflow entry points remain available ([#6160](https://github.com/nearai/ironclaw/issues/6160)).

### Removed

Expand Down
102 changes: 102 additions & 0 deletions crates/ironclaw_reborn_cli/tests/smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6948,6 +6948,108 @@ api_key_env = "REBORN_TEST_UNSET_BC8F4D_KEY"
);
}

#[test]
fn release_ci_skips_legacy_publish_dag_without_disabling_independent_docker_runs() {
let root = workspace_root();
let release_workflow = std::fs::read_to_string(root.join(".github/workflows/release.yml"))
.expect("release workflow")
.replace("\r\n", "\n");
let docker_workflow = std::fs::read_to_string(root.join(".github/workflows/docker.yml"))
.expect("Docker workflow")
.replace("\r\n", "\n");
let code_style_workflow =
std::fs::read_to_string(root.join(".github/workflows/code_style.yml"))
.expect("code style workflow")
.replace("\r\n", "\n");

let release_job = |job_name: &str| {
let job_marker = format!(" {job_name}:\n");
let job_start = release_workflow
.match_indices(&job_marker)
.find_map(|(index, _)| {
(index == 0 || release_workflow.as_bytes()[index - 1] == b'\n')
.then_some(index + job_marker.len())
})
.unwrap_or_else(|| panic!("release workflow should retain the {job_name} job"));
let jobs_after_marker = &release_workflow[job_start..];
let job_body = jobs_after_marker
.lines()
.take_while(|line| {
let trimmed = line.trim_start();
trimmed.is_empty() || line.len() - trimmed.len() > 2
})
.collect::<Vec<_>>()
.join("\n");
assert!(
!job_body.is_empty(),
"release workflow should retain the {job_name} job body"
);
job_body
};

let reborn_compile_job = release_job("reborn-binary-compile");
assert!(
reborn_compile_job.contains("uses: ./.github/workflows/reborn-release-compile.yml")
&& reborn_compile_job.contains("ref: ${{ github.sha }}")
&& !reborn_compile_job
.lines()
.any(|line| line.starts_with(" if:")),
"the Reborn compile matrix must remain the active release path"
);

let plan_job = release_job("plan");
assert!(
plan_job.contains("if: github.repository == ''")
&& plan_job.contains("dist host --steps=create")
&& plan_job.contains("dist plan --output-format=json"),
"release CI must retain but skip the legacy cargo-dist plan root"
);
for legacy_job_name in [
"build-local-artifacts",
"build-global-artifacts",
"build-wasm-extensions",
"host",
"update-registry-checksums",
"announce",
] {
let legacy_job = release_job(legacy_job_name);
assert!(
legacy_job.contains("\n - plan\n"),
"legacy release job {legacy_job_name} must remain downstream of the disabled plan root"
);
}

let docker_job = release_job("docker-image");
assert!(
docker_job.contains("needs: host")
&& docker_job.contains("if: github.repository == ''")
&& docker_job.contains("uses: ./.github/workflows/docker.yml")
&& docker_job.contains("release: true")
&& docker_job.contains("secrets: inherit"),
"release CI must retain but skip its Docker build/publish caller"
);
assert!(
docker_workflow.contains("workflow_dispatch:") && docker_workflow.contains("schedule:"),
"the independent Docker workflow must remain manually and periodically runnable"
);
let reborn_cli_selector = code_style_workflow
.lines()
.find(|line| line.contains("grep -Eq") && line.contains("crates/ironclaw_reborn_cli/"))
.expect("code style workflow should classify Reborn CLI changes");
assert!(
reborn_cli_selector.contains(
r"\.github/workflows/(code_style|release|docker|reborn-release-compile)\.yml$"
),
"release workflow-only PRs must run the Reborn CLI smoke contract"
);
assert!(
code_style_workflow.contains(
r#"if [[ "${{ needs.changes.outputs.has_reborn_cli }}" == "true" && "${{ needs.reborn-cli-smoke.result }}" != "success" ]]; then"#,
),
"the required Code Style roll-up must propagate workflow-only Reborn CLI smoke failures"
);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

fn local_yolo_command(temp: &tempfile::TempDir, args: &[&str]) -> Command {
let reborn_home = temp.path().join("reborn-home");
let home = temp.path().join("home");
Expand Down
7 changes: 7 additions & 0 deletions docs/reborn-binary.md
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,13 @@ evidence only and are excluded from the `artifacts-*` set uploaded to the
GitHub Release. It does not claim `serve`, external-service, installer, or
canonical Reborn packaging coverage.

While #6160's temporary Reborn-only release policy is active, matching tag runs
stop after this compile matrix. The legacy cargo-dist plan, WASM build, GitHub
Release host, registry-checksum, announcement, and release Docker caller all
skip, so the run creates neither a GitHub Release nor published release assets
or images. The independent manual and hourly entry points in `docker.yml`
remain available.

Current `dist plan --output-format=json` with `crates/ironclaw_reborn_cli` marked `dist = false` emits only the root legacy package artifacts (`ironclaw` package, `ironclaw-legacy` executable). Removing `dist = false` alone is not enough to ship the canonical Reborn `ironclaw` executable in the existing `ironclaw-v*` release workflow because that workflow is shaped around the root `ironclaw` package tag. Enabling the `ironclaw_reborn_cli` release also requires cargo-dist WiX metadata/template work and an explicit package/tag/versioning decision.

Follow-up issue: #3483 tracks packaging the canonical Reborn binary in release artifacts.
Expand Down
Loading