@ -5,7 +5,7 @@ on:
jobs:
jobs:
labeler:
labeler:
name : apply labels
name : review
permissions:
permissions:
contents : read
contents : read
pull-requests : write
pull-requests : write
@ -14,17 +14,17 @@ jobs:
- uses : actions/checkout@v4
- uses : actions/checkout@v4
with:
with:
submodules : false
submodules : false
# Label PRs
- uses : actions/labeler@v5.0.0
- uses : actions/labeler@v5.0.0
with:
with:
dot : true
dot : true
configuration-path : .github/labeler.yaml
configuration-path : .github/labeler.yaml
pr_branch_check:
# Check PR target branch
name : check branch
- name : check branch
runs-on : ubuntu-latest
uses : Vankka/pr-target-branch-action@def32ec9d93514138d6ac0132ee62e120a72aed5
if : github.repository == 'commaai/openpilot'
if : github.repository == 'commaai/openpilot'
steps:
- uses : Vankka/pr-target-branch-action@def32ec9d93514138d6ac0132ee62e120a72aed5
env:
env:
GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
with:
with:
@ -34,194 +34,20 @@ jobs:
already-exists-action : close_this
already-exists-action : close_this
already-exists-comment : "Your PR should be made against the `master` branch"
already-exists-comment : "Your PR should be made against the `master` branch"
comment:
# Welcome comment
runs-on : ubuntu-latest
- name : comment
steps:
uses : thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6
- name : comment
if : github.event.pull_request.head.repo.full_name != 'commaai/openpilot'
uses : thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6
with:
if : github.event.pull_request.head.repo.full_name != 'commaai/openpilot'
message : |
with:
<!-- _(run_id **${{ github.run_id }}**)_ -->
message : |
Thanks for contributing to openpilot! In order for us to review your PR as quickly as possible, check the following:
<!-- _(run_id **${{ github.run_id }}**)_ -->
* Convert your PR to a draft unless it's ready to review
Thanks for contributing to openpilot! In order for us to review your PR as quickly as possible, check the following:
* Read the [contributing docs](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md)
* Convert your PR to a draft unless it's ready to review
* Before marking as "ready for review", ensure:
* Read the [contributing docs](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md)
* the goal is clearly stated in the description
* Before marking as "ready for review", ensure:
* all the tests are passing
* the goal is clearly stated in the description
* the change is [something we merge](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md#what-gets-merged)
* all the tests are passing
* include a route or your device' dongle ID if relevant
* the change is [something we merge](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md#what-gets-merged)
comment_tag : run_id
* include a route or your device' dongle ID if relevant
GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
comment_tag : run_id
GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
check-pr-template:
runs-on : ubuntu-latest
permissions:
contents : read
issues : write
pull-requests : write
actions : read
if : false && github.event.pull_request.head.repo.full_name != 'commaai/openpilot'
steps:
- uses : actions/github-script@v7
with:
script : |
// Comment to add to the PR if no template has been used
const NO_TEMPLATE_MESSAGE =
"It looks like you didn't use one of the Pull Request templates. Please check [the contributing docs](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md). \
Also make sure that you didn't modify any of the checkboxes or headings within the template.";
// body data for future requests
const body_data = {
issue_number : context.issue.number,
owner : context.repo.owner,
repo : context.repo.repo,
};
// Utility function to extract all headings
const extractHeadings = (markdown) => {
const headingRegex = /^(#{1,6})\s+(.+)$/gm;
const boldTextRegex = /^(?:\*\*|__)(.+?)(?:\*\*|__)\s*$/gm;
const headings = [];
let headingMatch;
while ((headingMatch = headingRegex.exec(markdown))) {
headings.push(headingMatch[2].trim());
}
let boldMatch;
while ((boldMatch = boldTextRegex.exec(markdown))) {
headings.push(boldMatch[1].trim());
}
return headings;
};
// Utility function to extract all check box descriptions
const extractCheckBoxTexts = (markdown) => {
const checkboxRegex = /^\s*-\s*\[( |x)\]\s+(.+)$/gm;
const checkboxes = [];
let match;
while ((match = checkboxRegex.exec(markdown))) {
checkboxes.push(match[2].trim());
}
return checkboxes;
};
// Utility function to check if a list is a subset of another list
isSubset = (subset, superset) => {
return subset.every((item) => superset.includes(item));
};
// Utility function to check if a list of checkboxes is a subset of another list of checkboxes
isCheckboxSubset = (templateCheckBoxTexts, prTextCheckBoxTexts) => {
// Check if each template checkbox text is a substring of at least one PR checkbox text
// (user should be allowed to add additional text)
return templateCheckBoxTexts.every((item) => prTextCheckBoxTexts.some((element) => element.includes(item)))
}
// Get filenames of all currently checked-in PR templates
const template_contents = await github.rest.repos.getContent({
owner : context.repo.owner,
repo : context.repo.repo,
path : ".github/PULL_REQUEST_TEMPLATE" ,
});
var template_filenames = [];
for (const content of template_contents.data) {
template_filenames.push(content.path);
}
console.debug("Received template filenames: " + template_filenames);
// Retrieve templates
var templates = [];
for (const template_filename of template_filenames) {
const template_response = await github.rest.repos.getContent({
owner : context.repo.owner,
repo : context.repo.repo,
path : template_filename,
});
// Convert Base64 content back
const decoded_template = atob(template_response.data.content);
const headings = extractHeadings(decoded_template);
const checkboxes = extractCheckBoxTexts(decoded_template);
if (!headings.length && !checkboxes.length) {
console.warn(
"Invalid template! Contains neither headings nor checkboxes, ignoring it: \n" +
decoded_template
);
} else {
templates.push({ headings: headings, checkboxes: checkboxes });
}
}
// Retrieve the PR Body
const pull_request = await github.rest.issues.get({
...body_data,
});
const pull_request_text = pull_request.data.body;
console.debug("Received Pull Request body: \n" + pull_request_text);
/* Check if the PR Body matches one of the templates
A template is defined by all headings and checkboxes it contains
We extract all Headings and Checkboxes from the PR text and check if any of the templates is a subset of that
*/
const pr_headings = extractHeadings(pull_request_text);
const pr_checkboxes = extractCheckBoxTexts(pull_request_text);
console.debug("Found Headings in PR body:\n" + pr_headings);
console.debug("Found Checkboxes in PR body:\n" + pr_checkboxes);
var template_found = false;
// Iterate over each template to check if it applies
for (const template of templates) {
console.log(
"Checking for headings: [" +
template.headings +
"] and checkboxes: [" +
template.checkboxes + "]"
);
if (
isCheckboxSubset(template.checkboxes, pr_checkboxes) &&
isSubset(template.headings, pr_headings)
) {
console.debug("Found matching template!");
template_found = true;
}
}
// List comments from previous runs
var existing_comments = [];
const comments = await github.rest.issues.listComments({
...body_data,
});
for (const comment of comments.data) {
if (comment.body === NO_TEMPLATE_MESSAGE) {
existing_comments.push(comment);
}
}
// Add a comment to the PR that it is not using a the template (but only if this comment does not exist already)
if (!template_found) {
var comment_already_sent = false;
// Add an 'in-bot-review' label since this PR doesn't have the template
github.rest.issues.addLabels({
...body_data,
labels : [ "in-bot-review" ] ,
});
if (existing_comments.length < 1) {
github.rest.issues.createComment({
...body_data,
body : NO_TEMPLATE_MESSAGE,
});
}
} else {
// If template has been found, delete any old comment about missing template
for (const existing_comment of existing_comments) {
github.rest.issues.deleteComment({
...body_data,
comment_id : existing_comment.id,
});
}
// Remove the 'in-bot-review' label after the review is done and the PR has passed
github.rest.issues.removeLabel({
...body_data,
name : "in-bot-review" ,
}).catch((error) => {
console.log("Label 'in-bot-review' not found, ignoring");
});
}