diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 12d3537771b..8bef9f547f0 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -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 | @@ -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; diff --git a/.github/workflows/code_style.yml b/.github/workflows/code_style.yml index 25df43d4be1..4ba4df0e5a0 100644 --- a/.github/workflows/code_style.yml +++ b/.github/workflows/code_style.yml @@ -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" @@ -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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aaf37794663..e1c248bd825 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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: @@ -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 }} @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 442f0cc9c52..a3b4e7e6b99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/crates/ironclaw_reborn_cli/tests/smoke.rs b/crates/ironclaw_reborn_cli/tests/smoke.rs index 6ff066baf5e..433e8153cb8 100644 --- a/crates/ironclaw_reborn_cli/tests/smoke.rs +++ b/crates/ironclaw_reborn_cli/tests/smoke.rs @@ -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::>() + .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" + ); +} + fn local_yolo_command(temp: &tempfile::TempDir, args: &[&str]) -> Command { let reborn_home = temp.path().join("reborn-home"); let home = temp.path().join("home"); diff --git a/docs/reborn-binary.md b/docs/reborn-binary.md index df71c5cb5c3..b847b72530c 100644 --- a/docs/reborn-binary.md +++ b/docs/reborn-binary.md @@ -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.