ci: post a reminder for updated paths

docs are the first example

Assisted-by: Oh My Pi (Claude Opus 5)
This commit is contained in:
Johannes Kirschbauer
2026-09-13 16:26:06 +02:00
parent 815d65cd24
commit 40bb73107d
7 changed files with 193 additions and 1 deletions

View File

@@ -24,6 +24,10 @@ on:
# not evaluate untrusted code.
NIXPKGS_MANUAL_EDIT_CHECK_APP_PRIVATE_KEY:
required: false
# Can be provided in pull requests because the job it is used in does
# not evaluate untrusted code.
NIXPKGS_CI_APP_PRIVATE_KEY:
required: false
# Should only be provided in the merge queue, not in pull requests,
# where we're evaluating untrusted code.
CACHIX_AUTH_TOKEN_GHA:
@@ -136,6 +140,52 @@ jobs:
GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
run: gh api /rate_limit | jq
reminders:
if: inputs.baseBranch && inputs.headBranch
permissions:
pull-requests: write
runs-on: ubuntu-24.04-arm
timeout-minutes: 8
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
path: trusted
sparse-checkout: |
ci/github-script
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
if: github.event_name != 'pull_request' && vars.NIXPKGS_CI_CLIENT_ID
id: app-token
with:
client-id: ${{ vars.NIXPKGS_CI_CLIENT_ID }}
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
permission-pull-requests: write
- name: Log current API rate limits
env:
GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
run: gh api /rate_limit | jq
- name: Post reminders
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ steps.app-token.outputs.token || github.token }}
script: |
const { default: postReminders } = await import('${{ github.workspace }}/trusted/ci/github-script/reminders.ts')
await postReminders({
github,
context,
core,
dry: context.eventName == 'pull_request',
repoPath: 'trusted',
})
- name: Log current API rate limits
env:
GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
run: gh api /rate_limit | jq
github-script:
runs-on: ubuntu-24.04-arm
timeout-minutes: 5

View File

@@ -82,6 +82,7 @@ jobs:
secrets:
NIXPKGS_COMMIT_CHECK_APP_PRIVATE_KEY: ${{ secrets.NIXPKGS_COMMIT_CHECK_APP_PRIVATE_KEY }}
NIXPKGS_MANUAL_EDIT_CHECK_APP_PRIVATE_KEY: ${{ secrets.NIXPKGS_MANUAL_EDIT_CHECK_APP_PRIVATE_KEY }}
NIXPKGS_CI_APP_PRIVATE_KEY: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
with:
baseBranch: ${{ needs.prepare.outputs.baseBranch }}
headBranch: ${{ needs.prepare.outputs.headBranch }}

View File

@@ -88,6 +88,7 @@ jobs:
'ci/github-script/package.json',
'ci/github-script/package-lock.json',
'ci/github-script/prepare.js',
'ci/github-script/reminders.ts',
'ci/github-script/reviewers.js',
'ci/github-script/reviews.js',
'ci/github-script/supportedBranches.js',
@@ -95,7 +96,7 @@ jobs:
'ci/github-script/withRateLimit.js',
'ci/pinned.json',
'pkgs/top-level/release-supported-systems.json',
].includes(file))) core.setOutput('pr', true)
].includes(file) || file.startsWith('ci/github-script/reminders/'))) core.setOutput('pr', true)
merge-group:
if: needs.prepare.outputs.merge-group

View File

@@ -15,3 +15,9 @@ Run `./run commits OWNER REPO PR`, where OWNER is your username or "NixOS", REPO
## Labeler
Run `./run labels OWNER REPO`, where OWNER is your username or "NixOS" and REPO the name of your fork or "nixpkgs".
## Reminders
Run `./run reminders OWNER REPO PR` to post the reminders that apply to the paths a pull request touches.
To add a reminder, write the review body to `reminders/KEY.md` and list KEY with its path regexes in `reminders.js`.

View File

@@ -0,0 +1,111 @@
import fs from 'node:fs'
import path from 'node:path'
import type * as actionsCore from '@actions/core'
import type { context as actionsContext } from '@actions/github'
import type { GitHub } from '@actions/github/lib/utils'
import { getCommitDetailsForPR } from './get-pr-commit-details.js'
import { dismissReviews, postReview } from './reviews.js'
import { classify } from './supportedBranches.js'
/**
* Reminders to post as a non-blocking review when a pull request touches
* certain matching paths.
*/
export const reminders: { key: string; paths: RegExp[] }[] = [
{
key: 'docs-styleguide',
paths: [/^doc\//, /^nixos\/doc\//, /^nixos\/modules\/.*\.md$/],
},
]
export function matchesAny(changedPaths: string[], patterns: RegExp[]) {
return changedPaths.some((changedPath) =>
patterns.some((pattern) => pattern.test(changedPath)),
)
}
function reminderBody(key: string) {
return fs
.readFileSync(path.join(import.meta.dirname, 'reminders', `${key}.md`), {
encoding: 'utf8',
})
.trim()
}
export default async function postReminders({
github,
context,
core,
repoPath,
dry,
}: {
github: InstanceType<typeof GitHub>
context: typeof actionsContext
core: typeof actionsCore
repoPath?: string
dry: boolean
}) {
const pull_number = context.payload.pull_request?.number
if (!pull_number) {
core.info('This is not a pull request. Skipping reminders.')
return
}
const pr = (
await github.rest.pulls.get({
...context.repo,
pull_number,
})
).data
if (pr.user.login.endsWith('[bot]')) {
core.info('This is a bot, so reminders do not apply.')
return
}
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') &&
pr.base.repo.id === pr.head.repo?.id
) {
// This matches, for example, PRs from NixOS:staging-next to NixOS:master, or vice versa.
// We ignore them, we should only care about PRs introducing new commits.
// We still want to run on PRs from, e.g., Someone:master to NixOS:master though.
core.info(
'This PR is from one development branch to another. Skipping reminders.',
)
return
}
const details = await getCommitDetailsForPR({ core, pr, repoPath })
const changedPaths = details.flatMap(({ changedPaths }) => changedPaths)
for (const { key, paths } of reminders) {
if (matchesAny(changedPaths, paths)) {
await postReview({
github,
context,
core,
dry,
event: 'COMMENT',
body: reminderBody(key),
reviewKey: key,
})
} else {
await dismissReviews({
github,
context,
core,
dry,
reviewKey: key,
})
}
}
}

View File

@@ -0,0 +1,12 @@
Thanks for contributing to the documentation
Make sure you follow the [documentation styleguide](https://github.com/NixOS/nixpkgs/blob/master/doc/styleguide.md), most notably:
- Show, don't tell: lead with a minimal working example; explanation follows the code.
- No meta-commentary: don't write "This section explains how to…", just do it.
- Imperative mood and active voice: "Run the command", not "The user should run the following command".
- Present tense: "This creates a folder", not "This will create a folder".
- Be confident: no hedging with "should", "might", "typically", "usually".
- Cut filler words: "simply", "just", "easily", "basically"; "to", not "in order to".
For larger changes, like adding or removing whole sections, ask the NixOS documentation team for a review.

View File

@@ -127,4 +127,15 @@ program
await run(checkManualFileEdits, owner, repo, pr, options)
})
program
.command('reminders')
.description('Post reminders for the paths a PR touches')
.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 postReminders = (await import('./reminders.ts')).default
await run(postReminders, owner, repo, pr, options)
})
await program.parse()