nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at release-25.11 155 lines 4.8 kB view raw
1// @ts-check 2const { classify } = require('../supportedBranches.js') 3const { getCommitDetailsForPR } = require('./get-pr-commit-details.js') 4 5/** 6 * @param {{ 7 * github: InstanceType<import('@actions/github/lib/utils').GitHub>, 8 * context: import('@actions/github/lib/context').Context, 9 * core: import('@actions/core'), 10 * repoPath?: string, 11 * }} CheckCommitMessagesProps 12 */ 13async function checkCommitMessages({ github, context, core, repoPath }) { 14 // This check should only be run when we have the pull_request context. 15 const pull_number = context.payload.pull_request?.number 16 if (!pull_number) { 17 core.info('This is not a pull request. Skipping checks.') 18 return 19 } 20 21 const pr = ( 22 await github.rest.pulls.get({ 23 ...context.repo, 24 pull_number, 25 }) 26 ).data 27 28 const baseBranchType = classify( 29 pr.base.ref.replace(/^refs\/heads\//, ''), 30 ).type 31 const headBranchType = classify( 32 pr.head.ref.replace(/^refs\/heads\//, ''), 33 ).type 34 35 if ( 36 baseBranchType.includes('development') && 37 headBranchType.includes('development') && 38 pr.base.repo.id === pr.head.repo?.id 39 ) { 40 // This matches, for example, PRs from NixOS:staging-next to NixOS:master, or vice versa. 41 // Ignore them: we should only care about PRs introducing *new* commits. 42 // We still want to run on PRs from, e.g., Someone:master to NixOS:master, though. 43 core.info( 44 'This PR is from one development branch to another. Skipping checks.', 45 ) 46 return 47 } 48 49 const commits = await getCommitDetailsForPR({ core, pr, repoPath }) 50 51 const failures = new Set() 52 53 const conventionalCommitTypes = [ 54 'build', 55 'chore', 56 'ci', 57 'doc', 58 'docs', 59 'feat', 60 'feature', 61 'fix', 62 'perf', 63 'refactor', 64 'style', 65 'test', 66 ] 67 68 /** 69 * @param {string[]} types e.g. ["fix", "feat"] 70 * @param {string?} sha commit hash 71 */ 72 function makeConventionalCommitRegex(types, sha = null) { 73 core.info( 74 `${ 75 sha 76 ? `Conventional commit types for ${sha?.slice(0, 16)}` 77 : 'Default conventional commit types' 78 }: ${JSON.stringify(types)}`, 79 ) 80 81 return new RegExp(`^(${types.join('|')})!?(\\(.*\\))?!?:`) 82 } 83 84 // Optimize for the common case that we don't have path segments with the 85 // same name as a conventional commit type. 86 const fullConventionalCommitRegex = makeConventionalCommitRegex( 87 conventionalCommitTypes, 88 ) 89 90 for (const commit of commits) { 91 const logMsgStart = `Commit ${commit.sha}'s message's subject ("${commit.subject}")` 92 93 // If we have a commit `perf: ...`, and we touch a file containing the path 94 // segment "perf", we don't want to flag this. 95 const filteredTypes = conventionalCommitTypes.filter( 96 (type) => !commit.changedPathSegments.has(type), 97 ) 98 const conventionalCommitRegex = 99 filteredTypes.length === conventionalCommitTypes.length 100 ? fullConventionalCommitRegex 101 : makeConventionalCommitRegex(filteredTypes, commit.sha) 102 103 if (!commit.subject.includes(': ')) { 104 core.error( 105 `${logMsgStart} was detected as not meeting our guidelines because ` + 106 'it does not contain a colon followed by a whitespace. ' + 107 'There are likely other issues as well.', 108 ) 109 failures.add(commit.sha) 110 } 111 112 if (commit.subject.endsWith('.')) { 113 core.error( 114 `${logMsgStart} was detected as not meeting our guidelines because ` + 115 'it ends in a period. There may be other issues as well.', 116 ) 117 failures.add(commit.sha) 118 } 119 120 const fixups = ['amend!', 'fixup!', 'squash!'] 121 if (fixups.some((s) => commit.subject.startsWith(s))) { 122 core.error( 123 `${logMsgStart} was detected as not meeting our guidelines because ` + 124 `it begins with "${fixups.find((s) => commit.subject.startsWith(s))}". ` + 125 'Did you forget to run `git rebase -i --autosquash`?', 126 ) 127 failures.add(commit.sha) 128 } 129 130 if (conventionalCommitRegex.test(commit.subject)) { 131 core.error( 132 `${logMsgStart} was detected as not meeting our guidelines because ` + 133 'it seems to use conventional commit (conventionalcommits.org) ' + 134 'formatting. Nixpkgs has its own, different, commit message ' + 135 'formatting standards.', 136 ) 137 failures.add(commit.sha) 138 } 139 140 if (!failures.has(commit.sha)) { 141 core.info(`${logMsgStart} passed our automated checks!`) 142 } 143 } 144 145 if (failures.size !== 0) { 146 core.error( 147 'Please review the guidelines at ' + 148 '<https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#commit-conventions>, ' + 149 'as well as the applicable area-specific guidelines linked there.', 150 ) 151 core.setFailed('Committers: merging is discouraged.') 152 } 153} 154 155module.exports = checkCommitMessages