Consolidate PR validation comments workflow

This change replaces the old PR reporter workflow with a new pr-validation-comments workflow and updates the validation jobs to reference it. The new workflow centralizes PR comment handling for build and license checks using workflow_run, so forked PRs can still receive validation feedback without granting write access to code-executing jobs.
This commit is contained in:
Ludy87
2026-08-21 10:49:45 +02:00
parent 13958d3b30
commit f8bd4cd0b0
8 changed files with 437 additions and 222 deletions
+2 -2
View File
@@ -49,7 +49,7 @@ jobs:
if: steps.engine-check.outcome == 'failure' && github.event_name == 'pull_request'
continue-on-error: true
run: |
echo "Comments are handled by report-pr-check.yml"
echo "Comments are handled by pr-validation-comments.yml"
- name: Fail if engine check failed
if: steps.engine-check.outcome == 'failure'
@@ -79,4 +79,4 @@ jobs:
if: steps.engine-check.outcome == 'success' && github.event_name == 'pull_request'
continue-on-error: true
run: |
echo "Comments are handled by report-pr-check.yml"
echo "Comments are handled by pr-validation-comments.yml"
+2 -2
View File
@@ -69,7 +69,7 @@ jobs:
if: steps.spotless-check.outcome == 'failure' && github.event_name == 'pull_request'
continue-on-error: true
run: |
echo "Comments are handled by report-pr-check.yml"
echo "Comments are handled by pr-validation-comments.yml"
- name: Fail if backend format check failed
if: steps.spotless-check.outcome == 'failure'
@@ -91,7 +91,7 @@ jobs:
if: steps.spotless-check.outcome == 'success' && github.event_name == 'pull_request'
continue-on-error: true
run: |
echo "Comments are handled by report-pr-check.yml"
echo "Comments are handled by pr-validation-comments.yml"
- name: Build with Gradle (flavor=${{ matrix.flavor }})
# STIRLING_FLAVOR is read by settings.gradle and expands into the
+2 -2
View File
@@ -88,7 +88,7 @@ jobs:
if: steps.models-check.outcome == 'failure' && github.event_name == 'pull_request'
continue-on-error: true
run: |
echo "Comments are handled by report-pr-check.yml"
echo "Comments are handled by pr-validation-comments.yml"
- name: Fail if generated models check failed
if: steps.models-check.outcome == 'failure'
@@ -109,4 +109,4 @@ jobs:
if: steps.models-check.outcome == 'success' && github.event_name == 'pull_request'
continue-on-error: true
run: |
echo "Comments are handled by report-pr-check.yml"
echo "Comments are handled by pr-validation-comments.yml"
+1 -1
View File
@@ -2,7 +2,7 @@ name: Check TOML Translation Files on PR
# This reusable workflow validates TOML translation files.
# It is called by the pull_request workflow and deliberately has no write
# permissions. PR comments are handled by report-pr-check.yml via workflow_run.
# permissions. PR comments are handled by pr-validation-comments.yml via workflow_run.
on:
workflow_call:
@@ -157,7 +157,7 @@ jobs:
if: github.event_name == 'pull_request'
continue-on-error: true
run: |
echo "Comments are handled by report-pr-check.yml"
echo "Comments are handled by pr-validation-comments.yml"
- name: Summarize results (fork PRs)
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true) || github.actor == 'dependabot[bot]'
@@ -183,7 +183,7 @@ jobs:
if: github.event_name == 'pull_request'
continue-on-error: true
run: |
echo "Comments are handled by report-pr-check.yml"
echo "Comments are handled by pr-validation-comments.yml"
- name: Fail workflow if license warnings exist (PR only)
if: github.event_name == 'pull_request' && env.LICENSE_WARNINGS_EXIST == 'true'
@@ -341,13 +341,13 @@ jobs:
if: github.event_name == 'pull_request'
continue-on-error: true
run: |
echo "Comments are handled by report-pr-check.yml"
echo "Comments are handled by pr-validation-comments.yml"
- name: Comment on PR - Backend License Check Results
if: github.event_name == 'pull_request'
continue-on-error: true
run: |
echo "Comments are handled by report-pr-check.yml"
echo "Comments are handled by pr-validation-comments.yml"
- name: Fail workflow if license warnings exist (PR only)
if: github.event_name == 'pull_request' && env.LICENSE_WARNINGS_EXIST == 'true'
+2 -2
View File
@@ -37,7 +37,7 @@ jobs:
if: steps.frontend-check.outcome == 'failure' && github.event_name == 'pull_request'
continue-on-error: true
run: |
echo "Comments are handled by report-pr-check.yml"
echo "Comments are handled by pr-validation-comments.yml"
- name: Fail if frontend check failed
if: steps.frontend-check.outcome == 'failure'
run: |
@@ -57,7 +57,7 @@ jobs:
if: steps.frontend-check.outcome == 'success' && github.event_name == 'pull_request'
continue-on-error: true
run: |
echo "Comments are handled by report-pr-check.yml"
echo "Comments are handled by pr-validation-comments.yml"
- name: Install uv
if: always()
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
@@ -0,0 +1,424 @@
name: PR validation comments
# Reports validation results from the default branch. It never checks out or
# executes code from the pull request.
on:
workflow_run:
workflows:
- "Build and Test Workflow"
- "License Report Workflow"
types: [completed]
permissions:
contents: read
actions: read
jobs:
build-validation-comments:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.name == matrix.workflow
name: Build validation comment (${{ matrix.label }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- label: frontend
workflow: Build and Test Workflow
failed_step: Fail if frontend check failed
marker: <!-- frontend-check -->
title: Frontend Check Failed
type: standard
- label: engine
workflow: Build and Test Workflow
failed_step: Fail if engine check failed
marker: <!-- engine-check -->
title: Engine Check Failed
type: standard
- label: backend-format
workflow: Build and Test Workflow
failed_step: Fail if backend format check failed
marker: <!-- java-formatting-check -->
title: Backend Format Check Failed
type: standard
- label: generated-models
workflow: Build and Test Workflow
failed_step: Fail if generated models check failed
marker: <!-- generated-models-check -->
title: Generated Models Check Failed
type: standard
- label: toml
workflow: Build and Test Workflow
failed_step: Fail job if errors found
marker: <!-- toml-translation-check -->
title: TOML Translation Check Failed
type: toml
permissions:
actions: read
contents: read
issues: write
pull-requests: write
steps:
- name: Download frontend license warnings
if: matrix.type == 'frontend-license'
continue-on-error: true
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: frontend-license-warnings
path: license-artifacts/frontend
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Download backend license warnings
if: matrix.type == 'backend-license'
continue-on-error: true
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: backend-dependencies-without-allowed-license.json
path: license-artifacts/backend
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Download TOML translation result
if: matrix.type == 'toml'
continue-on-error: true
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: toml-translation-check-result
path: toml-artifacts
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Update ${{ matrix.label }} comment
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108d7 # v9.0.0
env:
CHECK_MARKER: ${{ matrix.marker }}
FAILED_STEP: ${{ matrix.failed_step }}
CHECK_TITLE: ${{ matrix.title }}
CHECK_TYPE: ${{ matrix.type }}
CHECK_JOB_NAME: ${{ matrix.job_name }}
with:
script: |
const run = context.payload.workflow_run;
const { owner, repo } = context.repo;
let pullRequest = run.pull_requests?.[0];
if (!pullRequest) {
const { data: associatedPullRequests } =
await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner,
repo,
commit_sha: run.head_sha,
});
pullRequest = associatedPullRequests[0];
}
if (!pullRequest) {
core.warning('No pull request found for this workflow run.');
return;
}
const jobs = await github.paginate(
github.rest.actions.listJobsForWorkflowRun,
{ owner, repo, run_id: run.id, per_page: 100 },
);
const failed = jobs.some(job =>
(!process.env.CHECK_JOB_NAME || job.name === process.env.CHECK_JOB_NAME) &&
job.steps?.some(step =>
step.name === process.env.FAILED_STEP &&
step.conclusion === 'failure'));
const comments = await github.paginate(
github.rest.issues.listComments,
{ owner, repo, issue_number: pullRequest.number, per_page: 100 },
);
const existing = comments.find(comment =>
comment.body?.includes(process.env.CHECK_MARKER));
const standardInstructions = {
'<!-- frontend-check -->':
"There are issues with your frontend code that will need to be fixed before they can be merged in.\n\nRun 'task frontend:fix' to auto-fix what can be fixed automatically, then run 'task frontend:check:all' to see what still needs fixing manually.",
'<!-- engine-check -->':
"There are issues with your Python code that will need to be fixed before they can be merged in.\n\nRun 'task engine:fix' to auto-fix what can be fixed automatically, then run 'task engine:check' to see what still needs fixing manually.",
'<!-- java-formatting-check -->':
"There are formatting issues in your Java code that will need to be fixed before they can be merged in.\n\nRun 'task backend:format' to auto-fix, then commit and push the changes.",
'<!-- generated-models-check -->':
"One or more generated files are out of date with the Java OpenAPI spec and will need to be regenerated before merging.\n\nRun 'task tool-models' to regenerate them, then commit the updated files.",
};
let body;
if (process.env.CHECK_TYPE === 'standard') {
body = [
process.env.CHECK_MARKER,
'## ' + (failed ? '❌ ' : '✅ ') + process.env.CHECK_TITLE,
'',
standardInstructions[process.env.CHECK_MARKER],
].join('\n');
} else if (process.env.CHECK_TYPE === 'toml') {
let details = 'See the TOML translation check logs for details.';
try {
details = require('fs').readFileSync('toml-artifacts/result.txt', 'utf8');
} catch (_) {}
body = failed
? process.env.CHECK_MARKER + '\n\n## ❌ TOML Translation Check Failed\n\n' +
details + '\n\nPlease correct the translation files and push the changes.'
: process.env.CHECK_MARKER +
'\n\n## ✅ TOML Translation Check Passed\n\nAll changed TOML translation files are consistent with the reference file.';
} else if (process.env.CHECK_TYPE === 'frontend-license') {
let warningDetails = 'Unable to read warning details.';
try {
const data = JSON.parse(require('fs').readFileSync(
'license-artifacts/frontend/license-warnings.json',
'utf8',
));
warningDetails = data.warnings.map(w => '- ' + w.message).join('\n');
} catch (_) {}
body = failed
? process.env.CHECK_MARKER + '\n\n## ❌ Frontend License Check Failed\n\nThe frontend license check detected compatibility warnings that require review:\n\n' +
warningDetails + '\n\n**Action Required:** Please review these licenses before merging.'
: process.env.CHECK_MARKER +
'\n\n## ✅ Frontend License Check Passed\n\nAll frontend licenses have been validated and no compatibility warnings were detected.';
} else {
let warningDetails = '';
try {
const data = JSON.parse(require('fs').readFileSync(
'license-artifacts/backend/dependencies-without-allowed-license.json',
'utf8',
));
warningDetails = data.map(dep =>
'- **' + dep.moduleName + '@' + dep.moduleVersion +
'** ' + dep.moduleLicenses.map(l => l.licenseName).join(', '),
).join('\n');
} catch (_) {}
body = failed
? process.env.CHECK_MARKER + '\n\n## ❌ Backend License Check Failed\n\nThe backend license check detected dependencies with incompatible or unallowed licenses:\n\n' +
(warningDetails || 'See uploaded artifact for details.') +
'\n\n**Action Required:** Please review these licenses before merging.'
: process.env.CHECK_MARKER +
'\n\n## ✅ Backend License Check Passed\n\nAll backend dependencies have valid and allowed licenses.';
}
if (failed && existing) {
await github.rest.issues.updateComment({
owner,
repo,
comment_id: existing.id,
body,
});
} else if (failed) {
await github.rest.issues.createComment({
owner,
repo,
issue_number: pullRequest.number,
body,
});
} else if (existing && process.env.CHECK_TYPE !== 'standard') {
await github.rest.issues.updateComment({
owner,
repo,
comment_id: existing.id,
body,
});
} else if (existing) {
await github.rest.issues.deleteComment({
owner,
repo,
comment_id: existing.id,
});
}
license-validation-comments:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.name == matrix.workflow
name: License validation comment (${{ matrix.label }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- label: frontend-license
workflow: License Report Workflow
job_name: Generate Frontend License Report
failed_step: Fail workflow if license warnings exist (PR only)
marker: <!-- frontend-license-check -->
title: Frontend License Check
type: frontend-license
- label: backend-license
workflow: License Report Workflow
job_name: Generate Backend License Report
failed_step: Fail workflow if license warnings exist (PR only)
marker: <!-- backend-license-check -->
title: Backend License Check
type: backend-license
permissions:
actions: read
contents: read
issues: write
pull-requests: write
steps:
- name: Download frontend license warnings
if: matrix.type == 'frontend-license'
continue-on-error: true
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: frontend-license-warnings
path: license-artifacts/frontend
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Download backend license warnings
if: matrix.type == 'backend-license'
continue-on-error: true
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: backend-dependencies-without-allowed-license.json
path: license-artifacts/backend
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Download TOML translation result
if: matrix.type == 'toml'
continue-on-error: true
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: toml-translation-check-result
path: toml-artifacts
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Update ${{ matrix.label }} comment
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108d7 # v9.0.0
env:
CHECK_MARKER: ${{ matrix.marker }}
FAILED_STEP: ${{ matrix.failed_step }}
CHECK_TITLE: ${{ matrix.title }}
CHECK_TYPE: ${{ matrix.type }}
CHECK_JOB_NAME: ${{ matrix.job_name }}
with:
script: |
const run = context.payload.workflow_run;
const { owner, repo } = context.repo;
let pullRequest = run.pull_requests?.[0];
if (!pullRequest) {
const { data: associatedPullRequests } =
await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner,
repo,
commit_sha: run.head_sha,
});
pullRequest = associatedPullRequests[0];
}
if (!pullRequest) {
core.warning('No pull request found for this workflow run.');
return;
}
const jobs = await github.paginate(
github.rest.actions.listJobsForWorkflowRun,
{ owner, repo, run_id: run.id, per_page: 100 },
);
const failed = jobs.some(job =>
(!process.env.CHECK_JOB_NAME || job.name === process.env.CHECK_JOB_NAME) &&
job.steps?.some(step =>
step.name === process.env.FAILED_STEP &&
step.conclusion === 'failure'));
const comments = await github.paginate(
github.rest.issues.listComments,
{ owner, repo, issue_number: pullRequest.number, per_page: 100 },
);
const existing = comments.find(comment =>
comment.body?.includes(process.env.CHECK_MARKER));
const standardInstructions = {
'<!-- frontend-check -->':
"There are issues with your frontend code that will need to be fixed before they can be merged in.\n\nRun 'task frontend:fix' to auto-fix what can be fixed automatically, then run 'task frontend:check:all' to see what still needs fixing manually.",
'<!-- engine-check -->':
"There are issues with your Python code that will need to be fixed before they can be merged in.\n\nRun 'task engine:fix' to auto-fix what can be fixed automatically, then run 'task engine:check' to see what still needs fixing manually.",
'<!-- java-formatting-check -->':
"There are formatting issues in your Java code that will need to be fixed before they can be merged in.\n\nRun 'task backend:format' to auto-fix, then commit and push the changes.",
'<!-- generated-models-check -->':
"One or more generated files are out of date with the Java OpenAPI spec and will need to be regenerated before merging.\n\nRun 'task tool-models' to regenerate them, then commit the updated files.",
};
let body;
if (process.env.CHECK_TYPE === 'standard') {
body = [
process.env.CHECK_MARKER,
'## ' + (failed ? '❌ ' : '✅ ') + process.env.CHECK_TITLE,
'',
standardInstructions[process.env.CHECK_MARKER],
].join('\n');
} else if (process.env.CHECK_TYPE === 'toml') {
let details = 'See the TOML translation check logs for details.';
try {
details = require('fs').readFileSync('toml-artifacts/result.txt', 'utf8');
} catch (_) {}
body = failed
? process.env.CHECK_MARKER + '\n\n## ❌ TOML Translation Check Failed\n\n' +
details + '\n\nPlease correct the translation files and push the changes.'
: process.env.CHECK_MARKER +
'\n\n## ✅ TOML Translation Check Passed\n\nAll changed TOML translation files are consistent with the reference file.';
} else if (process.env.CHECK_TYPE === 'frontend-license') {
let warningDetails = 'Unable to read warning details.';
try {
const data = JSON.parse(require('fs').readFileSync(
'license-artifacts/frontend/license-warnings.json',
'utf8',
));
warningDetails = data.warnings.map(w => '- ' + w.message).join('\n');
} catch (_) {}
body = failed
? process.env.CHECK_MARKER + '\n\n## ❌ Frontend License Check Failed\n\nThe frontend license check detected compatibility warnings that require review:\n\n' +
warningDetails + '\n\n**Action Required:** Please review these licenses before merging.'
: process.env.CHECK_MARKER +
'\n\n## ✅ Frontend License Check Passed\n\nAll frontend licenses have been validated and no compatibility warnings were detected.';
} else {
let warningDetails = '';
try {
const data = JSON.parse(require('fs').readFileSync(
'license-artifacts/backend/dependencies-without-allowed-license.json',
'utf8',
));
warningDetails = data.map(dep =>
'- **' + dep.moduleName + '@' + dep.moduleVersion +
'** ' + dep.moduleLicenses.map(l => l.licenseName).join(', '),
).join('\n');
} catch (_) {}
body = failed
? process.env.CHECK_MARKER + '\n\n## ❌ Backend License Check Failed\n\nThe backend license check detected dependencies with incompatible or unallowed licenses:\n\n' +
(warningDetails || 'See uploaded artifact for details.') +
'\n\n**Action Required:** Please review these licenses before merging.'
: process.env.CHECK_MARKER +
'\n\n## ✅ Backend License Check Passed\n\nAll backend dependencies have valid and allowed licenses.';
}
if (failed && existing) {
await github.rest.issues.updateComment({
owner,
repo,
comment_id: existing.id,
body,
});
} else if (failed) {
await github.rest.issues.createComment({
owner,
repo,
issue_number: pullRequest.number,
body,
});
} else if (existing && process.env.CHECK_TYPE !== 'standard') {
await github.rest.issues.updateComment({
owner,
repo,
comment_id: existing.id,
body,
});
} else if (existing) {
await github.rest.issues.deleteComment({
owner,
repo,
comment_id: existing.id,
});
}
-209
View File
@@ -1,209 +0,0 @@
name: PR Check Reporter
# Reports failed PR checks back to the pull request with actionable comments.
# Runs separately so fork PRs can receive comments without granting write
# access to workflows that execute pull request code.
on:
workflow_run:
workflows:
- "Build and Test Workflow"
- "License Report Workflow"
types: [completed]
permissions:
contents: read
actions: read
jobs:
report:
if: github.event.workflow_run.event == 'pull_request'
permissions:
contents: read
actions: read
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Download frontend license warnings
if: github.event.workflow_run.name == 'License Report Workflow'
continue-on-error: true
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: frontend-license-warnings
path: license-artifacts/frontend
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Download backend license warnings
if: github.event.workflow_run.name == 'License Report Workflow'
continue-on-error: true
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: backend-dependencies-without-allowed-license.json
path: license-artifacts/backend
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Download TOML translation check result
if: github.event.workflow_run.name == 'Build and Test Workflow'
continue-on-error: true
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: toml-translation-check-result
path: toml-artifacts
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Update PR check comments
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108d7 # v9.0.0
with:
script: |
const run = context.payload.workflow_run;
const { owner, repo } = context.repo;
let pullRequest = run.pull_requests?.[0];
if (!pullRequest) {
const { data: associatedPullRequests } =
await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner, repo, commit_sha: run.head_sha,
});
pullRequest = associatedPullRequests[0];
}
if (!pullRequest) {
core.warning(`No pull request found for workflow run ${run.id}.`);
return;
}
const jobs = await github.paginate(
github.rest.actions.listJobsForWorkflowRun,
{ owner, repo, run_id: run.id, per_page: 100 },
);
const checks = [
{
workflow: 'Build and Test Workflow',
marker: '<!-- engine-check -->',
failedStep: 'Fail if engine check failed',
title: 'Engine Check Failed',
instructions: "There are issues with your Python code that will need to be fixed before they can be merged in.\n\nRun 'task engine:fix' to auto-fix what can be fixed automatically, then run 'task engine:check' to see what still needs fixing manually.",
removeWhenFixed: true,
},
{
workflow: 'Build and Test Workflow',
marker: '<!-- java-formatting-check -->',
failedStep: 'Fail if backend format check failed',
title: 'Backend Format Check Failed',
instructions: "There are formatting issues in your Java code that will need to be fixed before they can be merged in.\n\nRun 'task backend:format' to auto-fix, then commit and push the changes.",
removeWhenFixed: true,
},
{
workflow: 'Build and Test Workflow',
marker: '<!-- frontend-check -->',
failedStep: 'Fail if frontend check failed',
title: 'Frontend Check Failed',
instructions: "There are issues with your frontend code that will need to be fixed before they can be merged in.\n\nRun 'task frontend:fix' to auto-fix what can be fixed automatically, then run 'task frontend:check:all' to see what still needs fixing manually.",
removeWhenFixed: true,
},
{
workflow: 'Build and Test Workflow',
marker: '<!-- toml-translation-check -->',
failedStep: 'Fail job if errors found',
title: 'TOML Translation Check Failed',
instructions: "The TOML translation check found errors. Review the TOML translation files and make them consistent with the en-US reference file.",
type: 'toml-translation',
removeWhenFixed: true,
},
{
workflow: 'Build and Test Workflow',
marker: '<!-- generated-models-check -->',
failedStep: 'Fail if generated models check failed',
title: 'Generated Models Check Failed',
instructions: "One or more generated files are out of date with the Java OpenAPI spec and will need to be regenerated before merging.\n\nRun 'task tool-models' to regenerate them, then commit the updated files.",
removeWhenFixed: true,
},
{
workflow: 'License Report Workflow',
marker: '<!-- frontend-license-check -->',
failedStep: 'Fail workflow if license warnings exist (PR only)',
jobName: 'Generate Frontend License Report',
title: 'Frontend License Check Failed',
instructions: 'Review the frontend license warnings reported on the pull request and resolve them before merging.',
type: 'frontend-license',
removeWhenFixed: false,
},
{
workflow: 'License Report Workflow',
marker: '<!-- backend-license-check -->',
failedStep: 'Fail workflow if license warnings exist (PR only)',
jobName: 'Generate Backend License Report',
title: 'Backend License Check Failed',
instructions: 'Review the backend license warnings reported on the pull request and resolve them before merging.',
type: 'backend-license',
removeWhenFixed: false,
},
];
const comments = await github.paginate(
github.rest.issues.listComments,
{ owner, repo, issue_number: pullRequest.number, per_page: 100 },
);
const licenseCommentBody = (type, failed, marker) => {
if (type === 'frontend-license') {
let warningDetails = 'Unable to read warning details';
try {
const data = JSON.parse(require('fs').readFileSync(
'license-artifacts/frontend/license-warnings.json', 'utf8'));
warningDetails = data.warnings.map(w => `- ${w.message}`).join('\n');
} catch (_) {}
return failed
? `${marker}\n\n## ❌ Frontend License Check Failed\n\nThe frontend license check has detected compatibility warnings that require review:\n\n${warningDetails}\n\n**Action Required:** Please review these licenses to ensure they are acceptable for your use case before merging.\n\n_This check will fail the PR until license issues are resolved._`
: `${marker}\n\n## ✅ Frontend License Check Passed\n\nAll frontend licenses have been validated and no compatibility warnings were detected.\n\nThe frontend license report has been updated successfully.`;
}
if (type === 'backend-license') {
let warningDetails = '';
try {
const data = JSON.parse(require('fs').readFileSync(
'license-artifacts/backend/dependencies-without-allowed-license.json', 'utf8'));
warningDetails = data.map(dep => `- **${dep.moduleName}@${dep.moduleVersion}** ${dep.moduleLicenses.map(l => l.licenseName).join(', ')}`).join('\n');
} catch (_) {}
return failed
? `${marker}\n\n## ❌ Backend License Check Failed\n\nThe backend license check has detected dependencies with incompatible or unallowed licenses:\n\n${warningDetails || 'See uploaded artifact for details.'}\n\n**Action Required:** Please review these licenses and resolve before merging.\n\n_This check will fail the PR until license issues are resolved._`
: `${marker}\n\n## ✅ Backend License Check Passed\n\nAll backend dependencies have valid and allowed licenses.\n\nThe backend license report has been updated successfully.`;
}
if (type === 'toml-translation') {
let details = 'See the TOML translation check logs for details.';
try {
details = require('fs').readFileSync('toml-artifacts/result.txt', 'utf8');
} catch (_) {}
return failed
? `${marker}\n\n## ❌ TOML Translation Check Failed\n\n${details}\n\nPlease correct the translation files and push the changes.`
: `${marker}\n\n## ✅ TOML Translation Check Passed\n\nAll changed TOML translation files are consistent with the reference file.`;
}
throw new Error(`Unsupported license comment type: ${type}`);
};
for (const check of checks) {
if (run.name !== check.workflow) continue;
const failed = jobs.some(job =>
(!check.jobName || job.name === check.jobName) &&
job.steps?.some(step => step.name === check.failedStep && step.conclusion === 'failure'));
const existing = comments.find(comment => comment.body?.includes(check.marker));
const body = check.type
? licenseCommentBody(check.type, failed, check.marker)
: [check.marker, `## ❌ ${check.title}`, '', check.instructions].join('\n');
if (failed && existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else if (failed) {
await github.rest.issues.createComment({ owner, repo, issue_number: pullRequest.number, body });
} else if (check.type && existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else if (check.removeWhenFixed && existing) {
await github.rest.issues.deleteComment({ owner, repo, comment_id: existing.id });
}
}