From 150e4a8dcf3c33ac3f8c7b88ca6e6bd91337a30a Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 14:29:15 +0200 Subject: [PATCH 01/25] add dco-advisor Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 117 ++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 .github/workflows/dco-advisor.yml diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml new file mode 100644 index 00000000..9ff621bf --- /dev/null +++ b/.github/workflows/dco-advisor.yml @@ -0,0 +1,117 @@ +name: DCO Advisor Bot + +on: + pull_request: + types: [opened, reopened, synchronize] + check_run: + types: [completed] + +permissions: + pull-requests: write + issues: write + +jobs: + dco_advisor: + runs-on: ubuntu-latest + steps: + - name: Handle DCO check result + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const pr = context.payload.pull_request || context.payload.check_run.pull_requests?.[0]; + if (!pr) return; + + const prNumber = pr.number; + const headSha = pr.head ? pr.head.sha : context.payload.check_run.head_sha; + + const { data: checks } = await github.rest.checks.listForRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: headSha + }); + + const dcoCheck = checks.check_runs.find(run => run.name.toLowerCase().includes("dco")); + if (!dcoCheck) return; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber + }); + + const botUsername = (await github.rest.users.getAuthenticated()).data.login; + + const existingComment = comments.find(c => + c.user.login === botUsername && + c.body.includes("") + ); + + const failureBody = ` + +❌ **DCO Check Failed** + +Hi @${pr.user.login}, your pull request has failed the Developer Certificate of Origin (DCO) check. + +Because this repository supports **remediation commits**, you can fix this without rewriting history! + +--- + +### 🛠 Quick Fix: Add a remediation commit +\`\`\`bash +git commit --allow-empty -s -m "chore: DCO remediation for failing commits" +git push +\`\`\` + +This adds an empty signed commit to declare you agree with your previous commits. + +--- + +
+🔧 Advanced: Sign off each commit + +If you prefer to re-sign your existing commits: + +**For the latest commit:** +\`\`\`bash +git commit --amend --signoff +git push --force +\`\`\` + +**For multiple commits:** +\`\`\`bash +git rebase --signoff origin/${pr.base.ref} +git push --force-with-lease +\`\`\` + +
+ +--- + +More info: [DCO Fix Guide](https://github.com/probot/dco#how-it-works) +`; + + const successBody = ` + +✅ **DCO Check Passed** + +Thanks @${pr.user.login}, all your commits are properly signed off. 🎉 +`; + + const newBody = dcoCheck.conclusion === "failure" ? failureBody : successBody; + + if (existingComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existingComment.id, + body: newBody + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: newBody + }); + } From 44c6aeb8de6413d4aa0eac360a7ab42b275ee607 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 14:31:59 +0200 Subject: [PATCH 02/25] fix yaml syntax Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 106 +++++++++++++++--------------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index 9ff621bf..ec7fc478 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -14,41 +14,40 @@ jobs: dco_advisor: runs-on: ubuntu-latest steps: - - name: Handle DCO check result - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const pr = context.payload.pull_request || context.payload.check_run.pull_requests?.[0]; - if (!pr) return; - - const prNumber = pr.number; - const headSha = pr.head ? pr.head.sha : context.payload.check_run.head_sha; - - const { data: checks } = await github.rest.checks.listForRef({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: headSha - }); - - const dcoCheck = checks.check_runs.find(run => run.name.toLowerCase().includes("dco")); - if (!dcoCheck) return; - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber - }); - - const botUsername = (await github.rest.users.getAuthenticated()).data.login; - - const existingComment = comments.find(c => - c.user.login === botUsername && - c.body.includes("") - ); - - const failureBody = ` - + - name: Handle DCO check result + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const pr = context.payload.pull_request || context.payload.check_run.pull_requests?.[0]; + if (!pr) return; + + const prNumber = pr.number; + const headSha = pr.head ? pr.head.sha : context.payload.check_run.head_sha; + + const { data: checks } = await github.rest.checks.listForRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: headSha + }); + + const dcoCheck = checks.check_runs.find(run => run.name.toLowerCase().includes("dco")); + if (!dcoCheck) return; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber + }); + + const botUsername = (await github.rest.users.getAuthenticated()).data.login; + + const existingComment = comments.find(c => + c.user.login === botUsername && + c.body.includes("") + ); + + const failureBody = ` ❌ **DCO Check Failed** Hi @${pr.user.login}, your pull request has failed the Developer Certificate of Origin (DCO) check. @@ -91,27 +90,26 @@ git push --force-with-lease More info: [DCO Fix Guide](https://github.com/probot/dco#how-it-works) `; - const successBody = ` - + const successBody = ` ✅ **DCO Check Passed** Thanks @${pr.user.login}, all your commits are properly signed off. 🎉 `; - const newBody = dcoCheck.conclusion === "failure" ? failureBody : successBody; - - if (existingComment) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existingComment.id, - body: newBody - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body: newBody - }); - } + const newBody = dcoCheck.conclusion === "failure" ? failureBody : successBody; + + if (existingComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existingComment.id, + body: newBody + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: newBody + }); + } From 4e0243a8b7c493dfff3e0b3e77bc94a9c3c92167 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 14:33:46 +0200 Subject: [PATCH 03/25] avoid js template syntax Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 98 ++++++++++++++++--------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index ec7fc478..91208214 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -47,54 +47,56 @@ jobs: c.body.includes("") ); - const failureBody = ` -❌ **DCO Check Failed** - -Hi @${pr.user.login}, your pull request has failed the Developer Certificate of Origin (DCO) check. - -Because this repository supports **remediation commits**, you can fix this without rewriting history! - ---- - -### 🛠 Quick Fix: Add a remediation commit -\`\`\`bash -git commit --allow-empty -s -m "chore: DCO remediation for failing commits" -git push -\`\`\` - -This adds an empty signed commit to declare you agree with your previous commits. - ---- - -
-🔧 Advanced: Sign off each commit - -If you prefer to re-sign your existing commits: - -**For the latest commit:** -\`\`\`bash -git commit --amend --signoff -git push --force -\`\`\` - -**For multiple commits:** -\`\`\`bash -git rebase --signoff origin/${pr.base.ref} -git push --force-with-lease -\`\`\` - -
- ---- - -More info: [DCO Fix Guide](https://github.com/probot/dco#how-it-works) -`; - - const successBody = ` -✅ **DCO Check Passed** - -Thanks @${pr.user.login}, all your commits are properly signed off. 🎉 -`; + const failureBody = [ + '', + '❌ **DCO Check Failed**', + '', + `Hi @${pr.user.login}, your pull request has failed the Developer Certificate of Origin (DCO) check.`, + '', + 'Because this repository supports **remediation commits**, you can fix this without rewriting history!', + '', + '---', + '', + '### 🛠 Quick Fix: Add a remediation commit', + '```bash', + 'git commit --allow-empty -s -m "chore: DCO remediation for failing commits"', + 'git push', + '```', + '', + 'This adds an empty signed commit to declare you agree with your previous commits.', + '', + '---', + '', + '
', + '🔧 Advanced: Sign off each commit', + '', + 'If you prefer to re-sign your existing commits:', + '', + '**For the latest commit:**', + '```bash', + 'git commit --amend --signoff', + 'git push --force', + '```', + '', + '**For multiple commits:**', + '```bash', + `git rebase --signoff origin/${pr.base.ref}`, + 'git push --force-with-lease', + '```', + '', + '
', + '', + '---', + '', + 'More info: [DCO Fix Guide](https://github.com/probot/dco#how-it-works)' + ].join('\n'); + + const successBody = [ + '', + '✅ **DCO Check Passed**', + '', + `Thanks @${pr.user.login}, all your commits are properly signed off. 🎉` + ].join('\n'); const newBody = dcoCheck.conclusion === "failure" ? failureBody : successBody; From 2682c6ea0ecdc67d9a1ee8a50ba5d7e61cd0371f Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 14:37:15 +0200 Subject: [PATCH 04/25] search comment more robust Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index 91208214..88100bb5 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -40,10 +40,7 @@ jobs: issue_number: prNumber }); - const botUsername = (await github.rest.users.getAuthenticated()).data.login; - const existingComment = comments.find(c => - c.user.login === botUsername && c.body.includes("") ); From 5c1ef004ff4f43cb27d0a8806aacac82055b3f84 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 14:38:26 +0200 Subject: [PATCH 05/25] this commit is not signed-off on purpose From a10f29f90f1b8020a964b931e88caa484c538eec Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 14:46:41 +0200 Subject: [PATCH 06/25] update the logic for sha filtering Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index 88100bb5..cbf0b83d 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -19,31 +19,42 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const pr = context.payload.pull_request || context.payload.check_run.pull_requests?.[0]; + const pr = context.payload.pull_request || context.payload.check_run?.pull_requests?.[0]; if (!pr) return; const prNumber = pr.number; - const headSha = pr.head ? pr.head.sha : context.payload.check_run.head_sha; + const baseRef = pr.base.ref; + const headSha = + context.payload.check_run?.head_sha || + pr.head?.sha; + // Get all check runs for the latest commit (headSha) const { data: checks } = await github.rest.checks.listForRef({ owner: context.repo.owner, repo: context.repo.repo, ref: headSha }); - const dcoCheck = checks.check_runs.find(run => run.name.toLowerCase().includes("dco")); + // Find the DCO check *for this specific commit* + const dcoCheck = checks.check_runs.find(run => + run.name.toLowerCase().includes("dco") && + run.head_sha === headSha + ); if (!dcoCheck) return; + // Get existing comments on the PR const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: prNumber }); + // Look for a previous bot comment const existingComment = comments.find(c => c.body.includes("") ); + // Define the failure comment body const failureBody = [ '', '❌ **DCO Check Failed**', @@ -77,7 +88,7 @@ jobs: '', '**For multiple commits:**', '```bash', - `git rebase --signoff origin/${pr.base.ref}`, + `git rebase --signoff origin/${baseRef}`, 'git push --force-with-lease', '```', '', From 761f62fc3f93d13ef1c9606450c311d5143053d6 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 15:01:38 +0200 Subject: [PATCH 07/25] this commit is without sign-off From 678070e3fd20cf9b310ecd6dd99ca2d78814472a Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 15:08:01 +0200 Subject: [PATCH 08/25] add debug output Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index cbf0b83d..f1e02d0f 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -42,6 +42,12 @@ jobs: ); if (!dcoCheck) return; + console.log("HEAD SHA:", headSha); + console.log("All check runs:"); + checks.check_runs.forEach(run => { + console.log(`- ${run.name} (${run.status}/${run.conclusion}) @ ${run.head_sha}`); + }); + // Get existing comments on the PR const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, @@ -106,6 +112,7 @@ jobs: `Thanks @${pr.user.login}, all your commits are properly signed off. 🎉` ].join('\n'); + console.log(`DCO check status for ${headSha}: ${dcoCheck.conclusion}`); const newBody = dcoCheck.conclusion === "failure" ? failureBody : successBody; if (existingComment) { From c241e9d67ec251dce6960dc8967b87f5212ee790 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 15:15:29 +0200 Subject: [PATCH 09/25] skip itself as it contains dco in name Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index f1e02d0f..9c4c3b1a 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -1,8 +1,6 @@ name: DCO Advisor Bot on: - pull_request: - types: [opened, reopened, synchronize] check_run: types: [completed] @@ -22,6 +20,13 @@ jobs: const pr = context.payload.pull_request || context.payload.check_run?.pull_requests?.[0]; if (!pr) return; + const checkRun = context.payload.check_run; + + if (!checkRun || !checkRun.name.toLowerCase().includes("dco")) { + console.log("Not a DCO check_run — skipping."); + return; + } + const prNumber = pr.number; const baseRef = pr.base.ref; const headSha = @@ -38,6 +43,7 @@ jobs: // Find the DCO check *for this specific commit* const dcoCheck = checks.check_runs.find(run => run.name.toLowerCase().includes("dco") && + !run.name.toLowerCase().includes("dco_advisor") && run.head_sha === headSha ); if (!dcoCheck) return; From f54ac23153ba1088ce47e6e9f312e7577c9930b8 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 15:19:11 +0200 Subject: [PATCH 10/25] restore pull_request on Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index 9c4c3b1a..d03a6a9d 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -1,6 +1,8 @@ name: DCO Advisor Bot on: + pull_request: + types: [opened, reopened, synchronize] check_run: types: [completed] From d63e635e12ae27b57c4970ba9eb29e1c3f327a56 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 15:26:06 +0200 Subject: [PATCH 11/25] use pr checks Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 59 +++++++++++++++++-------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index d03a6a9d..b1156fd7 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -1,8 +1,6 @@ name: DCO Advisor Bot on: - pull_request: - types: [opened, reopened, synchronize] check_run: types: [completed] @@ -22,39 +20,48 @@ jobs: const pr = context.payload.pull_request || context.payload.check_run?.pull_requests?.[0]; if (!pr) return; - const checkRun = context.payload.check_run; - - if (!checkRun || !checkRun.name.toLowerCase().includes("dco")) { - console.log("Not a DCO check_run — skipping."); - return; - } - const prNumber = pr.number; const baseRef = pr.base.ref; const headSha = context.payload.check_run?.head_sha || pr.head?.sha; - // Get all check runs for the latest commit (headSha) - const { data: checks } = await github.rest.checks.listForRef({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: headSha - }); + console.log("HEAD SHA:", headSha); + + const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); - // Find the DCO check *for this specific commit* - const dcoCheck = checks.check_runs.find(run => - run.name.toLowerCase().includes("dco") && + // Poll until DCO check has a conclusion (max 6 attempts, 30s) + let dcoCheck = null; + for (let attempt = 0; attempt < 6; attempt++) { + const { data: checks } = await github.rest.checks.listForRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: headSha + }); + + + console.log("All check runs:"); + checks.check_runs.forEach(run => { + console.log(`- ${run.name} (${run.status}/${run.conclusion}) @ ${run.head_sha}`); + }); + + dcoCheck = checks.check_runs.find(run => + run.name.toLowerCase().includes("dco") && !run.name.toLowerCase().includes("dco_advisor") && - run.head_sha === headSha - ); - if (!dcoCheck) return; + run.head_sha === headSha + ); - console.log("HEAD SHA:", headSha); - console.log("All check runs:"); - checks.check_runs.forEach(run => { - console.log(`- ${run.name} (${run.status}/${run.conclusion}) @ ${run.head_sha}`); - }); + console.log(`DCO check ${dcoCheck?.name} --> ${dcoCheck?.conclusion}`); + + if (dcoCheck?.conclusion) break; + console.log(`Waiting for DCO check... (${attempt + 1})`); + await sleep(5000); // wait 5 seconds + } + + if (!dcoCheck || !dcoCheck.conclusion) { + console.log("DCO check did not complete in time."); + return; + } // Get existing comments on the PR const { data: comments } = await github.rest.issues.listComments({ From b0656ba99a2829e1f727ba4df2d440d963d0d180 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 15:27:53 +0200 Subject: [PATCH 12/25] fix trigger Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index b1156fd7..76906980 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -1,8 +1,8 @@ name: DCO Advisor Bot on: - check_run: - types: [completed] + pull_request: + types: [opened, reopened, synchronize] permissions: pull-requests: write From 1583db38885f369963746254d1228b8bfe936fbf Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 16:02:02 +0200 Subject: [PATCH 13/25] new status check Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index 76906980..f6838d0e 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -127,8 +127,10 @@ jobs: `Thanks @${pr.user.login}, all your commits are properly signed off. 🎉` ].join('\n'); - console.log(`DCO check status for ${headSha}: ${dcoCheck.conclusion}`); - const newBody = dcoCheck.conclusion === "failure" ? failureBody : successBody; + const isFailure = ["failure", "action_required"].includes(dcoCheck.conclusion); + console.log(`DCO check conclusion for ${headSha}: ${dcoCheck.conclusion} (treated as ${isFailure ? "failure" : "success"})`); + + const newBody = isFailure ? failureBody : successBody; if (existingComment) { await github.rest.issues.updateComment({ From ef5e161243e14ab1f54e8af62ed353b3dccac9e5 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 16:05:39 +0200 Subject: [PATCH 14/25] chore: DCO remediation for failing commits Signed-off-by: Michele Dolfi From 376d50937fbbdb65512f31eb7cc289fbaac5eafc Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 16:07:08 +0200 Subject: [PATCH 15/25] DCO Remediation Commit for Michele Dolfi Signed-off-by: Michele Dolfi From b7c7d66045e580f28ecc775becd7c708b85fbbaf Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 16:11:09 +0200 Subject: [PATCH 16/25] more logs Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index f6838d0e..3f89c089 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -52,6 +52,8 @@ jobs: ); console.log(`DCO check ${dcoCheck?.name} --> ${dcoCheck?.conclusion}`); + console.log(`Actions: ${dcoCheck}`); + console.log(`Actions: ${dcoCheck?.actions}`); if (dcoCheck?.conclusion) break; console.log(`Waiting for DCO check... (${attempt + 1})`); From 9a4a851aea420977dad12b7e8f7422b30b47fb22 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 16:18:42 +0200 Subject: [PATCH 17/25] add output text to logs Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index 3f89c089..cbfcabcc 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -54,6 +54,8 @@ jobs: console.log(`DCO check ${dcoCheck?.name} --> ${dcoCheck?.conclusion}`); console.log(`Actions: ${dcoCheck}`); console.log(`Actions: ${dcoCheck?.actions}`); + const text = dcoCheck.output.text; + console.log(`Check output text: ${text}`); if (dcoCheck?.conclusion) break; console.log(`Waiting for DCO check... (${attempt + 1})`); From fc280b95b3d7dc48019170457554cc4c7f4b14ad Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 16:32:03 +0200 Subject: [PATCH 18/25] use logic scanning commits Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 159 ++++++++++++++++++------------ 1 file changed, 98 insertions(+), 61 deletions(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index cbfcabcc..3aa5ec25 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -52,10 +52,6 @@ jobs: ); console.log(`DCO check ${dcoCheck?.name} --> ${dcoCheck?.conclusion}`); - console.log(`Actions: ${dcoCheck}`); - console.log(`Actions: ${dcoCheck?.actions}`); - const text = dcoCheck.output.text; - console.log(`Check output text: ${text}`); if (dcoCheck?.conclusion) break; console.log(`Waiting for DCO check... (${attempt + 1})`); @@ -67,6 +63,104 @@ jobs: return; } + const isFailure = ["failure", "action_required"].includes(dcoCheck.conclusion); + console.log(`DCO check conclusion for ${headSha}: ${dcoCheck.conclusion} (treated as ${isFailure ? "failure" : "success"})`); + + // Parse DCO output for commit SHAs and author + let badCommits = []; + let authorName = ""; + let authorEmail = ""; + + if (isFailure) { + const { data: commits } = await github.rest.pulls.listCommits({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber, + }); + + for (const commit of commits) { + const commitMessage = commit.commit.message; + const signoffMatch = commitMessage.match(/^Signed-off-by:\s+.+<.+>$/m); + if (!signoffMatch) { + badCommits.push({ + sha: commit.sha, + authorName: commit.commit.author.name, + authorEmail: commit.commit.author.email, + }); + } + } + } + + // If multiple authors are present, you could adapt the message accordingly + // For now, we'll just use the first one + if (badCommits.length > 0) { + authorName = badCommits[0].authorName; + authorEmail = badCommits[0].authorEmail; + } + + // Generate remediation commit message if needed + let remediationSnippet = ""; + if (badCommits.length && authorEmail) { + remediationSnippet = `git commit --allow-empty -s -m "DCO Remediation Commit for ${authorName} <${authorEmail}>\n\n` + + badCommits.map(c => `I, ${c.authorName} <${c.authorEmail}>, hereby add my Signed-off-by to this commit: ${c.sha}`) + `"`; + } else { + remediationSnippet = "# Unable to auto-generate remediation message. Please check the DCO check details."; + } + + // Build comment + const commentHeader = ''; + let body = ""; + + if (isFailure) { + body = [ + commentHeader, + '❌ **DCO Check Failed**', + '', + `Hi @${username}, your pull request has failed the Developer Certificate of Origin (DCO) check.`, + '', + 'This repository supports **remediation commits**, so you can fix this without rewriting history — but you must follow the required message format.', + '', + '---', + '', + '### 🛠 Quick Fix: Add a remediation commit', + 'Run this command:', + '', + '```bash', + remediationSnippet, + 'git push', + '```', + '', + '---', + '', + '
', + '🔧 Advanced: Sign off each commit directly', + '', + '**For the latest commit:**', + '```bash', + 'git commit --amend --signoff', + 'git push --force-with-lease', + '```', + '', + '**For multiple commits:**', + '```bash', + `git rebase --signoff origin/${baseRef}`, + 'git push --force-with-lease', + '```', + '', + '
', + '', + 'More info: [DCO Fix Guide](https://github.com/probot/dco#how-it-works)' + ].join('\n'); + } else { + body = [ + commentHeader, + '✅ **DCO Check Passed**', + '', + `Thanks @${username}, all your commits are properly signed off. 🎉` + ].join('\n'); + } + // Get existing comments on the PR const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, @@ -79,63 +173,6 @@ jobs: c.body.includes("") ); - // Define the failure comment body - const failureBody = [ - '', - '❌ **DCO Check Failed**', - '', - `Hi @${pr.user.login}, your pull request has failed the Developer Certificate of Origin (DCO) check.`, - '', - 'Because this repository supports **remediation commits**, you can fix this without rewriting history!', - '', - '---', - '', - '### 🛠 Quick Fix: Add a remediation commit', - '```bash', - 'git commit --allow-empty -s -m "chore: DCO remediation for failing commits"', - 'git push', - '```', - '', - 'This adds an empty signed commit to declare you agree with your previous commits.', - '', - '---', - '', - '
', - '🔧 Advanced: Sign off each commit', - '', - 'If you prefer to re-sign your existing commits:', - '', - '**For the latest commit:**', - '```bash', - 'git commit --amend --signoff', - 'git push --force', - '```', - '', - '**For multiple commits:**', - '```bash', - `git rebase --signoff origin/${baseRef}`, - 'git push --force-with-lease', - '```', - '', - '
', - '', - '---', - '', - 'More info: [DCO Fix Guide](https://github.com/probot/dco#how-it-works)' - ].join('\n'); - - const successBody = [ - '', - '✅ **DCO Check Passed**', - '', - `Thanks @${pr.user.login}, all your commits are properly signed off. 🎉` - ].join('\n'); - - const isFailure = ["failure", "action_required"].includes(dcoCheck.conclusion); - console.log(`DCO check conclusion for ${headSha}: ${dcoCheck.conclusion} (treated as ${isFailure ? "failure" : "success"})`); - - const newBody = isFailure ? failureBody : successBody; - if (existingComment) { await github.rest.issues.updateComment({ owner: context.repo.owner, From f1b84a8e913fdf4bc2550bdef881df9cac175ca0 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 16:34:30 +0200 Subject: [PATCH 19/25] print html_url Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index 3aa5ec25..b867860c 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -52,6 +52,7 @@ jobs: ); console.log(`DCO check ${dcoCheck?.name} --> ${dcoCheck?.conclusion}`); + console.log(`DCO check HTML url: ${dcoCheck?.html_url}`); if (dcoCheck?.conclusion) break; console.log(`Waiting for DCO check... (${attempt + 1})`); From fa29e4bf1a2f58abeb2fd26f3d8096bb3f54654e Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 16:38:54 +0200 Subject: [PATCH 20/25] fix map function Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index b867860c..53b98582 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -73,6 +73,7 @@ jobs: let authorEmail = ""; if (isFailure) { + console.log(`Now checking all commits with failed sign-off`); const { data: commits } = await github.rest.pulls.listCommits({ owner: context.repo.owner, repo: context.repo.repo, @@ -83,6 +84,7 @@ jobs: const commitMessage = commit.commit.message; const signoffMatch = commitMessage.match(/^Signed-off-by:\s+.+<.+>$/m); if (!signoffMatch) { + console.log(`Bad commit found ${commit.sha}`) badCommits.push({ sha: commit.sha, authorName: commit.commit.author.name, @@ -103,7 +105,7 @@ jobs: let remediationSnippet = ""; if (badCommits.length && authorEmail) { remediationSnippet = `git commit --allow-empty -s -m "DCO Remediation Commit for ${authorName} <${authorEmail}>\n\n` + - badCommits.map(c => `I, ${c.authorName} <${c.authorEmail}>, hereby add my Signed-off-by to this commit: ${c.sha}`) + badCommits.map(c => `I, ${c.authorName} <${c.authorEmail}>, hereby add my Signed-off-by to this commit: ${c.sha}`).join('\n') + `"`; } else { remediationSnippet = "# Unable to auto-generate remediation message. Please check the DCO check details."; From 28cf58693351b6b81003c653043c378b49fb761c Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 16:40:33 +0200 Subject: [PATCH 21/25] add username Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index 53b98582..592b354a 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -25,6 +25,7 @@ jobs: const headSha = context.payload.check_run?.head_sha || pr.head?.sha; + const username = pr.user.login; console.log("HEAD SHA:", headSha); From 72ff13ca4441dd69b1ecacaa84d67b1877c5961e Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 16:41:34 +0200 Subject: [PATCH 22/25] fix body arg Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index 592b354a..88b5eaa7 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -182,13 +182,13 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, comment_id: existingComment.id, - body: newBody + body: body }); } else { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: prNumber, - body: newBody + body: body }); } From fb3f1f841c073010ed4a8966e499a17c3f2b5349 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 16:45:22 +0200 Subject: [PATCH 23/25] add link to dco details Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index 88b5eaa7..03f2ecc6 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -52,8 +52,6 @@ jobs: run.head_sha === headSha ); - console.log(`DCO check ${dcoCheck?.name} --> ${dcoCheck?.conclusion}`); - console.log(`DCO check HTML url: ${dcoCheck?.html_url}`); if (dcoCheck?.conclusion) break; console.log(`Waiting for DCO check... (${attempt + 1})`); @@ -72,9 +70,9 @@ jobs: let badCommits = []; let authorName = ""; let authorEmail = ""; + let moreInfo = `More info: [DCO check report](${dcoCheck?.html_url})`; if (isFailure) { - console.log(`Now checking all commits with failed sign-off`); const { data: commits } = await github.rest.pulls.listCommits({ owner: context.repo.owner, repo: context.repo.repo, @@ -154,7 +152,7 @@ jobs: '', '', '', - 'More info: [DCO Fix Guide](https://github.com/probot/dco#how-it-works)' + moreInfo ].join('\n'); } else { body = [ From aeacca28095dfad81d3364f633fc7f576ae83e6a Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Mon, 16 Jun 2025 16:46:29 +0200 Subject: [PATCH 24/25] DCO Remediation Commit for Michele Dolfi I, Michele Dolfi , hereby add my Signed-off-by to this commit: 5c1ef004ff4f43cb27d0a8806aacac82055b3f84 I, Michele Dolfi , hereby add my Signed-off-by to this commit: 761f62fc3f93d13ef1c9606450c311d5143053d6 Signed-off-by: Michele Dolfi From 2af739cd458e7aab2373e8debc972eaf530f0c00 Mon Sep 17 00:00:00 2001 From: Michele Dolfi Date: Tue, 17 Jun 2025 07:23:17 +0200 Subject: [PATCH 25/25] use correct event Signed-off-by: Michele Dolfi --- .github/workflows/dco-advisor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dco-advisor.yml b/.github/workflows/dco-advisor.yml index 03f2ecc6..dfb0b918 100644 --- a/.github/workflows/dco-advisor.yml +++ b/.github/workflows/dco-advisor.yml @@ -1,7 +1,7 @@ name: DCO Advisor Bot on: - pull_request: + pull_request_target: types: [opened, reopened, synchronize] permissions: