ci/github-script: convert to ES modules

ES modules work better with TypeScript than CommonJS modules do.

Assisted-by: GPT 5.6 Luna & Terra (Codex)
This commit is contained in:
Michael Daniels
2026-08-30 12:40:34 -04:00
parent 2d813b7719
commit eb501c45ae
30 changed files with 115 additions and 114 deletions

View File

@@ -1,13 +1,13 @@
// @ts-nocheck
module.exports = async ({ github, context, core, dry }) => {
const path = require('node:path')
const { DefaultArtifactClient } = await import('@actions/artifact')
const { readFile, writeFile } = require('node:fs/promises')
const withRateLimit = require('./withRateLimit.js')
const { classify } = require('./supportedBranches.js')
const { handleMerge } = require('./merge.js')
const { handleReviewers } = require('./reviewers.js')
import { readFile, writeFile } from 'node:fs/promises'
import path from 'node:path'
import { DefaultArtifactClient } from '@actions/artifact'
import { handleMerge } from './merge.js'
import { handleReviewers } from './reviewers.js'
import { classify } from './supportedBranches.js'
import withRateLimit from './withRateLimit.js'
export default async ({ github, context, core, dry }) => {
const artifactClient = new DefaultArtifactClient()
// Detect if running in a fork (not NixOS/nixpkgs)

View File

@@ -1,8 +1,6 @@
const assert = require('node:assert/strict')
const test = require('node:test')
const {
evaluateTargetBranchPolicy,
} = require('./check-target-branch-policy.ts')
import assert from 'node:assert/strict'
import test from 'node:test'
import { evaluateTargetBranchPolicy } from './check-target-branch-policy.ts'
type DecisionFacts = {
base: string

View File

@@ -1,4 +1,4 @@
const { classify, split } = require('./supportedBranches.js')
import { classify, split } from './supportedBranches.js'
type TargetBranchPolicyFacts = {
base: string
@@ -23,7 +23,13 @@ type TargetBranchPolicyResult = {
}
}
function getTargetBranchPolicy({ base, head }: { base: string; head: string }) {
export function getTargetBranchPolicy({
base,
head,
}: {
base: string
head: string
}) {
const baseClassification = classify(base)
const headClassification = classify(head)
const isPrimaryBase = baseClassification.type.includes('primary')
@@ -42,7 +48,7 @@ function getTargetBranchPolicy({ base, head }: { base: string; head: string }) {
}
}
function evaluateTargetBranchPolicy({
export function evaluateTargetBranchPolicy({
base,
head,
maxRebuildCount,
@@ -105,5 +111,3 @@ function evaluateTargetBranchPolicy({
shouldSkipDevelopmentMerge ? 'skip-development-merge' : 'dismiss',
)
}
module.exports = { evaluateTargetBranchPolicy, getTargetBranchPolicy }

View File

@@ -1,19 +1,18 @@
import { readFile } from 'node:fs/promises'
import type * as actionsCore from '@actions/core'
import type { context as actionsContext } from '@actions/github'
import type { GitHub } from '@actions/github/lib/utils'
import {
evaluateTargetBranchPolicy,
getTargetBranchPolicy,
} from './check-target-branch-policy.ts'
import { dismissReviews, postReview } from './reviews.js'
import { split } from './supportedBranches.js'
// TODO: should this be combined with the branch checks in prepare.js?
// They do seem quite similar, but this needs to run after eval,
// and prepare.js obviously doesn't.
const { split } = require('./supportedBranches.js')
const { readFile } = require('node:fs/promises')
const { postReview, dismissReviews } = require('./reviews.js')
const {
evaluateTargetBranchPolicy,
getTargetBranchPolicy,
} = require('./check-target-branch-policy.ts')
const reviewKey = 'check-target-branch'
type ChangedPaths = {
@@ -130,7 +129,7 @@ async function postPossibleMassRebuildReview(facts: TargetBranchReviewFacts) {
})
}
async function checkTargetBranch({
export async function checkTargetBranch({
github,
context,
core,
@@ -241,5 +240,3 @@ async function checkTargetBranch({
reviewKey,
})
}
module.exports = checkTargetBranch

View File

@@ -1,9 +1,10 @@
// @ts-nocheck
module.exports = async ({ github, context, core, dry, cherryPicks }) => {
const { execFileSync } = require('node:child_process')
const { classify } = require('./supportedBranches.js')
const withRateLimit = require('./withRateLimit.js')
const { dismissReviews, postReview } = require('./reviews.js')
import { execFileSync } from 'node:child_process'
import { dismissReviews, postReview } from './reviews.js'
import { classify } from './supportedBranches.js'
import withRateLimit from './withRateLimit.js'
export default async ({ github, context, core, dry, cherryPicks }) => {
const reviewKey = 'check-commits'
await withRateLimit({ github, core }, async (stats) => {

View File

@@ -1,6 +1,7 @@
// @ts-nocheck
const { promisify } = require('node:util')
const execFile = promisify(require('node:child_process').execFile)
import { execFile as nodeExecFile } from 'node:child_process'
import { promisify } from 'node:util'
const execFile = promisify(nodeExecFile)
/**
* @typedef {{
@@ -41,13 +42,13 @@ async function runGit({ args, repoPath, core, quiet }) {
*
* @param {{
* core: typeof import('@actions/core'),
* pr: Awaited<ReturnType<InstanceType<import('@actions/github/lib/utils').GitHub>["rest"]["pulls"]["get"]>>["data"]
* pr: Awaited<ReturnType<InstanceType<typeof import('@actions/github/lib/utils').GitHub>["rest"]["pulls"]["get"]>>["data"]
* repoPath?: string,
* }} GetCommitMessagesForPRProps
*
* @returns {Promise<Commit[]>}
*/
async function getCommitDetailsForPR({ core, pr, repoPath }) {
export async function getCommitDetailsForPR({ core, pr, repoPath }) {
await runGit({
args: ['fetch', `--depth=1`, 'origin', pr.base.sha],
repoPath,
@@ -113,5 +114,3 @@ async function getCommitDetailsForPR({ core, pr, repoPath }) {
}),
)
}
module.exports = { getCommitDetailsForPR }

View File

@@ -1,14 +1,14 @@
// @ts-nocheck
import { writeFileSync } from 'node:fs'
import withRateLimit from './withRateLimit.js'
const excludeTeams = [
/^voters.*$/,
/^nixpkgs-maintainers$/,
/^nixpkgs-committers$/,
]
module.exports = async ({ github, context, core, outFile }) => {
const withRateLimit = require('./withRateLimit.js')
const { writeFileSync } = require('node:fs')
export default async ({ github, context, core, outFile }) => {
const org = context.repo.owner
const result = {}

View File

@@ -1,5 +1,5 @@
const { classify } = require('./supportedBranches.js')
const { getCommitDetailsForPR } = require('./get-pr-commit-details.js')
import { getCommitDetailsForPR } from './get-pr-commit-details.js'
import { classify } from './supportedBranches.js'
/** @typedef {import('./get-pr-commit-details.js').Commit} Commit */
@@ -11,7 +11,7 @@ const { getCommitDetailsForPR } = require('./get-pr-commit-details.js')
* repoPath?: string,
* }} LintCommitsProps
*/
async function lintCommits({ github, context, core, repoPath }) {
export default async function lintCommits({ github, context, core, repoPath }) {
// This check should only be run when we have the pull_request context.
const pull_number = context.payload.pull_request?.number
if (!pull_number) {
@@ -220,5 +220,3 @@ async function checkCommitMetadata({ commits, core }) {
core.setFailed('Committers: merging is discouraged.')
}
}
module.exports = lintCommits

View File

@@ -1,5 +1,6 @@
const { classify } = require('./supportedBranches.js')
const { getCommitDetailsForPR } = require('./get-pr-commit-details')
import { getCommitDetailsForPR } from './get-pr-commit-details.js'
import { dismissReviews, postReview } from './reviews.js'
import { classify } from './supportedBranches.js'
/**
* @param {{
@@ -10,8 +11,13 @@ const { getCommitDetailsForPR } = require('./get-pr-commit-details')
* dry: boolean,
* }} CheckManualFileEditsProps
*/
async function checkManualFileEdits({ github, context, core, repoPath, dry }) {
const { dismissReviews, postReview } = require('./reviews.js')
export default async function checkManualFileEdits({
github,
context,
core,
repoPath,
dry,
}) {
const reviewKey = 'manual-file-edits'
const pull_number = context.payload.pull_request?.number
@@ -90,5 +96,3 @@ async function checkManualFileEdits({ github, context, core, repoPath, dry }) {
})
}
}
module.exports = checkManualFileEdits

View File

@@ -1,5 +1,5 @@
// @ts-nocheck
const { classify } = require('./supportedBranches.js')
import { classify } from './supportedBranches.js'
function runChecklist({
committers,
@@ -123,7 +123,7 @@ function hasMergeCommand(body) {
.match(/^@NixOS\/nixpkgs-merge-bot merge\s*$/im)
}
async function handleMergeComment({ github, body, node_id, reaction }) {
export async function handleMergeComment({ github, body, node_id, reaction }) {
if (!hasMergeCommand(body)) return
await github.graphql(
@@ -138,7 +138,7 @@ async function handleMergeComment({ github, body, node_id, reaction }) {
)
}
async function handleMerge({
export async function handleMerge({
github,
context,
core,
@@ -409,8 +409,3 @@ async function handleMerge({
// This is used to set the respective label in bot.js.
return result
}
module.exports = {
handleMerge,
handleMergeComment,
}

View File

@@ -12,7 +12,6 @@
"commander": "14.0.3"
},
"devDependencies": {
"@tsconfig/node24": "24.0.4",
"@types/node": "24.13.3",
"typescript": "7.0.2"
}
@@ -630,13 +629,6 @@
"@protobuf-ts/runtime": "^2.11.1"
}
},
"node_modules/@tsconfig/node24": {
"version": "24.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node24/-/node24-24.0.4.tgz",
"integrity": "sha512-2A933l5P5oCbv6qSxHs7ckKwobs8BDAe9SJ/Xr2Hy+nDlwmLE1GhFh/g/vXGRZWgxBg9nX/5piDtHR9Dkw/XuA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.13.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",

View File

@@ -5,6 +5,7 @@
"testsuite": "node --test",
"test": "npm run typecheck && npm run testsuite"
},
"type": "module",
"//": [
"Keep `@actions/core` and `@actions/github` in sync with",
"https://github.com/actions/github-script/blob/main/package.json."
@@ -17,7 +18,6 @@
"commander": "14.0.3"
},
"devDependencies": {
"@tsconfig/node24": "24.0.4",
"@types/node": "24.13.3",
"typescript": "7.0.2"
}

View File

@@ -1,10 +1,11 @@
// @ts-nocheck
const { classify } = require('./supportedBranches.js')
const { postReview, dismissReviews } = require('./reviews.js')
const reviewKey = 'prepare'
const supportedSystems = require('./supportedSystems.js')
import { dismissReviews, postReview } from './reviews.js'
import { classify } from './supportedBranches.js'
import supportedSystems from './supportedSystems.js'
module.exports = async ({ github, context, core, dry }) => {
const reviewKey = 'prepare'
export default async ({ github, context, core, dry }) => {
const pull_number = context.payload.pull_request.number
for (const retryInterval of [5, 10, 20, 40, 80]) {
@@ -65,7 +66,7 @@ module.exports = async ({ github, context, core, dry }) => {
// commits between that base and head is the real base. We can query for this via GitHub's
// REST API. There can be multiple candidates for the real base with the same number of
// commits. In this case we pick the "best" candidate by a fixed ordering of branches,
// as defined in ./supportedBranches.js.
// as defined in ci/github-script/supportedBranches.js.
//
// These requests take a while, when comparing against the wrong release - they need
// to look at way more than 10k commits in that case. Thus, we try to minimize the

View File

@@ -1,5 +1,5 @@
// @ts-nocheck
async function handleReviewers({
export async function handleReviewers({
github,
context,
core,
@@ -183,7 +183,3 @@ async function handleReviewers({
teams_reached.size === 0
)
}
module.exports = {
handleReviewers,
}

View File

@@ -30,7 +30,13 @@ const reviewUsers = [
* reviewKey?: string,
* }} DismissReviewsProps
*/
async function dismissReviews({ github, context, core, dry, reviewKey }) {
export async function dismissReviews({
github,
context,
core,
dry,
reviewKey,
}) {
const pull_number = context.payload.pull_request?.number
if (!pull_number) {
core.warning('dismissReviews called outside of pull_request context')
@@ -170,7 +176,7 @@ async function dismissReviews({ github, context, core, dry, reviewKey }) {
* reviewKey: string,
* }} PostReviewProps
*/
async function postReview({
export async function postReview({
github,
context,
core,
@@ -262,8 +268,3 @@ async function postReview({
}
}
}
module.exports = {
dismissReviews,
postReview,
}

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env -S node --import ./run
#!/usr/bin/env node
import { execSync } from 'node:child_process'
import { closeSync, mkdtempSync, openSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
@@ -112,7 +112,7 @@ program
.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 checkTargetBranch = (await import('./check-target-branch.ts')).default
const checkTargetBranch = (await import('./check-target-branch.ts')).checkTargetBranch
await run(checkTargetBranch, owner, repo, pr, options)
})

View File

@@ -3,6 +3,8 @@
#!nix-shell -i node -p nodejs
*/
// @ts-nocheck
import { resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
const typeConfig = {
master: ['development', 'primary'],
@@ -45,10 +47,13 @@ function classify(branch) {
}
}
module.exports = { classify, split }
export { classify, split }
// If called directly via CLI, runs the following tests:
if (!module.parent) {
if (
process.argv[1] &&
fileURLToPath(import.meta.url) === resolve(process.argv[1])
) {
console.log('split(branch)')
function testSplit(branch) {
console.log(branch, split(branch))

View File

@@ -1,5 +1,5 @@
// @ts-nocheck
module.exports = async ({ github, context, targetSha }) => {
export default async ({ github, context, targetSha }) => {
const { content, encoding } = (
await github.rest.repos.getContent({
...context.repo,

View File

@@ -14,7 +14,7 @@
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"moduleResolution": "node16",
"moduleResolution": "nodenext",
"allowImportingTsExtensions": true,
"allowJs": true,

View File

@@ -1,7 +1,7 @@
// @ts-nocheck
module.exports = async ({ github, core, maxConcurrent = 1 }, callback) => {
const Bottleneck = require('bottleneck')
import Bottleneck from 'bottleneck'
export default async ({ github, core, maxConcurrent = 1 }, callback) => {
const stats = {
issues: 0,
prs: 0,