This is an automated email from the ASF dual-hosted git repository.

Croway pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new a8c6b178b158 CI: Upload regen patch artifact when the 
uncommitted-changes check fails
a8c6b178b158 is described below

commit a8c6b178b158315f0598adca711208091032c528
Author: croway <[email protected]>
AuthorDate: Mon Aug 31 13:49:54 2026 +0200

    CI: Upload regen patch artifact when the uncommitted-changes check fails
    
    When the Build and test workflow fails because regenerated files are not
    committed, save the regenerated changes as a regen-patch artifact
    (patch + PR number) instead of discarding them, and extend the CI
    commenter to post apply instructions on the PR. Anyone with push access
    to the PR branch (the author, or committers via Allow edits from
    maintainers) can then fix the PR in seconds without re-running the
    ~15 minute regen build locally. A later clean run marks the comment as
    resolved.
    
    Co-authored-by: Claude Fable 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01WLj5xMfVRiqGwEge1JKC81
---
 .github/workflows/pr-build-main.yml     |  30 +++++++-
 .github/workflows/pr-test-commenter.yml | 122 ++++++++++++++++++++++++++++++++
 2 files changed, 151 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/pr-build-main.yml 
b/.github/workflows/pr-build-main.yml
index c4eae869ee77..e066df2a41ff 100644
--- a/.github/workflows/pr-build-main.yml
+++ b/.github/workflows/pr-build-main.yml
@@ -161,7 +161,35 @@ jobs:
         if: ${{ !inputs.skip_full_build }}
         shell: bash
         run: |
-          [[ -z $(git status --porcelain) ]] || { echo 'There are uncommitted 
changes'; git status; echo; echo; git diff; exit 1; }
+          if [[ -z $(git status --porcelain) ]]; then
+            exit 0
+          fi
+          echo 'There are uncommitted changes'
+          git status
+          echo
+          echo
+          git diff
+          # Save the regenerated changes as a patch (with the PR number, since
+          # the ci-comment artifact is not produced on this failure path) so
+          # they can be applied to the PR branch without re-running the regen
+          # build locally. The commenter workflow posts apply instructions.
+          git add -A
+          mkdir -p regen-patch-artifact
+          git diff --cached --binary > regen-patch-artifact/regen.patch
+          echo "${{ github.event.number || inputs.pr_number }}" > 
regen-patch-artifact/pr-number
+          exit 1
+      # Both non-experimental matrix entries regenerate the same files, so the
+      # patch content is identical across JDKs — last writer wins, same as the
+      # ci-comment artifact below.
+      - name: Upload regen patch artifact
+        if: >
+          failure() && !matrix.experimental &&
+          hashFiles('regen-patch-artifact/regen.patch') != ''
+        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a 
# v7.0.1
+        with:
+          name: regen-patch
+          path: regen-patch-artifact/
+          overwrite: true
       - name: mvn test
         uses: ./.github/actions/incremental-build
         with:
diff --git a/.github/workflows/pr-test-commenter.yml 
b/.github/workflows/pr-test-commenter.yml
index 60dc485d08fd..90eb416c39cc 100644
--- a/.github/workflows/pr-test-commenter.yml
+++ b/.github/workflows/pr-test-commenter.yml
@@ -119,3 +119,125 @@ jobs:
             } catch (error) {
               core.warning(`Failed to post CI test summary comment: 
${error.message}`);
             }
