Skip to content

Commit 27ed56e

Browse files
authored
ci: point documentation PRs at the styleguide (#562912)
2 parents 61a6516 + 40bb731 commit 27ed56e

7 files changed

Lines changed: 193 additions & 1 deletion

File tree

‎.github/workflows/check.yml‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ on:
2424
# not evaluate untrusted code.
2525
NIXPKGS_MANUAL_EDIT_CHECK_APP_PRIVATE_KEY:
2626
required: false
27+
# Can be provided in pull requests because the job it is used in does
28+
# not evaluate untrusted code.
29+
NIXPKGS_CI_APP_PRIVATE_KEY:
30+
required: false
2731
# Should only be provided in the merge queue, not in pull requests,
2832
# where we're evaluating untrusted code.
2933
CACHIX_AUTH_TOKEN_GHA:
@@ -136,6 +140,52 @@ jobs:
136140
GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
137141
run: gh api /rate_limit | jq
138142

143+
reminders:
144+
if: inputs.baseBranch && inputs.headBranch
145+
permissions:
146+
pull-requests: write
147+
runs-on: ubuntu-24.04-arm
148+
timeout-minutes: 8
149+
steps:
150+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
151+
with:
152+
persist-credentials: false
153+
path: trusted
154+
sparse-checkout: |
155+
ci/github-script
156+
157+
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
158+
if: github.event_name != 'pull_request' && vars.NIXPKGS_CI_CLIENT_ID
159+
id: app-token
160+
with:
161+
client-id: ${{ vars.NIXPKGS_CI_CLIENT_ID }}
162+
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
163+
permission-pull-requests: write
164+
165+
- name: Log current API rate limits
166+
env:
167+
GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
168+
run: gh api /rate_limit | jq
169+
170+
- name: Post reminders
171+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
172+
with:
173+
github-token: ${{ steps.app-token.outputs.token || github.token }}
174+
script: |
175+
const { default: postReminders } = await import('${{ github.workspace }}/trusted/ci/github-script/reminders.ts')
176+
await postReminders({
177+
github,
178+
context,
179+
core,
180+
dry: context.eventName == 'pull_request',
181+
repoPath: 'trusted',
182+
})
183+
184+
- name: Log current API rate limits
185+
env:
186+
GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
187+
run: gh api /rate_limit | jq
188+
139189
github-script:
140190
runs-on: ubuntu-24.04-arm
141191
timeout-minutes: 5

‎.github/workflows/pull-request-target.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ jobs:
8282
secrets:
8383
NIXPKGS_COMMIT_CHECK_APP_PRIVATE_KEY: ${{ secrets.NIXPKGS_COMMIT_CHECK_APP_PRIVATE_KEY }}
8484
NIXPKGS_MANUAL_EDIT_CHECK_APP_PRIVATE_KEY: ${{ secrets.NIXPKGS_MANUAL_EDIT_CHECK_APP_PRIVATE_KEY }}
85+
NIXPKGS_CI_APP_PRIVATE_KEY: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
8586
with:
8687
baseBranch: ${{ needs.prepare.outputs.baseBranch }}
8788
headBranch: ${{ needs.prepare.outputs.headBranch }}

‎.github/workflows/test.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,15 @@ jobs:
8989
'ci/github-script/package.json',
9090
'ci/github-script/package-lock.json',
9191
'ci/github-script/prepare.js',
92+
'ci/github-script/reminders.ts',
9293
'ci/github-script/reviewers.js',
9394
'ci/github-script/reviews.js',
9495
'ci/github-script/supportedBranches.js',
9596
'ci/github-script/supportedSystems.js',
9697
'ci/github-script/withRateLimit.js',
9798
'ci/pinned.json',
9899
'pkgs/top-level/release-supported-systems.json',
99-
].includes(file))) core.setOutput('pr', true)
100+
].includes(file) || file.startsWith('ci/github-script/reminders/'))) core.setOutput('pr', true)
100101
101102
merge-group:
102103
if: needs.prepare.outputs.merge-group

‎ci/github-script/README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,9 @@ Run `./run commits OWNER REPO PR`, where OWNER is your username or "NixOS", REPO
1515
## Labeler
1616

1717
Run `./run labels OWNER REPO`, where OWNER is your username or "NixOS" and REPO the name of your fork or "nixpkgs".
18+
19+
## Reminders
20+
21+
Run `./run reminders OWNER REPO PR` to post the reminders that apply to the paths a pull request touches.
22+
23+
To add a reminder, write the review body to `reminders/KEY.md` and list KEY with its path regexes in `reminders.js`.

