-
Notifications
You must be signed in to change notification settings - Fork 1
ci: add workflow to trigger specific builds on PRs by comments #152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,251 @@ | ||
| name: Trigger build on PR comment | ||
| # Trigger a build when a comment is made on a PR with /build in the comment body | ||
| # We can do the following builds: linux, windows, macos | ||
| # e.g. /build linux=<targets> | ||
| # e.g. /build windows | ||
| # e.g. /build macos | ||
| # e.g. /build linux=<targets> windows | ||
| # e.g. /build linux=<targets> macos windows | ||
| # | ||
| # Linux targets can be comma-separated list of distros, e.g. ubuntu/20.04,centos/7 | ||
| # e.g. /build linux=ubuntu/20.04,centos/7 windows | ||
| # e.g. /build linux=ubuntu/20.04 macos | ||
| # e.g. /build macos windows | ||
| # e.g. /build linux=centos/7 | ||
| # | ||
| # If no platform is specified then nothing is done | ||
| on: | ||
| issue_comment: | ||
| jobs: | ||
| pr-build-comment: | ||
| name: Build on PR comment | ||
| # Ignore comments that are not on PRs (i.e. issues) | ||
| # Trigger only if /build is present in the comment body | ||
| if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '/build')}} | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| pr-number: ${{ github.event.issue.number }} | ||
| # Builds should be run (they may fail) | ||
| should-run: ${{ steps.parse.outputs.should_run }} | ||
| # Type of build to run | ||
| build-windows: ${{ steps.parse.outputs.windows }} | ||
| build-macos: ${{ steps.parse.outputs.macos }} | ||
| build-linux: ${{ steps.parse.outputs.linux }} | ||
| # Linux targets matrix to build | ||
| linux-targets-json: ${{ steps.parse.outputs.linux_targets_json }} | ||
| steps: | ||
| - name: Log useful info | ||
| run: | | ||
| echo "PR Number is ${{ github.event.issue.number }}" | ||
| echo "Comment ID is ${{ github.event.comment.id }}" | ||
| echo "Comment body is $BODY" | ||
| shell: bash | ||
| env: | ||
| BODY: ${{ github.event.comment.body }} | ||
|
|
||
| - name: Debug | ||
| uses: raven-actions/debug@v1 | ||
|
|
||
| - name: Parse /build comment | ||
| id: parse | ||
| uses: actions/github-script@v8 | ||
| with: | ||
| script: | | ||
| const body = (context.payload.comment?.body || '').trim(); | ||
| const firstLine = body.split('\n')[0].trim(); | ||
| const out = { | ||
| windows: false, | ||
| macos: false, | ||
| linuxTargets: [], | ||
| }; | ||
|
|
||
| // Must start with '/build' | ||
| if (!/^\/build\b/i.test(firstLine)) { | ||
| core.setOutput('should_run', 'false'); | ||
| core.setOutput('windows', 'false'); | ||
| core.setOutput('macos', 'false'); | ||
| core.setOutput('linux', 'false'); | ||
| core.setOutput('linux_targets_json', '[]'); | ||
| return; | ||
| } | ||
|
|
||
| const tail = firstLine.replace(/^\/build\b/i, '').trim(); | ||
| if (tail.length > 0) { | ||
| const tokens = tail.split(/\s+/).filter(Boolean); | ||
| for (const raw of tokens) { | ||
| const lower = raw.toLowerCase(); | ||
| if (lower === 'windows') { | ||
| out.windows = true; | ||
| } else if (lower === 'macos' || lower === 'macosx' || lower === 'osx') { | ||
| out.macos = true; | ||
| } else if (lower.startsWith('linux=')) { | ||
| // Keep original token to preserve any casing/symbols after '=' | ||
| const rhs = raw.slice(raw.indexOf('=') + 1); | ||
| const arr = rhs.split(',').map(s => s.trim()).filter(Boolean); | ||
| out.linuxTargets.push(...arr); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const shouldRun = out.windows || out.macos || out.linuxTargets.length > 0; | ||
|
|
||
| core.setOutput('should_run', shouldRun ? 'true' : 'false'); | ||
| core.setOutput('windows', out.windows ? 'true' : 'false'); | ||
| core.setOutput('macos', out.macos ? 'true' : 'false'); | ||
| core.setOutput('linux', out.linuxTargets.length > 0 ? 'true' : 'false'); | ||
| core.setOutput('linux_targets_json', JSON.stringify(out.linuxTargets)); | ||
|
|
||
| pr-build-get-metadata: | ||
| name: Get build metadata | ||
| needs: pr-build-comment | ||
| if: needs.pr-build-comment.outputs.should-run == 'true' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| outputs: | ||
| version: ${{ steps.set-version.outputs.version }} | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| ref: refs/pull/${{ github.event.issue.number }}/merge | ||
|
|
||
| - name: Get the versions to use | ||
| # Look for the default line in the Dockerfile | ||
| id: set-version | ||
| run: | | ||
| VERSION=$(grep "ARG FLUENTDO_AGENT_VERSION=" Dockerfile.ubi | cut -d '=' -s -f 2 -) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Rule violated: Require Safe Shell Scripts (set -euo pipefail or explicit env checks) Shell script lacks Prompt for AI agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cubic-dev-ai is this just meaning for pipefail? Github Actions should automatically handle There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @patrick-stephens No, it's not just about GitHub does set Adding - name: Get the versions to use
id: set-version
run: |
set -euo pipefail
VERSION=$(grep "ARG FLUENTDO_AGENT_VERSION=" Dockerfile.ubi | cut -d '=' -s -f 2 -)
if [[ -z "$VERSION" ]]; then
echo "Error: Version not found in Dockerfile.ubi"
exit 1
fi
echo "Using version: $VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
shell: bashThat way, if the file's missing or the ARG isn't there, the job fails early with a clear message instead of propagating junk. |
||
| echo "Using version: $VERSION" | ||
| echo "version=$VERSION" >> $GITHUB_OUTPUT | ||
| shell: bash | ||
|
|
||
| pr-build-windows: | ||
| name: Build Windows | ||
| needs: | ||
| - pr-build-comment | ||
| - pr-build-get-metadata | ||
| if: needs.pr-build-comment.outputs.should-run == 'true' && needs.pr-build-comment.outputs.build-windows == 'true' | ||
| uses: ./.github/workflows/call-build-windows-packages.yaml | ||
| with: | ||
| version: ${{ needs.pr-build-get-metadata.outputs.version }} | ||
| ref: refs/pull/${{ needs.pr-build-comment.outputs.pr-number }}/merge | ||
|
|
||
| pr-build-macos: | ||
| name: Build macOS | ||
| needs: | ||
| - pr-build-comment | ||
| - pr-build-get-metadata | ||
| if: needs.pr-build-comment.outputs.should-run == 'true' && needs.pr-build-comment.outputs.build-macos == 'true' | ||
| uses: ./.github/workflows/call-build-macos-packages.yaml | ||
| with: | ||
| version: ${{ needs.pr-build-get-metadata.outputs.version }} | ||
| ref: refs/pull/${{ needs.pr-build-comment.outputs.pr-number }}/merge | ||
|
|
||
| pr-build-linux: | ||
| name: Build Linux | ||
| needs: | ||
| - pr-build-comment | ||
| - pr-build-get-metadata | ||
| if: needs.pr-build-comment.outputs.should-run == 'true' && needs.pr-build-comment.outputs.build-linux != '' | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Inconsistent condition for Linux build check. The output is set to Prompt for AI agents |
||
| uses: ./.github/workflows/call-build-linux-packages.yaml | ||
| with: | ||
| version: ${{ needs.pr-build-get-metadata.outputs.version }} | ||
| ref: refs/pull/${{ needs.pr-build-comment.outputs.pr-number }}/merge | ||
| target-matrix: ${{ needs.pr-build-comment.outputs.linux-targets-json }} | ||
|
|
||
| pr-build-comment-response: | ||
| name: Respond to PR comment | ||
| if: needs.pr-build-comment.outputs.should-run == 'true' | ||
| needs: | ||
| - pr-build-comment | ||
| - pr-build-windows | ||
| - pr-build-macos | ||
| - pr-build-linux | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Post results as PR comment | ||
| uses: actions/github-script@v8 | ||
| env: | ||
| WINDOWS_REQUESTED: ${{ needs.pr-build-comment.outputs.build-windows }} | ||
| MACOS_REQUESTED: ${{ needs.pr-build-comment.outputs.build-macos }} | ||
| LINUX_REQUESTED: ${{ needs.pr-build-comment.outputs.build-linux }} | ||
| LINUX_TARGETS_JSON: ${{ needs.pr-build-comment.outputs.linux-targets-json }} | ||
| WINDOWS_RESULT: ${{ needs.pr-build-windows.result }} | ||
| MACOS_RESULT: ${{ needs.pr-build-macos.result }} | ||
| LINUX_RESULT: ${{ needs.pr-build-linux.result }} | ||
| with: | ||
| script: | | ||
| const {owner, repo} = context.repo; | ||
| const run_id = context.runId; | ||
|
|
||
| // Gather job URLs | ||
| const pages = await github.paginate(github.rest.actions.listJobsForWorkflowRun, { | ||
| owner, repo, run_id, per_page: 100 | ||
| }); | ||
| const byName = {}; | ||
| for (const job of pages) { | ||
| for (const j of job.jobs || []) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Incorrect iteration over paginated results. Prompt for AI agents |
||
| // Keep the first occurrence | ||
| if (!byName[j.name]) { | ||
| byName[j.name] = j.html_url; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| function statusEmoji(result) { | ||
| if (!result) return '➖'; | ||
| const r = String(result).toLowerCase(); | ||
| if (r === 'success') return '✅'; | ||
| if (r === 'failure') return '❌'; | ||
| if (r === 'cancelled') return '🛑'; | ||
| if (r === 'skipped') return '⏭️'; | ||
| return '❓'; | ||
| } | ||
|
|
||
| const requested = { | ||
| windows: process.env.WINDOWS_REQUESTED === 'true', | ||
| macos: process.env.MACOS_REQUESTED === 'true', | ||
| linux: process.env.LINUX_REQUESTED === 'true', | ||
| }; | ||
|
|
||
| const results = { | ||
| windows: process.env.WINDOWS_RESULT || 'skipped', | ||
| macos: process.env.MACOS_RESULT || 'skipped', | ||
| linux: process.env.LINUX_RESULT || 'skipped', | ||
| }; | ||
|
|
||
| const linuxTargets = (() => { | ||
| try { return JSON.parse(process.env.LINUX_TARGETS_JSON || '[]'); } | ||
| catch { return []; } | ||
| })(); | ||
|
|
||
| const rows = []; | ||
| if (requested.windows) { | ||
| const name = 'Build Windows packages'; | ||
| const url = byName[name] || `${context.serverUrl}/${owner}/${repo}/actions/runs/${run_id}`; | ||
| rows.push(`- Windows: ${statusEmoji(results.windows)} (${results.windows}) — [Job logs](${url})`); | ||
| } | ||
| if (requested.macos) { | ||
| const name = 'Build macOS packages'; | ||
| const url = byName[name] || `${context.serverUrl}/${owner}/${repo}/actions/runs/${run_id}`; | ||
| rows.push(`- macOS: ${statusEmoji(results.macos)} (${results.macos}) — [Job logs](${url})`); | ||
| } | ||
| if (requested.linux) { | ||
| const name = 'Build Linux packages'; | ||
| const url = byName[name] || `${context.serverUrl}/${owner}/${repo}/actions/runs/${run_id}`; | ||
| const targetsStr = linuxTargets.length ? '`' + linuxTargets.join('`, `') + '`' : '_none_'; | ||
| rows.push(`- Linux: ${statusEmoji(results.linux)} (${results.linux}) — Targets: ${targetsStr} — [Job logs](${url})`); | ||
| } | ||
|
|
||
| const body = [ | ||
| `Build results for /build on PR #${process.env.ISSUE_NUMBER}:`, | ||
| '', | ||
| ...rows, | ||
| ].join('\n'); | ||
|
|
||
| await github.rest.issues.createComment({ | ||
| owner, | ||
| repo, | ||
| issue_number: Number(process.env.ISSUE_NUMBER), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Prompt for AI agents |
||
| body | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: Missing
always()in condition. This job depends on conditional build jobs that will be skipped when not requested. Withoutalways(), this job won't run when dependencies are skipped, so the results comment will never be posted.Prompt for AI agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cubic-dev-ai this should only run if we decide to run the builds, the condition it has should detect that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@patrick-stephens You're right that the
if: needs.pr-build-comment.outputs.should-run == 'true'condition correctly gates whether builds should trigger overall—that part works fine to detect if a valid/buildcommand was parsed.The issue I flagged is separate: even when builds are triggered (or partially skipped), the response job (which posts the results comment) depends on the build jobs via
needs. When a build job likepr-build-windowsisn't requested, it gets skipped entirely. GitHub Actions then skips the response job too, because skipped dependencies block the whole chain. That's why you needalways()added to the response job'sifcondition, like this:This ensures the response always runs to report what happened (success, skip, etc.), regardless of which builds fired. Without it, you'd never get that summary comment back on the PR.