summaryrefslogtreecommitdiffstats
path: root/ci/github-script/manual-file-edits.js
blob: bac83d1ce6b28e7703e4977c4ba5b8c645ee1372 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { getCommitDetailsForPR } from './get-pr-commit-details.js'
import { dismissReviews, postReview } from './reviews.js'
import { classify } from './supportedBranches.js'

/**
 * @param {{
 *  github: InstanceType<typeof import('@actions/github/lib/utils').GitHub>,
 *  context: typeof import('@actions/github').context,
 *  core: typeof import('@actions/core'),
 *  repoPath?: string,
 *  dry: boolean,
 * }} CheckManualFileEditsProps
 */
export default async function checkManualFileEdits({
  github,
  context,
  core,
  repoPath,
  dry,
}) {
  const reviewKey = 'manual-file-edits'

  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

  if (pr.user.login.endsWith('[bot]')) {
    core.info('This is a bot, so these checks 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.
    // 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 checks.',
    )
    return
  }

  const details = await getCommitDetailsForPR({ core, pr, repoPath })

  if (
    details.some(({ changedPaths }) =>
      changedPaths.includes('maintainers/github-teams.json'),
    )
  ) {
    postReview({
      github,
      context,
      core,
      dry,
      event: 'REQUEST_CHANGES',
      body: [
        'maintainers/github-teams.json is supposed to accurately reflect the state of the teams in GitHub.\n',
        'Therefore, it should not be edited manually.\n',
        'All changes to teams listed in maintainers/github-teams.json should be performed in GitHub by a team maintainer.\n',
        "Team maintainers are listed in the github-teams.json file and in GitHub's UI.\n",
        'If there is no team maintainer available, an org owner can make the needed change, please contact one by',
        'following the instructions at https://github.com/NixOS/org/blob/main/doc/github-org-owners.md#how-to-contact-the-team.\n',
        'Thank you!',
      ].reduce(
        (prev, curr) => prev + (!prev || prev.endsWith('\n') ? '' : ' ') + curr,
        '',
      ),
      reviewKey,
    })
  } else {
    dismissReviews({
      github,
      context,
      core,
      dry,
      reviewKey,
    })
  }
}