‎ci/github-script/reminders.ts‎

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import fs from 'node:fs'
2+
import path from 'node:path'
3+
import type * as actionsCore from '@actions/core'
4+
import type { context as actionsContext } from '@actions/github'
5+
import type { GitHub } from '@actions/github/lib/utils'
6+
import { getCommitDetailsForPR } from './get-pr-commit-details.js'
7+
import { dismissReviews, postReview } from './reviews.js'
8+
import { classify } from './supportedBranches.js'
9+
10+
/**
11+
* Reminders to post as a non-blocking review when a pull request touches
12+
* certain matching paths.
13+
*/
14+
export const reminders: { key: string; paths: RegExp[] }[] = [
15+
{
16+
key: 'docs-styleguide',
17+
paths: [/^doc\//, /^nixos\/doc\//, /^nixos\/modules\/.*\.md$/],
18+
},
19+
]
20+
21+
export function matchesAny(changedPaths: string[], patterns: RegExp[]) {
22+
return changedPaths.some((changedPath) =>
23+
patterns.some((pattern) => pattern.test(changedPath)),
24+
)
25+
}
26+
27+
function reminderBody(key: string) {
28+
return fs
29+
.readFileSync(path.join(import.meta.dirname, 'reminders', `${key}.md`), {
30+
encoding: 'utf8',
31+
})
32+
.trim()
33+
}
34+
35+
export default async function postReminders({
36+
github,
37+
context,
38+
core,
39+
repoPath,
40+
dry,
41+
}: {
42+
github: InstanceType<typeof GitHub>
43+
context: typeof actionsContext
44+
core: typeof actionsCore
45+
repoPath?: string
46+
dry: boolean
47+
}) {
48+
const pull_number = context.payload.pull_request?.number
49+
if (!pull_number) {
50+
core.info('This is not a pull request. Skipping reminders.')
51+
return
52+
}
53+
54+
const pr = (
55+
await github.rest.pulls.get({
56+
...context.repo,
57+
pull_number,
58+
})
59+
).data
60+
61+
if (pr.user.login.endsWith('[bot]')) {
62+
core.info('This is a bot, so reminders do not apply.')
63+
return
64+
}
65+
66+
const baseBranchType = classify(
67+
pr.base.ref.replace(/^refs\/heads\//, ''),
68+
).type
69+
const headBranchType = classify(
70+
pr.head.ref.replace(/^refs\/heads\//, ''),
71+
).type
72+
73+
if (
74+
baseBranchType.includes('development') &&
75+
headBranchType.includes('development') &&
76+
pr.base.repo.id === pr.head.repo?.id
77+
) {
78+
// This matches, for example, PRs from NixOS:staging-next to NixOS:master, or vice versa.
79+
// We ignore them, we should only care about PRs introducing new commits.
80+
// We still want to run on PRs from, e.g., Someone:master to NixOS:master though.
81+
core.info(
82+
'This PR is from one development branch to another. Skipping reminders.',
83+
)
84+
return
85+
}
86+
87+
const details = await getCommitDetailsForPR({ core, pr, repoPath })
88+
const changedPaths = details.flatMap(({ changedPaths }) => changedPaths)
89+
90+
for (const { key, paths } of reminders) {
91+
if (matchesAny(changedPaths, paths)) {
92+
await postReview({
93+
github,
94+
context,
95+
core,
96+
dry,
97+
event: 'COMMENT',
98+
body: reminderBody(key),
99+
reviewKey: key,
100+
})
101+
} else {
102+
await dismissReviews({
103+
github,
104+
context,
105+
core,
106+
dry,
107+
reviewKey: key,
108+
})
109+
}
110+
}
111+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Thanks for contributing to the documentation
2+
3+
Make sure you follow the [documentation styleguide](https://github.com/NixOS/nixpkgs/blob/master/doc/styleguide.md), most notably:
4+
5+
- Show, don't tell: lead with a minimal working example; explanation follows the code.
6+
- No meta-commentary: don't write "This section explains how to…", just do it.
7+
- Imperative mood and active voice: "Run the command", not "The user should run the following command".
8+
- Present tense: "This creates a folder", not "This will create a folder".
9+
- Be confident: no hedging with "should", "might", "typically", "usually".
10+
- Cut filler words: "simply", "just", "easily", "basically"; "to", not "in order to".
11+
12+
For larger changes, like adding or removing whole sections, ask the NixOS documentation team for a review.

‎ci/github-script/run‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,15 @@ program
127127
await run(checkManualFileEdits, owner, repo, pr, options)
128128
})
129129

130+
program
131+
.command('reminders')
132+
.description('Post reminders for the paths a PR touches')
133+
.argument('<owner>', 'Owner of the GitHub repository to run on (Example: NixOS)')
134+
.argument('<repo>', 'Name of the GitHub repository to run on (Example: nixpkgs)')
135+
.argument('<pr>', 'Number of the Pull Request to run on')
136+
.action(async (owner, repo, pr, options) => {
137+
const postReminders = (await import('./reminders.ts')).default
138+
await run(postReminders, owner, repo, pr, options)
139+
})
140+
130141
await program.parse()

0 commit comments

Comments
 (0)