@ -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,9 +34,7 @@ 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  
			
		
	
		
		
			
				
					
					     steps:  
			
		
	
		
		
	
		
		
			
				
					
					     - name :   comment      - name :   comment  
			
		
	
		
		
			
				
					
					       uses :   thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6        uses :   thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6  
			
		
	
		
		
			
				
					
					       if :   github.event.pull_request.head.repo.full_name != 'commaai/openpilot'        if :   github.event.pull_request.head.repo.full_name != 'commaai/openpilot'  
			
		
	
	
		
		
			
				
					
						
						
						
							
								 
						
					 
					@ -53,175 +51,3 @@ jobs: 
			
		
	
		
		
			
				
					
					               *  include a route or your device' dongle ID if relevant                *  include a route or your device' dongle ID if relevant  
			
		
	
		
		
			
				
					
					         comment_tag :   run_id          comment_tag :   run_id  
			
		
	
		
		
			
				
					
					         GITHUB_TOKEN :   ${{ secrets.GITHUB_TOKEN }}          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");  
			
		
	
		
		
			
				
					
					               });  
			
		
	
		
		
			
				
					
					             }