mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-09-12 21:37:53 -04:00
299 lines
10 KiB
YAML
299 lines
10 KiB
YAML
name: PR Build (Comment Triggered)
|
|
|
|
on:
|
|
issue_comment:
|
|
types: [created]
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
validate:
|
|
runs-on: ubuntu-latest
|
|
if: github.event.issue.pull_request != null
|
|
permissions:
|
|
contents: read
|
|
issues: write
|
|
pull-requests: write
|
|
statuses: write
|
|
outputs:
|
|
build: ${{ steps.parse.outputs.build }}
|
|
meta: ${{ steps.parse.outputs.meta }}
|
|
pr_ref: ${{ steps.parse.outputs.pr_ref }}
|
|
pr_sha: ${{ steps.parse.outputs.pr_sha }}
|
|
pr_number: ${{ steps.parse.outputs.pr_number }}
|
|
|
|
steps:
|
|
- name: Parse command and check permissions
|
|
id: parse
|
|
uses: actions/github-script@v9
|
|
with:
|
|
script: |
|
|
const comment = context.payload.comment.body.trim();
|
|
|
|
const ALLOWED_USERS = ['flaminel'];
|
|
|
|
// Parse supported commands
|
|
const commands = {
|
|
'/build-windows': {
|
|
target: 'windows',
|
|
label: 'Windows installer',
|
|
artifacts: '`Cleanuparr-windows-installer`'
|
|
},
|
|
'/build-linux': {
|
|
target: 'linux',
|
|
label: 'Linux executables',
|
|
artifacts: '`executable-linux-amd64` and `executable-linux-arm64`'
|
|
},
|
|
'/test': {
|
|
target: 'test',
|
|
label: 'Unit tests',
|
|
artifacts: '`test-results`, `coverage-report` and `frontend-coverage-report`'
|
|
},
|
|
'/e2e': {
|
|
target: 'e2e',
|
|
label: 'E2E tests',
|
|
artifacts: '`e2e-test-results-core` and `e2e-test-results-clients`'
|
|
},
|
|
'/build-docker': {
|
|
target: 'docker',
|
|
label: 'Docker image',
|
|
artifacts: ''
|
|
}
|
|
};
|
|
|
|
const command = commands[comment];
|
|
if (!command) {
|
|
console.log(`Comment "${comment}" is not a recognized build command, skipping.`);
|
|
return;
|
|
}
|
|
|
|
const login = context.payload.comment.user.login;
|
|
if (!ALLOWED_USERS.includes(login.toLowerCase())) {
|
|
console.log(`User ${login} is not allowed to trigger builds, skipping.`);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await github.rest.reactions.createForIssueComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
comment_id: context.payload.comment.id,
|
|
content: 'eyes'
|
|
});
|
|
} catch (e) {
|
|
console.log(`Could not add reaction: ${e}`);
|
|
}
|
|
|
|
// Fetch PR details
|
|
const pr = await github.rest.pulls.get({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number: context.issue.number
|
|
});
|
|
|
|
// Verify PR is open
|
|
if (pr.data.state !== 'open') {
|
|
console.log('PR is not open, skipping.');
|
|
return;
|
|
}
|
|
|
|
console.log(`${login} triggered ${comment} on PR #${pr.data.number} @ ${pr.data.head.sha}.`);
|
|
|
|
try {
|
|
await github.rest.repos.createCommitStatus({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
sha: pr.data.head.sha,
|
|
state: 'pending',
|
|
context: `pr-build / ${command.target}`,
|
|
description: `${command.label} running`,
|
|
target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`
|
|
});
|
|
} catch (e) {
|
|
console.log(`Could not create commit status: ${e}`);
|
|
}
|
|
|
|
core.setOutput('build', command.target);
|
|
core.setOutput('meta', JSON.stringify({ label: command.label, artifacts: command.artifacts }));
|
|
|
|
// Export PR details for downstream jobs
|
|
core.setOutput('pr_ref', pr.data.head.ref);
|
|
core.setOutput('pr_sha', pr.data.head.sha);
|
|
core.setOutput('pr_number', String(pr.data.number));
|
|
|
|
build-frontend:
|
|
needs: validate
|
|
if: needs.validate.outputs.build == 'windows' || needs.validate.outputs.build == 'linux'
|
|
uses: ./.github/workflows/build-frontend.yml
|
|
with:
|
|
ref: ${{ needs.validate.outputs.pr_sha }}
|
|
secrets: inherit
|
|
|
|
build-windows:
|
|
needs: [validate, build-frontend]
|
|
if: needs.validate.outputs.build == 'windows'
|
|
concurrency:
|
|
group: pr-build-windows-${{ needs.validate.outputs.pr_number }}
|
|
cancel-in-progress: true
|
|
uses: ./.github/workflows/build-windows-installer.yml
|
|
with:
|
|
ref: ${{ needs.validate.outputs.pr_sha }}
|
|
secrets: inherit
|
|
|
|
build-linux:
|
|
needs: [validate, build-frontend]
|
|
if: needs.validate.outputs.build == 'linux'
|
|
concurrency:
|
|
group: pr-build-linux-${{ needs.validate.outputs.pr_number }}
|
|
cancel-in-progress: true
|
|
uses: ./.github/workflows/build-executable.yml
|
|
with:
|
|
ref: ${{ needs.validate.outputs.pr_sha }}
|
|
platforms: '[{"runtime":"linux-x64","platform":"linux-amd64"},{"runtime":"linux-arm64","platform":"linux-arm64"}]'
|
|
secrets: inherit
|
|
|
|
test:
|
|
needs: validate
|
|
if: needs.validate.outputs.build == 'test'
|
|
concurrency:
|
|
group: pr-test-${{ needs.validate.outputs.pr_number }}
|
|
cancel-in-progress: true
|
|
uses: ./.github/workflows/test.yml
|
|
with:
|
|
ref: ${{ needs.validate.outputs.pr_sha }}
|
|
secrets: inherit
|
|
|
|
e2e:
|
|
needs: validate
|
|
if: needs.validate.outputs.build == 'e2e'
|
|
concurrency:
|
|
group: pr-e2e-${{ needs.validate.outputs.pr_number }}
|
|
cancel-in-progress: true
|
|
uses: ./.github/workflows/e2e.yml
|
|
with:
|
|
ref: ${{ needs.validate.outputs.pr_sha }}
|
|
secrets: inherit
|
|
|
|
build-docker:
|
|
needs: validate
|
|
if: needs.validate.outputs.build == 'docker'
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
uses: ./.github/workflows/build-docker.yml
|
|
with:
|
|
ref: ${{ needs.validate.outputs.pr_sha }}
|
|
pr_number: ${{ needs.validate.outputs.pr_number }}
|
|
push_docker: true
|
|
secrets: inherit
|
|
|
|
post-result:
|
|
needs: [validate, build-frontend, build-windows, build-linux, test, e2e, build-docker]
|
|
if: always() && needs.validate.outputs.build != ''
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
issues: write
|
|
pull-requests: write
|
|
statuses: write
|
|
|
|
steps:
|
|
- name: Post result comment
|
|
uses: actions/github-script@v9
|
|
env:
|
|
PR_REF: ${{ needs.validate.outputs.pr_ref }}
|
|
META: ${{ needs.validate.outputs.meta }}
|
|
with:
|
|
script: |
|
|
const target = '${{ needs.validate.outputs.build }}';
|
|
|
|
// needs.* is only readable inside this job, so the result lookup has to live here.
|
|
const results = {
|
|
windows: '${{ needs.build-windows.result }}',
|
|
linux: '${{ needs.build-linux.result }}',
|
|
test: '${{ needs.test.result }}',
|
|
e2e: '${{ needs.e2e.result }}',
|
|
docker: '${{ needs.build-docker.result }}'
|
|
};
|
|
|
|
const frontend = '${{ needs.build-frontend.result }}';
|
|
|
|
let result = results[target];
|
|
if (result === 'skipped' && (frontend === 'failure' || frontend === 'cancelled')) {
|
|
result = frontend;
|
|
}
|
|
|
|
if (!result || result === 'skipped') {
|
|
console.log(`Nothing to report for target "${target}" (${result}).`);
|
|
return;
|
|
}
|
|
|
|
const { label, artifacts } = JSON.parse(process.env.META);
|
|
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
|
|
|
|
const prSha = '${{ needs.validate.outputs.pr_sha }}';
|
|
const statusContext = `pr-build / ${target}`;
|
|
let publishStatus = result !== 'cancelled';
|
|
|
|
if (publishStatus) {
|
|
try {
|
|
const combined = await github.rest.repos.getCombinedStatusForRef({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
ref: prSha
|
|
});
|
|
const current = combined.data.statuses.find(s => s.context === statusContext);
|
|
publishStatus = !current || current.state !== 'pending' || current.target_url === runUrl;
|
|
} catch (e) {
|
|
console.log(`Could not read the current commit status: ${e}`);
|
|
}
|
|
}
|
|
|
|
if (publishStatus) {
|
|
try {
|
|
await github.rest.repos.createCommitStatus({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
sha: prSha,
|
|
state: result === 'success' ? 'success' : 'failure',
|
|
context: statusContext,
|
|
description: `${label} ${result}`,
|
|
target_url: runUrl
|
|
});
|
|
} catch (e) {
|
|
console.log(`Could not update commit status: ${e}`);
|
|
}
|
|
} else {
|
|
console.log(`A newer run owns "${statusContext}", skipping the status update.`);
|
|
}
|
|
|
|
const prRef = process.env.PR_REF;
|
|
const prNumber = parseInt('${{ needs.validate.outputs.pr_number }}');
|
|
const shortSha = prSha.substring(0, 7);
|
|
|
|
let body;
|
|
if (result === 'success') {
|
|
const how = target === 'docker'
|
|
? [`**Pull:** \`docker pull ghcr.io/cleanuparr/cleanuparr:pr-${prNumber}\``]
|
|
: [
|
|
`**Download:** open the [workflow run](${runUrl}), scroll to the **Artifacts** section at the bottom.`,
|
|
`The ${artifacts} artifact(s) are attached to that run.`
|
|
];
|
|
body = [`${label} **succeeded** for \`${prRef}\` (\`${shortSha}\`).`, ``, ...how].join('\n');
|
|
} else {
|
|
const verb = result === 'cancelled' ? 'was **cancelled**' : '**failed**';
|
|
body = [
|
|
`${label} ${verb} for \`${prRef}\` (\`${shortSha}\`).`,
|
|
``,
|
|
`See the [workflow run](${runUrl}) for details.`
|
|
].join('\n');
|
|
}
|
|
|
|
await github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: prNumber,
|
|
body
|
|
});
|