+      - name: Download regen patch artifact
+        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # 
v9.0.0
+        with:
+          script: |
+            const artifacts = await 
github.rest.actions.listWorkflowRunArtifacts({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              run_id: ${{ github.event.workflow_run.id }},
+            });
+            const match = artifacts.data.artifacts.find(a => a.name === 
'regen-patch');
+            if (!match) {
+              core.info('No regen-patch artifact found, skipping');
+              return;
+            }
+            const download = await github.rest.actions.downloadArtifact({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              artifact_id: match.id,
+              archive_format: 'zip',
+            });
+            const fs = require('fs');
+            fs.writeFileSync('${{ github.workspace }}/regen-patch.zip', 
Buffer.from(download.data));
+      - name: Extract regen patch artifact
+        run: |
+          if [ -f regen-patch.zip ]; then
+            unzip -o regen-patch.zip -d regen-patch-artifact
+          fi
+      - name: Post or update regen patch comment
+        if: always()
+        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # 
v9.0.0
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          script: |
+            const fs = require('fs');
+
+            const regenPatchFile = 'regen-patch-artifact/regen.patch';
+            const regenPrFile = 'regen-patch-artifact/pr-number';
+            const ciPrFile = 'ci-comment-artifact/pr-number';
+
+            const regenNeeded = fs.existsSync(regenPatchFile);
+
+            // The regen artifact carries its own PR number (the ci-comment
+            // artifact is not produced when the build fails at the regen
+            // check); on clean runs fall back to the ci-comment artifact so an
+            // earlier warning comment can be marked resolved.
+            let prNumber = 0;
+            if (fs.existsSync(regenPrFile)) {
+              prNumber = parseInt(fs.readFileSync(regenPrFile, 'utf8').trim(), 
10);
+            } else if (fs.existsSync(ciPrFile)) {
+              prNumber = parseInt(fs.readFileSync(ciPrFile, 'utf8').trim(), 
10);
+            }
+            if (!prNumber) {
+              core.info('No PR number available, skipping regen patch 
comment');
+              return;
+            }
+
+            const marker = '<!-- regen-patch -->';
+            const runId = ${{ github.event.workflow_run.id }};
+            const runUrl = `${{ github.server_url }}/${{ github.repository 
}}/actions/runs/${runId}`;
+            const repoSlug = `${context.repo.owner}/${context.repo.repo}`;
+
+            let body;
+            if (regenNeeded) {
+              body = [
+                marker,
+                '### :warning: Generated files are out of date',
+                '',
+                `The [CI build](${runUrl}) regenerated files that are not 
committed on this PR.`,
+                'The missing changes are available as the `regen-patch` 
artifact of that run.',
+                '',
+                'Anyone with push access to this branch (the PR author, or a 
committer when',
+                '*Allow edits from maintainers* is enabled) can apply and push 
them:',
+                '',
+                '```bash',
+                `gh pr checkout ${prNumber} --repo ${repoSlug}`,
+                `gh run download ${runId} --repo ${repoSlug} -n regen-patch`,
+                'git apply --index regen.patch && rm regen.patch pr-number',
+                'git commit -m "Regen" && git push',
+                '```',
+                '',
+                'Alternatively, run `./etc/scripts/regen.sh` locally and 
commit the resulting changes.',
+                'If the patch does not apply cleanly, update the branch from 
the base branch first —',
+                'the patch was generated against the merge with the base 
branch.',
+              ].join('\n');
+            } else {
+              body = [
+                marker,
+                '### :white_check_mark: Generated files are up to date',
+                '',
+                `An earlier CI run reported uncommitted generated changes; the 
[latest run](${runUrl}) no longer does.`,
+              ].join('\n');
+            }
+
+            try {
+              const { data: comments } = await 
github.rest.issues.listComments({
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                issue_number: prNumber,
+              });
+              const existing = comments.find(c => c.body && 
c.body.includes(marker));
+
+              if (existing) {
+                if (!regenNeeded && 
existing.body.includes(':white_check_mark:')) {
+                  return; // already marked resolved
+                }
+                await github.rest.issues.updateComment({
+                  owner: context.repo.owner,
+                  repo: context.repo.repo,
+                  comment_id: existing.id,
+                  body: body,
+                });
+              } else if (regenNeeded) {
+                await github.rest.issues.createComment({
+                  owner: context.repo.owner,
+                  repo: context.repo.repo,
+                  issue_number: prNumber,
+                  body: body,
+                });
+              }
+            } catch (error) {
+              core.warning(`Failed to post regen patch comment: 
${error.message}`);
+            }

Reply via email to