From cf1f510d7742e68be1718a4034225eeb14da171b Mon Sep 17 00:00:00 2001 From: Mason Daugherty Date: Wed, 18 Mar 2026 00:53:18 -0400 Subject: [PATCH] fix(infra): resolve module crash blocking external PR enforcement (#36059) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `pr-labeler.js` used `require('@actions/core')` to access GitHub Actions logging/failure helpers, but that module is bundled inside `actions/github-script`'s dist — it's not resolvable via Node's `require()` from a checked-out file on disk. Two of the three call sites were in rarely-hit error branches, so the bug was latent. The third (`applyTierLabel`) ran unconditionally, crashing the tier-label step on every external PR. Because the tier step runs *before* the "add external label" step, the crash prevented the `external` label from ever being applied — which meant `require_issue_link.yml` never triggered and unapproved external PRs stayed open. ## Changes - Thread the `core` object (provided by `actions/github-script` at eval time) through `loadAndInit()` → `init()` instead of calling `require('@actions/core')` from the checked-out script — fixes the `MODULE_NOT_FOUND` crash on all three call sites (`ensureLabel`, `getContributorInfo`, `applyTierLabel`) - Add a console-based fallback in `loadAndInit` so callers that don't need `core.setFailed` still work without passing it - Update all 9 `loadAndInit(github, owner, repo)` call sites across `pr_labeler.yml`, `pr_labeler_backfill.yml`, and `tag-external-issues.yml` to pass `core` --- .github/scripts/pr-labeler.js | 14 +++++++------- .github/workflows/pr_labeler.yml | 8 ++++---- .github/workflows/pr_labeler_backfill.yml | 2 +- .github/workflows/tag-external-issues.yml | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/scripts/pr-labeler.js b/.github/scripts/pr-labeler.js index bc0639bffb7..ac2ff31ba70 100644 --- a/.github/scripts/pr-labeler.js +++ b/.github/scripts/pr-labeler.js @@ -1,7 +1,7 @@ // Shared helpers for pr_labeler.yml and tag-external-issues.yml. // // Usage from actions/github-script (requires actions/checkout first): -// const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo); +// const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); const fs = require('fs'); const path = require('path'); @@ -32,7 +32,10 @@ function loadConfig() { return config; } -function init(github, owner, repo, config) { +function init(github, owner, repo, config, core) { + if (!core) { + throw new Error('init() requires a `core` parameter (e.g., from actions/github-script)'); + } const { trustedThreshold, labelColor, @@ -60,7 +63,6 @@ function init(github, owner, repo, config) { } catch (createErr) { // 422 = label created by a concurrent run between our get and create if (createErr.status !== 422) throw createErr; - const core = require('@actions/core'); core.info(`Label "${name}" creation returned 422 (likely already exists)`); } } @@ -196,7 +198,6 @@ function init(github, owner, repo, config) { mergedCount = result?.data?.total_count ?? null; } catch (e) { if (e?.status !== 422) throw e; - const core = require('@actions/core'); core.warning(`Search failed for ${author}; skipping tier.`); } } @@ -209,7 +210,6 @@ function init(github, owner, repo, config) { // ── Tier label resolution ─────────────────────────────────────────── async function applyTierLabel(issueNumber, author, { skipNewContributor = false } = {}) { - const core = require('@actions/core'); let mergedCount; try { const result = await github.rest.search.issuesAndPullRequests({ @@ -263,9 +263,9 @@ function init(github, owner, repo, config) { }; } -function loadAndInit(github, owner, repo) { +function loadAndInit(github, owner, repo, core) { const config = loadConfig(); - return { config, h: init(github, owner, repo, config) }; + return { config, h: init(github, owner, repo, config, core) }; } module.exports = { loadConfig, init, loadAndInit }; diff --git a/.github/workflows/pr_labeler.yml b/.github/workflows/pr_labeler.yml index 26127e16271..50edec08cc9 100644 --- a/.github/workflows/pr_labeler.yml +++ b/.github/workflows/pr_labeler.yml @@ -76,7 +76,7 @@ jobs: github-token: ${{ steps.app-token.outputs.token }} script: | const { owner, repo } = context.repo; - const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo); + const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); const author = context.payload.sender.login; const { isExternal } = await h.checkMembership( @@ -92,7 +92,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { owner, repo } = context.repo; - const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo); + const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); const pr = context.payload.pull_request; if (!pr) return; @@ -184,7 +184,7 @@ jobs: github-token: ${{ steps.app-token.outputs.token }} script: | const { owner, repo } = context.repo; - const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo); + const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); const pr = context.payload.pull_request; await h.applyTierLabel(pr.number, pr.user.login); @@ -201,7 +201,7 @@ jobs: const { owner, repo } = context.repo; const prNumber = context.payload.pull_request.number; - const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo); + const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); await h.ensureLabel('external'); await github.rest.issues.addLabels({ diff --git a/.github/workflows/pr_labeler_backfill.yml b/.github/workflows/pr_labeler_backfill.yml index 4cebfde8628..ba75f58257e 100644 --- a/.github/workflows/pr_labeler_backfill.yml +++ b/.github/workflows/pr_labeler_backfill.yml @@ -48,7 +48,7 @@ jobs: return; } - const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo); + const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); for (const name of [...h.sizeLabels, ...h.tierLabels]) { await h.ensureLabel(name); diff --git a/.github/workflows/tag-external-issues.yml b/.github/workflows/tag-external-issues.yml index 7c3e615d3a2..fe32849d539 100644 --- a/.github/workflows/tag-external-issues.yml +++ b/.github/workflows/tag-external-issues.yml @@ -68,7 +68,7 @@ jobs: github-token: ${{ steps.app-token.outputs.token }} script: | const { owner, repo } = context.repo; - const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo); + const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); const author = context.payload.sender.login; const { isExternal } = await h.checkMembership( @@ -86,7 +86,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { owner, repo } = context.repo; - const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo); + const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); const issue = context.payload.issue; // new-contributor is only meaningful on PRs, not issues @@ -101,7 +101,7 @@ jobs: const { owner, repo } = context.repo; const issue_number = context.payload.issue.number; - const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo); + const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); const label = '${{ steps.check-membership.outputs.is-external }}' === 'true' ? 'external' : 'internal'; @@ -141,7 +141,7 @@ jobs: return; } - const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo); + const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); const tierLabels = ['trusted-contributor']; for (const name of tierLabels) {