Loading .github/workflows/lint.yml +22 −0 Original line number Diff line number Diff line Loading @@ -124,3 +124,25 @@ jobs: echo "If you're having trouble, ping @NixOS/nixpkgs-vet" exit "$exitCode" fi commits: runs-on: ubuntu-slim timeout-minutes: 5 steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false path: trusted sparse-checkout: | ci/github-script - name: Check commit messages uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | const checkCommitMessages = require('./trusted/ci/github-script/lint-commits.js') checkCommitMessages({ github, context, core, }) ci/github-script/lint-commits.js 0 → 100644 +90 −0 Original line number Diff line number Diff line // @ts-check const { classify } = require('../supportedBranches.js') /** * @param {{ * github: InstanceType<import('@actions/github/lib/utils').GitHub>, * context: import('@actions/github/lib/context').Context * core: import('@actions/core') * }} CheckCommitMessagesProps */ async function checkCommitMessages({ github, context, core }) { // Do not run on merge groups: it'd be much harder, and there is no need // (changes to the target branch can't change commit messages on the base branch). const pull_number = context.payload.pull_request?.number if (!pull_number) { core.info('This is not a pull request. Skipping checks.') return } const pr = ( await github.rest.pulls.get({ ...context.repo, pull_number, }) ).data const baseBranchType = classify( pr.base.ref.replace(/^refs\/heads\//, ''), ).type const headBranchType = classify( pr.head.ref.replace(/^refs\/heads\//, ''), ).type if ( baseBranchType.includes('development') && headBranchType.includes('development') ) { // This matches, for example, PRs from staging-next to master, or vice versa. // Ignore them: we should only care about PRs introducing *new* commits. core.info( 'This PR is from one development branch to another. Skipping checks.', ) return } const commits = await github.paginate(github.rest.pulls.listCommits, { ...context.repo, pull_number, }) const failures = new Set() for (const commit of commits) { const message = commit.commit.message const firstLine = message.split('\n')[0] const logMsgStart = `Commit ${commit.sha}'s message's subject ("${firstLine}")` if (!firstLine.includes(':')) { core.error( `${logMsgStart} was detected as not meeting our guidelines because ` + 'it does not contain a colon. There are likely other issues as well.', ) failures.add(commit.sha) } if (firstLine.endsWith('.')) { core.error( `${logMsgStart} was detected as not meeting our guidelines because ` + 'it ends in a period. There may be other issues as well.', ) failures.add(commit.sha) } if (!failures.has(commit.sha)) { core.info(`${logMsgStart} passed our automated checks!`) } } if (failures.size !== 0) { core.error( 'Please review the guidelines at ' + 'https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#commit-conventions, ' + 'as well as the applicable area-specific guidelines linked there.', ) core.setFailed('Committers: merging is discouraged.') } } module.exports = checkCommitMessages ci/github-script/run +11 −0 Original line number Diff line number Diff line Loading @@ -94,4 +94,15 @@ program await run(getTeams, owner, repo, undefined, { ...options, outFile }) }) program .command('lint-commits') .description('Lint for common errors in commit messages') .argument('<owner>', 'Owner of the GitHub repository to run on (Example: NixOS)') .argument('<repo>', 'Name of the GitHub repository to run on (Example: nixpkgs)') .argument('<pr>', 'Number of the Pull Request to run on') .action(async (owner, repo, pr, options) => { const checkCommitMessages = (await import('./lint-commits.js')).default await run(checkCommitMessages, owner, repo, pr, options) }) await program.parse() Loading
.github/workflows/lint.yml +22 −0 Original line number Diff line number Diff line Loading @@ -124,3 +124,25 @@ jobs: echo "If you're having trouble, ping @NixOS/nixpkgs-vet" exit "$exitCode" fi commits: runs-on: ubuntu-slim timeout-minutes: 5 steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false path: trusted sparse-checkout: | ci/github-script - name: Check commit messages uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | const checkCommitMessages = require('./trusted/ci/github-script/lint-commits.js') checkCommitMessages({ github, context, core, })
ci/github-script/lint-commits.js 0 → 100644 +90 −0 Original line number Diff line number Diff line // @ts-check const { classify } = require('../supportedBranches.js') /** * @param {{ * github: InstanceType<import('@actions/github/lib/utils').GitHub>, * context: import('@actions/github/lib/context').Context * core: import('@actions/core') * }} CheckCommitMessagesProps */ async function checkCommitMessages({ github, context, core }) { // Do not run on merge groups: it'd be much harder, and there is no need // (changes to the target branch can't change commit messages on the base branch). const pull_number = context.payload.pull_request?.number if (!pull_number) { core.info('This is not a pull request. Skipping checks.') return } const pr = ( await github.rest.pulls.get({ ...context.repo, pull_number, }) ).data const baseBranchType = classify( pr.base.ref.replace(/^refs\/heads\//, ''), ).type const headBranchType = classify( pr.head.ref.replace(/^refs\/heads\//, ''), ).type if ( baseBranchType.includes('development') && headBranchType.includes('development') ) { // This matches, for example, PRs from staging-next to master, or vice versa. // Ignore them: we should only care about PRs introducing *new* commits. core.info( 'This PR is from one development branch to another. Skipping checks.', ) return } const commits = await github.paginate(github.rest.pulls.listCommits, { ...context.repo, pull_number, }) const failures = new Set() for (const commit of commits) { const message = commit.commit.message const firstLine = message.split('\n')[0] const logMsgStart = `Commit ${commit.sha}'s message's subject ("${firstLine}")` if (!firstLine.includes(':')) { core.error( `${logMsgStart} was detected as not meeting our guidelines because ` + 'it does not contain a colon. There are likely other issues as well.', ) failures.add(commit.sha) } if (firstLine.endsWith('.')) { core.error( `${logMsgStart} was detected as not meeting our guidelines because ` + 'it ends in a period. There may be other issues as well.', ) failures.add(commit.sha) } if (!failures.has(commit.sha)) { core.info(`${logMsgStart} passed our automated checks!`) } } if (failures.size !== 0) { core.error( 'Please review the guidelines at ' + 'https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#commit-conventions, ' + 'as well as the applicable area-specific guidelines linked there.', ) core.setFailed('Committers: merging is discouraged.') } } module.exports = checkCommitMessages
ci/github-script/run +11 −0 Original line number Diff line number Diff line Loading @@ -94,4 +94,15 @@ program await run(getTeams, owner, repo, undefined, { ...options, outFile }) }) program .command('lint-commits') .description('Lint for common errors in commit messages') .argument('<owner>', 'Owner of the GitHub repository to run on (Example: NixOS)') .argument('<repo>', 'Name of the GitHub repository to run on (Example: nixpkgs)') .argument('<pr>', 'Number of the Pull Request to run on') .action(async (owner, repo, pr, options) => { const checkCommitMessages = (await import('./lint-commits.js')).default await run(checkCommitMessages, owner, repo, pr, options) }) await program.parse()