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}`);
+ }