Skip to content

Commit 17afc27

Browse files
authored
Checkout base branch instead of PR head in build workflow (#204)
1 parent 5db5a5e commit 17afc27

File tree

2 files changed

+167
-53
lines changed

2 files changed

+167
-53
lines changed

.github/workflows/build-pr-cmk.yml

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
name: Build cmk binaries on PR
1919

2020
on:
21-
pull_request_target:
21+
pull_request:
2222
types: [opened, synchronize, reopened]
2323

2424
concurrency:
@@ -36,10 +36,9 @@ jobs:
3636
outcome: ${{ steps.meta.outputs.outcome }}
3737
artifact_url: ${{ steps.meta.outputs.artifact_url }}
3838
steps:
39-
- name: Checkout PR HEAD
39+
- name: Checkout PR code
4040
uses: actions/checkout@v4
4141
with:
42-
ref: ${{ github.event.pull_request.head.sha }}
4342
persist-credentials: false
4443

4544
- name: Set up Go
@@ -68,53 +67,3 @@ jobs:
6867
run: |
6968
echo "outcome=${{ steps.build.outcome }}" >> $GITHUB_OUTPUT
7069
echo "artifact_url=${{ steps.upload_artifact.outputs.artifact-url }}" >> $GITHUB_OUTPUT
71-
72-
comment:
73-
if: always()
74-
needs: build
75-
permissions:
76-
contents: read
77-
issues: write
78-
pull-requests: write
79-
runs-on: ubuntu-24.04
80-
steps:
81-
- name: Comment or update cmk build artifact on PR
82-
uses: actions/github-script@v7
83-
with:
84-
script: |
85-
const { execSync } = require('child_process');
86-
87-
const issue_number = context.payload.pull_request.number;
88-
const identifier = "cmk-build-artifact-comment";
89-
90-
const owner = context.payload.repository.owner.login; // base repo (pull_request_target)
91-
const repo = context.payload.repository.name;
92-
93-
const buildOutcome = "${{ needs.build.outputs.outcome }}";
94-
const artifactUrl = "${{ needs.build.outputs.artifact_url }}";
95-
const runId = "${{ github.run_id }}";
96-
97-
core.info(`Will comment on ${owner}/${repo}#${issue_number}`);
98-
core.info(`Outcome=${buildOutcome || '(empty)'} Artifact=${artifactUrl || '(none)'}`);
99-
100-
let body = `<!-- ${identifier} -->\n`;
101-
if (buildOutcome === 'success' && artifactUrl) {
102-
const expiryDate = execSync("date -d '+10 days' '+%B %d, %Y'").toString().trim();
103-
body += `✅ Build complete for PR #${issue_number}.\n\n`;
104-
body += `🔗 Download the [cmk binaries](${artifactUrl}) (expires on ${expiryDate})`;
105-
} else {
106-
body += `❌ Build failed for PR #${issue_number}.\n\n`;
107-
body += `See the run: https://github.com/${owner}/${repo}/actions/runs/${runId}`;
108-
}
109-
110-
const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number });
111-
const existing = comments.find(c => c.user.login === 'github-actions[bot]' && c.body.includes(identifier));
112-
113-
if (existing) {
114-
core.info(`Updating comment id ${existing.id}`);
115-
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
116-
} else {
117-
core.info(`Creating new comment`);
118-
await github.rest.issues.createComment({ owner, repo, issue_number, body });
119-
}
120-
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
name: Comment on PR build results
19+
20+
on:
21+
workflow_run:
22+
workflows: ["Build cmk binaries on PR"]
23+
types:
24+
- completed
25+
26+
permissions:
27+
contents: read
28+
issues: write
29+
pull-requests: write
30+
actions: read
31+
32+
jobs:
33+
comment:
34+
runs-on: ubuntu-24.04
35+
if: >
36+
github.event.workflow_run.event == 'pull_request'
37+
steps:
38+
- name: Download artifact metadata
39+
uses: actions/github-script@v7
40+
id: artifact-metadata
41+
with:
42+
script: |
43+
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
44+
owner: context.repo.owner,
45+
repo: context.repo.repo,
46+
run_id: context.payload.workflow_run.id,
47+
});
48+
49+
const prArtifact = artifacts.data.artifacts.find(a => a.name.startsWith('cmk-binaries.pr'));
50+
51+
if (prArtifact) {
52+
const prNumber = prArtifact.name.match(/pr(\d+)/)?.[1];
53+
return {
54+
artifact_url: prArtifact.archive_download_url,
55+
pr_number: prNumber,
56+
conclusion: context.payload.workflow_run.conclusion
57+
};
58+
}
59+
60+
return {
61+
pr_number: null,
62+
conclusion: context.payload.workflow_run.conclusion
63+
};
64+
65+
- name: Get PR number from workflow run
66+
id: get-pr
67+
uses: actions/github-script@v7
68+
env:
69+
METADATA: ${{ steps.artifact-metadata.outputs.result }}
70+
with:
71+
script: |
72+
// Primary source: PRs attached to the workflow_run (for pull_request-triggered runs)
73+
const runPRs = context.payload.workflow_run.pull_requests;
74+
if (runPRs && runPRs.length > 0) {
75+
return runPRs[0].number;
76+
}
77+
// Fallback 1: PR number discovered from artifact metadata
78+
let metadata = {};
79+
if (process.env.METADATA) {
80+
try {
81+
metadata = JSON.parse(process.env.METADATA);
82+
} catch (e) {
83+
core.warning(`Failed to parse artifact metadata: ${e.message}`);
84+
}
85+
}
86+
if (metadata.pr_number) {
87+
return metadata.pr_number;
88+
}
89+
// Fallback 2: look up PRs associated with the workflow run head SHA
90+
const associated = await github.rest.repos.listPullRequestsAssociatedWithCommit({
91+
owner: context.repo.owner,
92+
repo: context.repo.repo,
93+
commit_sha: context.payload.workflow_run.head_sha,
94+
});
95+
if (associated.data.length > 0) {
96+
return associated.data[0].number;
97+
}
98+
return null;
99+
100+
- name: Comment or update build result on PR
101+
uses: actions/github-script@v7
102+
with:
103+
script: |
104+
const { execSync } = require('child_process');
105+
const prNumber = ${{ steps.get-pr.outputs.result }};
106+
107+
if (!prNumber) {
108+
core.warning('Could not determine PR number, skipping comment');
109+
return;
110+
}
111+
112+
const identifier = "cmk-build-artifact-comment";
113+
const owner = context.repo.owner;
114+
const repo = context.repo.repo;
115+
const conclusion = '${{ github.event.workflow_run.conclusion }}';
116+
const runId = '${{ github.event.workflow_run.id }}';
117+
const runUrl = `https://github.com/${owner}/${repo}/actions/runs/${runId}`;
118+
119+
core.info(`Commenting on PR #${prNumber}`);
120+
core.info(`Build conclusion: ${conclusion}`);
121+
122+
let body = `<!-- ${identifier} -->\n`;
123+
124+
if (conclusion === 'success') {
125+
const expiryDate = execSync("date -d '+10 days' '+%B %d, %Y'").toString().trim();
126+
body += `✅ Build complete for PR #${prNumber}.\n\n`;
127+
body += `📦 Binary artifacts are available in the [workflow run](${runUrl}) (expires on ${expiryDate}).\n\n`;
128+
body += `> **Note:** Download artifacts by clicking on the workflow run link above, then scroll to the "Artifacts" section.\n`;
129+
body += `> _Artifacts from PR builds are for testing only and may contain unreviewed, malicious code._`;
130+
} else if (conclusion === 'failure') {
131+
body += `❌ Build failed for PR #${prNumber}.\n\n`;
132+
body += `See the [workflow run](${runUrl}) for details.`;
133+
} else {
134+
body += `⚠️ Build ${conclusion} for PR #${prNumber}.\n\n`;
135+
body += `See the [workflow run](${runUrl}) for details.`;
136+
}
137+
138+
const { data: comments } = await github.rest.issues.listComments({
139+
owner,
140+
repo,
141+
issue_number: prNumber
142+
});
143+
144+
const existing = comments.find(c =>
145+
c.user.login === 'github-actions[bot]' &&
146+
c.body.includes(identifier)
147+
);
148+
149+
if (existing) {
150+
core.info(`Updating existing comment id ${existing.id}`);
151+
await github.rest.issues.updateComment({
152+
owner,
153+
repo,
154+
comment_id: existing.id,
155+
body
156+
});
157+
} else {
158+
core.info(`Creating new comment`);
159+
await github.rest.issues.createComment({
160+
owner,
161+
repo,
162+
issue_number: prNumber,
163+
body
164+
});
165+
}

0 commit comments

Comments
 (0)