nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1/// @ts-check
2
3// TODO: should this be combined with the branch checks in prepare.js?
4// They do seem quite similar, but this needs to run after eval,
5// and prepare.js obviously doesn't.
6
7const { classify, split } = require('../supportedBranches.js')
8const { readFile } = require('node:fs/promises')
9const { postReview, dismissReviews } = require('./reviews.js')
10
11const reviewKey = 'check-target-branch'
12/**
13 * @param {{
14 * github: InstanceType<import('@actions/github/lib/utils').GitHub>,
15 * context: import('@actions/github/lib/context').Context
16 * core: import('@actions/core')
17 * dry: boolean
18 * }} CheckTargetBranchProps
19 */
20async function checkTargetBranch({ github, context, core, dry }) {
21 /**
22 * @type {{
23 * attrdiff: {
24 * added: string[],
25 * changed: string[],
26 * removed: string[],
27 * },
28 * labels: Record<string, boolean>,
29 * rebuildCountByKernel: Record<string, number>,
30 * rebuildsByKernel: Record<string, string[]>,
31 * rebuildsByPlatform: Record<string, string[]>,
32 * }}
33 */
34 const changed = JSON.parse(
35 await readFile('comparison/changed-paths.json', 'utf-8'),
36 )
37 const pull_number = context.payload.pull_request?.number
38 if (!pull_number) {
39 core.warning(
40 'Skipping checkTargetBranch: no pull_request number (is this being run as part of a merge group?)',
41 )
42 return
43 }
44 const prInfo = (
45 await github.rest.pulls.get({
46 ...context.repo,
47 pull_number,
48 })
49 ).data
50 const base = prInfo.base.ref
51 const head = prInfo.head.ref
52 const baseClassification = classify(base)
53 const headClassification = classify(head)
54
55 // Don't run on, e.g., staging-nixos to master merges.
56 if (headClassification.type.includes('development')) {
57 core.info(
58 `Skipping checkTargetBranch: PR is from a development branch (${head})`,
59 )
60
61 await dismissReviews({
62 github,
63 context,
64 core,
65 dry,
66 reviewKey,
67 })
68
69 return
70 }
71 // Don't run on PRs against staging branches, wip branches, haskell-updates, etc.
72 if (!baseClassification.type.includes('primary')) {
73 core.info(
74 `Skipping checkTargetBranch: PR is against a non-primary base branch (${base})`,
75 )
76
77 await dismissReviews({
78 github,
79 context,
80 core,
81 dry,
82 reviewKey,
83 })
84
85 return
86 }
87
88 const maxRebuildCount = Math.max(
89 ...Object.values(changed.rebuildCountByKernel),
90 )
91 const rebuildsAllTests =
92 changed.attrdiff.changed.includes('nixosTests.simple')
93
94 // https://github.com/NixOS/nixpkgs/pull/481205#issuecomment-3790123921
95 // These should go to staging-nixos instead of master,
96 // but release-xx.xx (not staging-xx.xx) when backported
97 let isExemptKernelUpdate = false
98 if (prInfo.changed_files === 1 && base.startsWith('release-')) {
99 const changedFiles = (
100 await github.rest.pulls.listFiles({
101 ...context.repo,
102 pull_number,
103 })
104 ).data
105 isExemptKernelUpdate =
106 changedFiles.length === 1 &&
107 changedFiles[0].filename ===
108 'pkgs/os-specific/linux/kernel/kernels-org.json'
109 }
110
111 // https://github.com/NixOS/nixpkgs/pull/483194#issuecomment-3793393218
112 const isExemptHomeAssistantUpdate =
113 maxRebuildCount <= 1500 && head === 'wip-home-assistant'
114
115 core.info(
116 [
117 `checkTargetBranch: this PR:`,
118 ` * causes ${maxRebuildCount} rebuilds`,
119 ` * ${rebuildsAllTests ? 'rebuilds' : 'does not rebuild'} all NixOS tests`,
120 ` * ${isExemptKernelUpdate ? 'is' : 'is not'} an exempt kernel update`,
121 ` * ${isExemptHomeAssistantUpdate ? 'is' : 'is not'} an exempt home-assistant update`,
122 ].join('\n'),
123 )
124
125 if (
126 maxRebuildCount >= 1000 &&
127 !isExemptHomeAssistantUpdate &&
128 !isExemptKernelUpdate
129 ) {
130 const desiredBranch =
131 base === 'master' ? 'staging' : `staging-${split(base).version}`
132 const body = [
133 `The PR's base branch is set to \`${base}\`, but this PR causes ${maxRebuildCount} rebuilds.`,
134 'It is therefore considered a mass rebuild.',
135 `Please [change the base branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-base-branch-of-a-pull-request) to [the right base branch for your changes](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#branch-conventions) (probably \`${desiredBranch}\`).`,
136 ].join('\n')
137
138 await postReview({
139 github,
140 context,
141 core,
142 dry,
143 body,
144 event: 'COMMENT',
145 reviewKey,
146 })
147
148 throw new Error('This PR is against the wrong branch.')
149 } else if (rebuildsAllTests && !isExemptKernelUpdate) {
150 let branchText
151 if (base === 'master' && maxRebuildCount >= 500) {
152 branchText = '(probably either `staging-nixos` or `staging`)'
153 } else if (base === 'master') {
154 branchText = '(probably `staging-nixos`)'
155 } else {
156 branchText = `(probably \`staging-${split(base).version}\`)`
157 }
158 const body = [
159 `The PR's base branch is set to \`${base}\`, but this PR rebuilds all NixOS tests.`,
160 base === 'master' && maxRebuildCount >= 500
161 ? `Since this PR also causes ${maxRebuildCount} rebuilds, it may also be considered a mass rebuild.`
162 : '',
163 `Please [change the base branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-base-branch-of-a-pull-request) to [the right base branch for your changes](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#branch-conventions) ${branchText}.`,
164 ].join('\n')
165
166 await postReview({
167 github,
168 context,
169 core,
170 dry,
171 body,
172 event: 'COMMENT',
173 reviewKey,
174 })
175
176 throw new Error('This PR is against the wrong branch.')
177 } else if (
178 maxRebuildCount >= 500 &&
179 !isExemptKernelUpdate &&
180 !isExemptHomeAssistantUpdate
181 ) {
182 const stagingBranch =
183 base === 'master' ? 'staging' : `staging-${split(base).version}`
184 const body = [
185 `The PR's base branch is set to \`${base}\`, and this PR causes ${maxRebuildCount} rebuilds.`,
186 `Please consider whether this PR causes a mass rebuild according to [our conventions](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#branch-conventions).`,
187 `If it does cause a mass rebuild, please [change the base branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-base-branch-of-a-pull-request) to [the right base branch for your changes](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#branch-conventions) (probably \`${stagingBranch}\`).`,
188 `If it does not cause a mass rebuild, this message can be ignored.`,
189 ].join('\n')
190
191 await postReview({
192 github,
193 context,
194 core,
195 dry,
196 body,
197 event: 'COMMENT',
198 reviewKey,
199 })
200 } else {
201 core.info('checkTargetBranch: this PR is against an appropriate branch.')
202
203 await dismissReviews({
204 github,
205 context,
206 core,
207 dry,
208 reviewKey,
209 })
210 }
211}
212
213module.exports = checkTargetBranch