diff --git a/.automation/skills/upstream-rebase/SKILL.md b/.automation/skills/upstream-rebase/SKILL.md index c23286bb72c4a9..752b3fdb97d317 100644 --- a/.automation/skills/upstream-rebase/SKILL.md +++ b/.automation/skills/upstream-rebase/SKILL.md @@ -26,7 +26,12 @@ Your report can only stop it. When you finish, the workflow independently: alone that the series sits on the upstream tip, is linear, lost or gained no commit, and changed the tree by exactly the upstream delta; 2. builds x86_64 and arm64 `vmlinux` with `kernel-build.sh`, which turns on - every downstream feature as a built-in. + every downstream feature as a built-in. x86_64 starts from the latest + Edera host config in edera-dev/linux-kernel-oci; arm64 from defconfig. + +If the build fails, you get your branch back with the build logs and one +chance to repair it (see "Repairing a failed build"), and the repaired branch +is checked and built again from scratch. If all of that passes **and your report's "Needs a decision" section is `None.` with nothing marked UNSURE anywhere** (checked by @@ -138,6 +143,12 @@ The workflow re-runs these builds itself, but run them here so you can fix what your resolutions broke. Each takes a while; run them once the rebase is complete, not after every commit. +Run each build in the foreground and wait for it: the shell allows a command +up to 90 minutes, which is enough for a full build. Never start a build in +the background, and never end your session while one is running. A run +that stops to wait for a build ends there, and nobody fixes what it would +have shown. + ```sh bash .rebase-tools/scripts/kernel-build.sh x86_64 .rebase-scratch/obj-x86 4 bash .rebase-tools/scripts/kernel-build.sh arm64 .rebase-scratch/obj-arm64 4 @@ -149,9 +160,10 @@ the build script names). Find out which, and say so in the report; if the build script's list is what is stale, that is a **Needs a decision** item, not something to work around. -If a build fails because of the rebase (a resolution, or an upstream change a -downstream commit has not caught up with), fix it **in the downstream commit -that is wrong**. Interactive editors do not work here, so drive the todo list +A failing build is not a finished rebase. If a build fails because of the +rebase (a resolution, or an upstream change a downstream commit has not +caught up with), fix it, build again, and keep going until both builds pass. +Fix it **in the downstream commit that is wrong**. Interactive editors do not work here, so drive the todo list with sed: `git -c sequence.editor="sed -i 's/^pick /edit /'" rebase -i UPSTREAM`, amend, then `git rebase --continue`. Do not add a fix-up commit on top unless there is no single commit it belongs to; if you must, its @@ -238,6 +250,36 @@ x86_64, arm64: pass, fail (with the first error), or pre-existing failure Then stop. Leave RESULT checked out or not; the workflow reads the branch, not the working tree. +## Repairing a failed build + +The workflow sends you here, in a fresh session, when the branch you produced +failed its independent build. The prompt gives you `BUILD_LOGS`, a directory +holding the log of each arch the workflow built, and your branch is checked +out as `RESULT`. `REPORT` already holds the report you wrote for the rebase. + +1. Read the logs. Find the first error in each failing one; later errors + usually follow from it. A log that ends with `kernel-build: ... built` + passed. +2. Reproduce the failure with `kernel-build.sh` exactly as in step 3, then + fix it the same way: in the downstream commit that is wrong, with the + same rules on fix-up commits. If it fails on pristine `UPSTREAM` too, it + is not yours: say so and leave the branch alone. +3. Build both arches again, not only the one that failed, and keep fixing + and rebuilding until both pass. Do not stop at the first fix. +4. Run `verify-upstream-rebase.sh` as in step 5. Every new drift line must be + one of your build fixes. +5. Update `REPORT` in place. Add each fix to **Build fixes** (what broke, the + first error, which commit you changed, why), and rewrite **Builds** with + the results you now have. Leave the rest as it is, except to add an + **UNSURE** item if a fix needed a judgement call. A reviewer will read + this report, not your session. + +Exit status 3 is still not something to work around here: a Kconfig symbol +the build script names no longer surviving `olddefconfig` goes to **Needs a +decision**, as in step 3. If you cannot make the build pass, leave `RESULT` +at the best branch you have and say in **Builds** exactly what still fails +and why. + ## If you cannot finish If the rebase cannot be completed (a conflict you cannot resolve at all, a diff --git a/.github/scripts/kernel-build.sh b/.github/scripts/kernel-build.sh index 2a4c38dd68cb80..5920e581871522 100755 --- a/.github/scripts/kernel-build.sh +++ b/.github/scripts/kernel-build.sh @@ -7,6 +7,12 @@ # replay that breaks any of them, or links against a symbol upstream removed, # fails here. Building only a subdirectory would miss those link errors. # +# x86_64 adds Edera's shipping host config, fetched from the tip of +# edera-dev/linux-kernel-oci each run, over defconfig, so the branch is +# judged against the kernel we actually ship. That config builds +# OpenPaX out and several Edera drivers as modules, so the options below are +# still forced on as built-ins over it. arm64 starts from defconfig. +# # The rebase skill runs this same script before they finish, so # what Claude checks and what the workflow checks cannot drift apart. # @@ -18,7 +24,8 @@ # aarch64-linux-gnu-) unless LLVM is set, in which case it builds with clang. # # Exit status: make's on a build failure; 3 if an option it asked for did not -# survive olddefconfig (a Kconfig dependency the series no longer satisfies). +# survive olddefconfig (a Kconfig dependency the series no longer satisfies); +# git's if the host config cannot be fetched. set -euo pipefail @@ -51,6 +58,8 @@ x86_64) HYPERVISOR_GUEST PARAVIRT XEN_PV XEN_PVHVM_GUEST PCI_XEN XEN_PCIDEV_FRONTEND XEN_PCIDEV_BACKEND XEN_IOMMU HYPERV HYPERV_VMBUS HYPERV_NET PCI_HYPERV PCI_HYPERV_INTERFACE + # host.config builds AGP as a module, which caps DRM at m. + AGP ) ;; arm64) @@ -72,6 +81,19 @@ mkdir -p "$objdir" mk() { make -s ARCH="$karch" "${cross[@]}" O="$objdir" "$@"; } mk defconfig +if [ "$arch" = x86_64 ]; then + # host.config is a fragment against x86_64_defconfig: it leaves out + # whatever matches defconfig (SMP and NET among them), so it only means + # something merged over defconfig, the way linux-kernel-oci applies it. + oci=https://github.com/edera-dev/linux-kernel-oci + cfgrepo=$(mktemp -d) + trap 'rm -rf "$cfgrepo"' EXIT + git init -q --bare "$cfgrepo" + git -C "$cfgrepo" fetch -q --depth 1 "$oci" main + git -C "$cfgrepo" show FETCH_HEAD:configs/x86_64/host.config >"$cfgrepo/host.config" + echo "kernel-build: merging host.config from $oci@$(git -C "$cfgrepo" rev-parse FETCH_HEAD)" + scripts/kconfig/merge_config.sh -m -O "$objdir" "$objdir/.config" "$cfgrepo/host.config" >/dev/null +fi enable=() for o in "${opts[@]}"; do enable+=(--enable "$o"); done scripts/config --file "$objdir/.config" "${enable[@]}" diff --git a/.github/workflows/automation-selftest.yml b/.github/workflows/automation-selftest.yml index fc7305654ec509..d4a7a3d84570a3 100644 --- a/.github/workflows/automation-selftest.yml +++ b/.github/workflows/automation-selftest.yml @@ -19,6 +19,7 @@ on: paths: - '.github/scripts/**' - '.github/workflows/upstream-rebase.yml' + - '.github/workflows/rebase-build.yml' - '.github/workflows/kernel-nightly.yml' - '.github/workflows/rebase-land.yml' - '.github/workflows/automation-selftest.yml' diff --git a/.github/workflows/rebase-build.yml b/.github/workflows/rebase-build.yml new file mode 100644 index 00000000000000..2b07628fb92109 --- /dev/null +++ b/.github/workflows/rebase-build.yml @@ -0,0 +1,117 @@ +name: Rebase build + +# The independent build of a rebase result, called by upstream-rebase.yml: +# once for Claude's rebase and, if that fails and Claude repairs the branch, +# once more for the repaired branch. +# +# It builds x86_64 and arm64 vmlinux with kernel-build.sh, taken from the +# commit the calling run started from, never from the tree under test. Each +# build's log is kept as an artifact, so a repair round can read what broke. + +on: + workflow_call: + inputs: + old: + description: The downstream tip the rebase started from + type: string + required: true + up: + description: The upstream commit the result sits on + type: string + required: true + result: + description: The commit to build + type: string + required: true + bundle: + description: The artifact holding rebase.bundle with the result + type: string + required: true + logs: + description: Prefix for the build log artifacts; the arch is appended + type: string + required: true + upstream_url: + type: string + required: true + upstream_branch: + type: string + required: true + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + arch: [x86_64, arm64] + name: build (${{ matrix.arch }}) + steps: + - name: Harden runner + uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + ref: ${{ inputs.old }} + fetch-depth: 0 + filter: blob:none + persist-credentials: false + + - name: Trusted tools + run: | + set -euo pipefail + git fetch --no-tags --filter=blob:none origin "$GITHUB_SHA" + mkdir "$RUNNER_TEMP/tools" + git archive "$GITHUB_SHA" .github/scripts | tar -x -C "$RUNNER_TEMP/tools" --strip-components=1 + + - name: Download + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ inputs.bundle }} + path: ${{ runner.temp }}/in + + - name: Check out the result + env: + UPSTREAM_URL: ${{ inputs.upstream_url }} + UPSTREAM_BRANCH: ${{ inputs.upstream_branch }} + UP: ${{ inputs.up }} + RESULT: ${{ inputs.result }} + run: | + set -euo pipefail + git fetch --no-tags "$UPSTREAM_URL" "$UPSTREAM_BRANCH" + git merge-base --is-ancestor "$UP" FETCH_HEAD + git fetch "$RUNNER_TEMP/in/rebase.bundle" rebase-result:rebase-result + [ "$(git rev-parse rebase-result)" = "$RESULT" ] || { echo "bundle does not hold $RESULT"; exit 1; } + git checkout --detach "$RESULT" + + - name: Install toolchains + run: | + set -euo pipefail + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + build-essential bc bison flex libelf-dev libssl-dev python3 \ + gcc-aarch64-linux-gnu + + - name: Build + env: + ARCH: ${{ matrix.arch }} + run: | + set -euo pipefail + bash "$RUNNER_TEMP/tools/scripts/kernel-build.sh" "$ARCH" "$RUNNER_TEMP/obj" 2>&1 | + tee "$RUNNER_TEMP/build-$ARCH.log" + + - name: Upload the log + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ inputs.logs }}-${{ matrix.arch }} + path: ${{ runner.temp }}/build-${{ matrix.arch }}.log + if-no-files-found: ignore + retention-days: 30 diff --git a/.github/workflows/upstream-rebase.yml b/.github/workflows/upstream-rebase.yml index 75056019338fdf..313c1f85b7ae61 100644 --- a/.github/workflows/upstream-rebase.yml +++ b/.github/workflows/upstream-rebase.yml @@ -10,15 +10,20 @@ name: Upstream rebase # # Nothing Claude says about its own result decides what happens to it. The # model runs in a job that holds a read-only token and cannot push. Its branch -# leaves that job as a git bundle, and two things then judge it -# independently, with scripts taken from the commit this run started from -# (the default branch), never from the tree under test: +# leaves that job as a git bundle and is built independently +# (rebase-build.yml). If that build fails, Claude gets one repair round: a +# second read-only job hands it the build logs, it fixes the branch in the +# downstream commit that broke, and the repaired branch is built again from +# scratch. The final branch is then judged, with scripts taken from the +# commit this run started from (the default branch), never from the tree +# under test: # # - verify-upstream-rebase.sh proves the series sits on the upstream tip, is # linear, lost or gained no commit, and changed the tree by exactly the # upstream delta; # - kernel-build.sh builds x86_64 and arm64 vmlinux with every downstream -# feature built in. +# feature built in, x86_64 on top of the latest Edera host config from +# edera-dev/linux-kernel-oci. # # If both pass and Claude's report asks for no decision # (report-needs-decision.sh), the tree is force-pushed to the result, pinned @@ -232,6 +237,10 @@ jobs: anthropic_organization_id: '${{ steps.ids.outputs.org }}' anthropic_service_account_id: '${{ steps.ids.outputs.svc }}' anthropic_workspace_id: '${{ steps.ids.outputs.ws }}' + # A kernel build takes far longer than the Bash tool's default + # timeout of two minutes. Without this, a build has to go to the + # background, and the run ends while Claude waits for it. + settings: '{"env": {"BASH_DEFAULT_TIMEOUT_MS": "5400000", "BASH_MAX_TIMEOUT_MS": "5400000"}}' prompt: | Rebase the downstream kernel series onto upstream by following .rebase-tools/skills/upstream-rebase/SKILL.md exactly. Read it in @@ -314,8 +323,8 @@ jobs: retention-days: 30 verify: - needs: [prepare, rebase] - if: needs.rebase.outputs.produced == 'true' + needs: [prepare, rebase, build, fix] + if: always() && needs.rebase.outputs.produced == 'true' && needs.build.result != 'cancelled' && needs.fix.result != 'cancelled' runs-on: ubuntu-latest timeout-minutes: 45 outputs: @@ -343,10 +352,11 @@ jobs: mkdir "$RUNNER_TEMP/tools" git archive "$GITHUB_SHA" .github/scripts | tar -x -C "$RUNNER_TEMP/tools" --strip-components=1 + # The repaired branch when there is one, otherwise Claude's rebase. - name: Download uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: rebase-${{ inputs.key }} + name: ${{ needs.fix.outputs.produced == 'true' && format('fix-{0}', inputs.key) || format('rebase-{0}', inputs.key) }} path: ${{ runner.temp }}/in - name: Check @@ -354,7 +364,7 @@ jobs: env: OLD: ${{ needs.prepare.outputs.old }} UP: ${{ needs.prepare.outputs.up }} - RESULT: ${{ needs.rebase.outputs.result }} + RESULT: ${{ needs.fix.outputs.produced == 'true' && needs.fix.outputs.result || needs.rebase.outputs.result }} run: | set -euo pipefail git fetch --no-tags "$UPSTREAM_URL" "$UPSTREAM_BRANCH" @@ -386,13 +396,31 @@ jobs: build: needs: [prepare, rebase] if: needs.rebase.outputs.produced == 'true' + uses: ./.github/workflows/rebase-build.yml + with: + old: ${{ needs.prepare.outputs.old }} + up: ${{ needs.prepare.outputs.up }} + result: ${{ needs.rebase.outputs.result }} + bundle: rebase-${{ inputs.key }} + logs: build-${{ inputs.key }} + upstream_url: ${{ inputs.upstream_url }} + upstream_branch: ${{ inputs.upstream_branch }} + + # The repair round. The same read-only footing as the rebase job: Claude + # gets the failed build logs and its own branch back, and nothing it says + # decides anything. A branch that comes out of here is verified and built + # again from scratch before publish looks at it. + fix: + needs: [prepare, rebase, build] + if: always() && needs.rebase.outputs.produced == 'true' && needs.build.result == 'failure' runs-on: ubuntu-latest - timeout-minutes: 120 - strategy: - fail-fast: false - matrix: - arch: [x86_64, arm64] - name: build (${{ matrix.arch }}) + timeout-minutes: 330 + permissions: + contents: read + id-token: write + outputs: + produced: ${{ steps.collect.outputs.produced }} + result: ${{ steps.collect.outputs.result }} steps: - name: Harden runner uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 @@ -411,16 +439,28 @@ jobs: run: | set -euo pipefail git fetch --no-tags --filter=blob:none origin "$GITHUB_SHA" - mkdir "$RUNNER_TEMP/tools" - git archive "$GITHUB_SHA" .github/scripts | tar -x -C "$RUNNER_TEMP/tools" --strip-components=1 + mkdir .rebase-tools + git archive "$GITHUB_SHA" .github/scripts .automation | tar -x -C .rebase-tools --strip-components=1 - - name: Download + - name: Download Claude's rebase uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: rebase-${{ inputs.key }} - path: ${{ runner.temp }}/in + path: ${{ runner.temp }}/in/rebase + + # A build that died before it started leaves no log; Claude can still + # build the branch itself. + - name: Download the build logs + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: build-${{ inputs.key }}-* + path: ${{ runner.temp }}/in/build + merge-multiple: true - - name: Check out the result + # The logs go where Claude can read them; .rebase-scratch/ is ignored + # by the kernel's .gitignore, so they cannot end up in a commit. + - name: Restore the branch env: UP: ${{ needs.prepare.outputs.up }} RESULT: ${{ needs.rebase.outputs.result }} @@ -428,8 +468,17 @@ jobs: set -euo pipefail git fetch --no-tags "$UPSTREAM_URL" "$UPSTREAM_BRANCH" git merge-base --is-ancestor "$UP" FETCH_HEAD - git fetch "$RUNNER_TEMP/in/rebase.bundle" rebase-result:rebase-result - git checkout --detach "$RESULT" + git branch upstream-target "$UP" + git fetch "$RUNNER_TEMP/in/rebase/rebase.bundle" rebase-result:rebase-result + [ "$(git rev-parse rebase-result)" = "$RESULT" ] || { echo "bundle does not hold $RESULT"; exit 1; } + git checkout -q rebase-result + mkdir -p .rebase-scratch/build-logs + for f in "$RUNNER_TEMP"/in/build/*.log; do + [ ! -e "$f" ] || cp "$f" .rebase-scratch/build-logs/ + done + cp "$RUNNER_TEMP/in/rebase/report.md" "$RUNNER_TEMP/rebase-report.md" + git config --global user.name "edera-upstream-rebase" + git config --global user.email "noreply@edera.dev" - name: Install toolchains run: | @@ -439,13 +488,106 @@ jobs: build-essential bc bison flex libelf-dev libssl-dev python3 \ gcc-aarch64-linux-gnu - - name: Build + # The rebase job has already checked that these are set. + - name: Normalize identifiers + id: ids + env: + RULE: ${{ vars.REBASE_FEDERATION_RULE_ID }} + ORG: ${{ vars.ANTHROPIC_ORGANIZATION_ID }} + SVC: ${{ vars.ANTHROPIC_SERVICE_ACCOUNT_ID }} + WS: ${{ vars.ANTHROPIC_WORKSPACE_ID }} + run: | + set -euo pipefail + strip() { printf '%s' "$1" | tr -d '[:space:]'; } + { + echo "rule=$(strip "$RULE")" + echo "org=$(strip "$ORG")" + echo "svc=$(strip "$SVC")" + echo "ws=$(strip "$WS")" + } >> "$GITHUB_OUTPUT" + + - name: Repair + id: claude + continue-on-error: true + uses: anthropics/claude-code-action@6fed3ca145920b639991cb756090506e1bcaf515 # v1.0.245 + with: + github_token: ${{ github.token }} + anthropic_federation_rule_id: '${{ steps.ids.outputs.rule }}' + anthropic_organization_id: '${{ steps.ids.outputs.org }}' + anthropic_service_account_id: '${{ steps.ids.outputs.svc }}' + anthropic_workspace_id: '${{ steps.ids.outputs.ws }}' + settings: '{"env": {"BASH_DEFAULT_TIMEOUT_MS": "5400000", "BASH_MAX_TIMEOUT_MS": "5400000"}}' + prompt: | + Your rebase of the downstream kernel series failed the workflow's + independent build. Repair it by following the "Repairing a failed + build" section of .rebase-tools/skills/upstream-rebase/SKILL.md + exactly. Read the whole skill first; the rest of it still applies. + + DOWNSTREAM=${{ env.DOWNSTREAM }} + OLD_TIP=${{ needs.prepare.outputs.old }} + UPSTREAM=${{ needs.prepare.outputs.up }} (local branch upstream-target) + RESULT=rebase-result (your rebase, checked out, at ${{ needs.rebase.outputs.result }}) + BUILD_LOGS=.rebase-scratch/build-logs (one log per arch the workflow built) + REPORT=${{ runner.temp }}/rebase-report.md (your report from the rebase) + + You cannot push and must not try. The workflow verifies and + builds your branch independently again afterwards. + claude_args: | + --model claude-opus-5-5 + --max-turns 400 + --allowedTools "Read,Grep,Glob,Edit,Write,Bash(git:*),Bash(make:*),Bash(bash .rebase-tools/scripts/verify-upstream-rebase.sh:*),Bash(bash .rebase-tools/scripts/kernel-build.sh:*),Bash(scripts/config:*),Bash(mkdir -p .rebase-scratch:*),Bash(diff:*),Bash(comm:*),Bash(sort:*),Bash(grep:*),Bash(sed:*),Bash(awk:*),Bash(cat:*),Bash(head:*),Bash(tail:*),Bash(wc:*),Bash(ls:*),Bash(cut:*),Bash(tee:*)" + + # Only a branch that still sits on the upstream tip and differs from + # the one that failed is worth building again. The report is always + # kept: when nothing was repaired, it says why. + - name: Collect the result + id: collect + if: always() env: - ARCH: ${{ matrix.arch }} - run: bash "$RUNNER_TEMP/tools/scripts/kernel-build.sh" "$ARCH" "$RUNNER_TEMP/obj" + UP: ${{ needs.prepare.outputs.up }} + BEFORE: ${{ needs.rebase.outputs.result }} + OUTCOME: ${{ steps.claude.outcome }} + run: | + set -euo pipefail + out=$RUNNER_TEMP/out + mkdir -p "$out" + cp "$RUNNER_TEMP/rebase-report.md" "$out/report.md" + if git rev-parse --verify -q refs/heads/rebase-result >/dev/null \ + && git merge-base --is-ancestor "$UP" rebase-result \ + && [ "$(git rev-parse rebase-result)" != "$UP" ] \ + && [ "$(git rev-parse rebase-result)" != "$BEFORE" ]; then + git bundle create "$out/rebase.bundle" refs/heads/rebase-result "^$UP" + echo "produced=true" >> "$GITHUB_OUTPUT" + echo "result=$(git rev-parse rebase-result)" >> "$GITHUB_OUTPUT" + else + echo "produced=false" >> "$GITHUB_OUTPUT" + echo "::warning::The repair round left no new branch (repair step: $OUTCOME)." + fi + + - name: Upload + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: fix-${{ inputs.key }} + path: ${{ runner.temp }}/out/ + if-no-files-found: error + retention-days: 30 + + rebuild: + needs: [prepare, fix] + if: always() && needs.fix.outputs.produced == 'true' + uses: ./.github/workflows/rebase-build.yml + with: + old: ${{ needs.prepare.outputs.old }} + up: ${{ needs.prepare.outputs.up }} + result: ${{ needs.fix.outputs.result }} + bundle: fix-${{ inputs.key }} + logs: rebuild-${{ inputs.key }} + upstream_url: ${{ inputs.upstream_url }} + upstream_branch: ${{ inputs.upstream_branch }} publish: - needs: [prepare, rebase, verify, build] + needs: [prepare, rebase, build, fix, verify, rebuild] if: always() && needs.prepare.outputs.run == 'true' && needs.rebase.result != 'cancelled' runs-on: ubuntu-latest timeout-minutes: 45 @@ -482,6 +624,16 @@ jobs: name: rebase-${{ inputs.key }} path: ${{ runner.temp }}/in/rebase + # The repaired branch if there is one, and the repair round's report + # whether or not it repaired anything. + - name: Download the repair + if: needs.fix.result != 'skipped' + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: fix-${{ inputs.key }} + path: ${{ runner.temp }}/in/fix + # Absent when Claude produced no branch and the checker never ran. - name: Download the checker report if: needs.verify.result == 'success' @@ -507,10 +659,12 @@ jobs: JOB_TOKEN: ${{ github.token }} PRODUCED: ${{ needs.rebase.outputs.produced }} VERIFY: ${{ needs.verify.outputs.status }} - BUILD: ${{ needs.build.result }} + BUILD: ${{ needs.fix.outputs.produced == 'true' && needs.rebuild.result || needs.build.result }} + REPAIR: ${{ needs.fix.result }} + REPAIRED: ${{ needs.fix.outputs.produced }} OLD: ${{ needs.prepare.outputs.old }} UP: ${{ needs.prepare.outputs.up }} - RESULT: ${{ needs.rebase.outputs.result }} + RESULT: ${{ needs.fix.outputs.produced == 'true' && needs.fix.outputs.result || needs.rebase.outputs.result }} PREFIX: ${{ needs.prepare.outputs.prefix }} CANDIDATE: ${{ needs.prepare.outputs.candidate }} KEY: ${{ inputs.key }} @@ -519,6 +673,9 @@ jobs: set -euo pipefail in=$RUNNER_TEMP/in report=$in/rebase/report.md + bundle=$in/rebase/rebase.bundle + [ ! -s "$in/fix/report.md" ] || report=$in/fix/report.md + [ "$REPAIRED" != "true" ] || bundle=$in/fix/rebase.bundle verify=$in/verify/verify.md summary() { cat >> "$GITHUB_STEP_SUMMARY"; } title="Nightly upstream rebase of $DOWNSTREAM failed" @@ -549,7 +706,7 @@ jobs: git fetch --no-tags "$UPSTREAM_URL" "$UPSTREAM_BRANCH" git merge-base --is-ancestor "$UP" FETCH_HEAD - git fetch "$in/rebase/rebase.bundle" rebase-result:rebase-result + git fetch "$bundle" rebase-result:rebase-result [ "$(git rev-parse rebase-result)" = "$RESULT" ] # Workflows triggered by a pull request, including the land workflow, @@ -646,7 +803,14 @@ jobs: echo echo "## Independent checks" echo - echo "Builds: **${BUILD}** (x86_64 and arm64 vmlinux with the Edera features built in; per-build results in the run)." + echo "Builds: **${BUILD}** (x86_64 vmlinux on the Edera host config and arm64 vmlinux, both with the Edera features built in; per-build results in the run)." + if [ "$REPAIRED" = "true" ]; then + echo + echo "The first build of Claude's rebase failed. Claude repaired the branch from the build logs, and the builds and checker above are of the repaired branch; its report's **Build fixes** section says what changed." + elif [ "$REPAIR" != "skipped" ]; then + echo + echo "The build failed and Claude's repair round (${REPAIR}) left no new branch; this is the unrepaired rebase." + fi echo if [ -s "$verify" ]; then cat "$verify"; else echo "The checker did not run."; fi echo