From 83d04aa23219d8cb892136d8a377a6ea593e3067 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Fri, 24 Jul 2026 01:06:35 +0200 Subject: [PATCH] fix: make GitHub Actions checks opt-in for outdated and update (#13259) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checking GitHub Actions for updates spawns `git ls-remote` against every repository referenced by the workflow files. That reaches the network in a way pnpm cannot validate up front, and it fails wherever GitHub is not reachable the way pnpm assumes: a GitHub Enterprise Server hosting the actions (pnpm/pnpm#13220), a container with a custom certificate authority (pnpm/pnpm#13254), an offline or proxied CI network. Both reports came within days of the feature shipping, and neither is fixable from pnpm's side, so the check no longer runs unless it is asked for. `pnpm outdated` and `pnpm update` — interactive or not, recursive or not — now read workflow files only with `--include-github-actions` or with `update.githubActions` set to `true`. `update.githubActions: false` keeps working; it is now equivalent to leaving the setting unset, so anyone who added it to silence the warnings needs no change. The opt-in rule lives in one place per stack (`shouldCheckGitHubActions` and `github_actions::opted_in`) and is shared by every call site, so the commands cannot drift apart again. `pnpm outdated` gained the `--include-github-actions` flag it was missing. Closes pnpm/pnpm#13254 --- ...nteractive-update-github-actions-opt-in.md | 12 +++ pnpm/crates/cli/src/cli_args/outdated.rs | 8 +- pnpm/crates/cli/src/cli_args/update.rs | 4 +- pnpm/crates/cli/src/cli_args/update/tests.rs | 13 ++- pnpm/crates/cli/src/github_actions.rs | 13 ++- pnpm/crates/cli/src/github_actions/tests.rs | 17 +++- pnpm/crates/config/src/workspace_yaml.rs | 16 ++-- pnpm11/core/types/src/package.ts | 6 +- pnpm11/deps/github-actions/src/index.ts | 15 ++++ .../commands/src/outdated/outdated.ts | 12 ++- .../commands/src/outdated/recursive.ts | 4 +- .../commands/test/outdated/githubActions.ts | 86 +++++++++++++++++++ .../installing/commands/src/update/index.ts | 24 +++--- .../commands/test/update/githubActions.ts | 61 +++++++++++++ 14 files changed, 251 insertions(+), 40 deletions(-) create mode 100644 .changeset/interactive-update-github-actions-opt-in.md create mode 100644 pnpm11/deps/inspection/commands/test/outdated/githubActions.ts create mode 100644 pnpm11/installing/commands/test/update/githubActions.ts diff --git a/.changeset/interactive-update-github-actions-opt-in.md b/.changeset/interactive-update-github-actions-opt-in.md new file mode 100644 index 0000000000..d7024fab28 --- /dev/null +++ b/.changeset/interactive-update-github-actions-opt-in.md @@ -0,0 +1,12 @@ +--- +"@pnpm/deps.github-actions": patch +"@pnpm/deps.inspection.commands": patch +"@pnpm/installing.commands": patch +"@pnpm/types": patch +"pacquet": patch +"pnpm": patch +--- + +Checking GitHub Actions dependencies for updates is now opt-in for every command. Neither `pnpm outdated` nor `pnpm update` reads the workflow files unless `--include-github-actions` is passed or `update.githubActions` is set to `true` in `pnpm-workspace.yaml`. Reading them runs `git ls-remote` against every referenced repository, which fails in environments where GitHub is not reachable the way pnpm assumes (a GitHub Enterprise Server, a custom certificate authority, or an offline network) [#13254](https://github.com/pnpm/pnpm/issues/13254). + +`pnpm outdated` accepts the `--include-github-actions` option too. diff --git a/pnpm/crates/cli/src/cli_args/outdated.rs b/pnpm/crates/cli/src/cli_args/outdated.rs index a2f758d682..c1add826e8 100644 --- a/pnpm/crates/cli/src/cli_args/outdated.rs +++ b/pnpm/crates/cli/src/cli_args/outdated.rs @@ -384,6 +384,10 @@ pub struct OutdatedArgs { #[clap(long, value_enum)] pub sort_by: Option, + /// Also check GitHub Actions dependencies in workflow and action files. + #[clap(long = "include-github-actions")] + pub include_github_actions: bool, + /// Check globally installed packages. #[clap(short = 'g', long)] pub global: bool, @@ -485,7 +489,7 @@ impl OutdatedArgs { Vec::new() }; if include.contains(&DependencyGroup::Dev) - && config.update_config.github_actions != Some(false) + && github_actions::opted_in(self.include_github_actions, config) { let actions = github_actions::find_outdated::( root, @@ -622,7 +626,7 @@ impl OutdatedArgs { } if include.contains(&DependencyGroup::Dev) - && config.update_config.github_actions != Some(false) + && github_actions::opted_in(self.include_github_actions, config) { let action_matcher = github_actions::selector_matcher(&self.packages); let actions = github_actions::find_outdated::( diff --git a/pnpm/crates/cli/src/cli_args/update.rs b/pnpm/crates/cli/src/cli_args/update.rs index bd38c365f5..5354d900a6 100644 --- a/pnpm/crates/cli/src/cli_args/update.rs +++ b/pnpm/crates/cli/src/cli_args/update.rs @@ -395,9 +395,7 @@ impl UpdateArgs { include_direct.contains(&DependencyGroup::Dev) && !self.no_save && !self.lockfile_only - && ((self.interactive && config.update_config.github_actions != Some(false)) - || self.include_github_actions - || config.update_config.github_actions == Some(true)) + && github_actions::opted_in(self.include_github_actions, config) } } diff --git a/pnpm/crates/cli/src/cli_args/update/tests.rs b/pnpm/crates/cli/src/cli_args/update/tests.rs index 39e5128db9..2af649f48b 100644 --- a/pnpm/crates/cli/src/cli_args/update/tests.rs +++ b/pnpm/crates/cli/src/cli_args/update/tests.rs @@ -56,25 +56,30 @@ fn prod_with_no_optional_drops_optional() { } #[test] -fn github_actions_are_opt_in_except_for_interactive_updates() { +fn github_actions_are_opt_in_for_every_update() { let include_direct = vec![DependencyGroup::Prod, DependencyGroup::Dev]; let mut config = Config::new(); assert!(!update_args(&[]).should_update_github_actions(&config, &include_direct)); + assert!( + !update_args(&["--interactive"]).should_update_github_actions(&config, &include_direct), + ); assert!( update_args(&["--include-github-actions"]) .should_update_github_actions(&config, &include_direct), ); - assert!(update_args(&["--interactive"]).should_update_github_actions(&config, &include_direct)); + assert!( + update_args(&["--interactive", "--include-github-actions"]) + .should_update_github_actions(&config, &include_direct), + ); config.update_config.github_actions = Some(true); assert!(update_args(&[]).should_update_github_actions(&config, &include_direct)); + assert!(update_args(&["--interactive"]).should_update_github_actions(&config, &include_direct)); assert!( !update_args(&["--prod"]).should_update_github_actions(&config, &[DependencyGroup::Prod],), ); - // An explicit `false` opts interactive updates out of GitHub Actions, - // but never overrides the explicit `--include-github-actions` flag. config.update_config.github_actions = Some(false); assert!( !update_args(&["--interactive"]).should_update_github_actions(&config, &include_direct), diff --git a/pnpm/crates/cli/src/github_actions.rs b/pnpm/crates/cli/src/github_actions.rs index 246c6923d9..9cea0e9688 100644 --- a/pnpm/crates/cli/src/github_actions.rs +++ b/pnpm/crates/cli/src/github_actions.rs @@ -1,6 +1,9 @@ use futures_util::{StreamExt, stream}; use node_semver::{Range as SemverRange, Version}; -use pacquet_config::matcher::{Matcher, create_matcher}; +use pacquet_config::{ + Config, + matcher::{Matcher, create_matcher}, +}; use pacquet_network::redact_and_sanitize; use pacquet_reporter::{GlobalLog, LogEvent, LogLevel, Reporter}; use pacquet_resolving_git_resolver::{GitCommandRunner, RealGitRunner, get_repo_refs}; @@ -73,6 +76,14 @@ struct PlannedUpdate { const GIT_CONCURRENCY: usize = 8; +/// GitHub Actions dependencies are opt-in. Reading them means running `git +/// ls-remote` against every referenced repository, so `pnpm outdated` and +/// `pnpm update` only look at workflow files when asked to, either with +/// `--include-github-actions` or with `update.githubActions: true`. +pub fn opted_in(include_github_actions: bool, config: &Config) -> bool { + include_github_actions || config.update_config.github_actions == Some(true) +} + pub async fn find_outdated( root: &Path, compatible: bool, diff --git a/pnpm/crates/cli/src/github_actions/tests.rs b/pnpm/crates/cli/src/github_actions/tests.rs index 2391a0b9ad..edb8732452 100644 --- a/pnpm/crates/cli/src/github_actions/tests.rs +++ b/pnpm/crates/cli/src/github_actions/tests.rs @@ -1,9 +1,10 @@ use super::{ ActionReference, RepoVersion, find_current, find_outdated_with_runner, is_selector, - normalize_selector, render_target_ref, render_target_value, + normalize_selector, opted_in, render_target_ref, render_target_value, repo_versions as versions_from_refs, selector_matcher, split_uses_value, update_with_runner, }; use node_semver::Version; +use pacquet_config::Config; use pacquet_reporter::{GlobalLog, LogEvent, LogLevel, Reporter, SilentReporter}; use pacquet_resolving_git_resolver::{GitCommandRunner, GitRunError}; use std::{collections::HashMap, fs, future::Future, path::PathBuf, pin::Pin, sync::Mutex}; @@ -77,6 +78,20 @@ impl GitCommandRunner for PreOneGitRunner { } } +#[test] +fn workflow_files_are_read_only_when_opted_in() { + let mut config = Config::new(); + assert!(!opted_in(false, &config)); + assert!(opted_in(true, &config)); + + config.update_config.github_actions = Some(false); + assert!(!opted_in(false, &config)); + assert!(opted_in(true, &config)); + + config.update_config.github_actions = Some(true); + assert!(opted_in(false, &config)); +} + #[test] fn distinguishes_action_selectors_from_package_selectors() { assert_eq!( diff --git a/pnpm/crates/config/src/workspace_yaml.rs b/pnpm/crates/config/src/workspace_yaml.rs index af6b8767d2..e052c47f80 100644 --- a/pnpm/crates/config/src/workspace_yaml.rs +++ b/pnpm/crates/config/src/workspace_yaml.rs @@ -537,10 +537,10 @@ pub struct UpdateSettings { #[serde(skip_serializing_if = "Option::is_none")] pub changeset: Option, - /// Whether `pnpm update` should also update GitHub Actions - /// dependencies. When explicitly set to `false`, `pnpm outdated` - /// and the interactive `pnpm update` skip GitHub Actions - /// dependencies as well. + /// Whether `pnpm outdated` and `pnpm update` should also look at + /// the GitHub Actions referenced by the workflow files. Opt-in: + /// neither command reads them unless this is set to `true` or + /// `--include-github-actions` is passed. #[serde(skip_serializing_if = "Option::is_none")] pub github_actions: Option, @@ -569,10 +569,10 @@ pub struct UpdateConfig { #[serde(skip_serializing_if = "Option::is_none")] pub ignore_dependencies: Option>, - /// Whether `pnpm update` should also update GitHub Actions - /// dependencies. When explicitly set to `false`, `pnpm outdated` - /// and the interactive `pnpm update` skip GitHub Actions - /// dependencies as well. + /// Whether `pnpm outdated` and `pnpm update` should also look at + /// the GitHub Actions referenced by the workflow files. Opt-in: + /// neither command reads them unless this is set to `true` or + /// `--include-github-actions` is passed. #[serde(skip_serializing_if = "Option::is_none")] pub github_actions: Option, diff --git a/pnpm11/core/types/src/package.ts b/pnpm11/core/types/src/package.ts index 20fc9b162f..3f8fd04ec5 100644 --- a/pnpm11/core/types/src/package.ts +++ b/pnpm11/core/types/src/package.ts @@ -208,9 +208,9 @@ export interface UpdateSettings { */ changeset?: boolean /** - * Whether `pnpm update` should also update GitHub Actions dependencies. - * When explicitly set to `false`, `pnpm outdated` and the interactive - * `pnpm update` skip GitHub Actions dependencies as well. + * Whether `pnpm outdated` and `pnpm update` should also look at the GitHub + * Actions referenced by the workflow files. Opt-in: neither command reads + * them unless this is set to `true` or `--include-github-actions` is passed. */ githubActions?: boolean /** diff --git a/pnpm11/deps/github-actions/src/index.ts b/pnpm11/deps/github-actions/src/index.ts index f1316809aa..a4d6c7dc26 100644 --- a/pnpm11/deps/github-actions/src/index.ts +++ b/pnpm11/deps/github-actions/src/index.ts @@ -72,6 +72,21 @@ interface PlannedUpdate { const SHA_PATTERN = /^[0-9a-f]{40}$/ const limitRepoReads = pLimit(8) +export interface GitHubActionsOptInOptions { + includeGithubActions?: boolean + updateConfig?: { githubActions?: boolean } +} + +/** + * GitHub Actions dependencies are opt-in. Reading them means running + * `git ls-remote` against every referenced repository, so `pnpm outdated` and + * `pnpm update` only look at workflow files when asked to, either with + * `--include-github-actions` or with `update.githubActions: true`. + */ +export function shouldCheckGitHubActions (opts: GitHubActionsOptInOptions): boolean { + return opts.includeGithubActions === true || opts.updateConfig?.githubActions === true +} + export function isGitHubActionSelector (selector: string): boolean { const pattern = selector.startsWith('!') ? selector.slice(1) : selector return !pattern.startsWith('@') && pattern.includes('/') diff --git a/pnpm11/deps/inspection/commands/src/outdated/outdated.ts b/pnpm11/deps/inspection/commands/src/outdated/outdated.ts index 616ebd3861..e87aaf4a8c 100644 --- a/pnpm11/deps/inspection/commands/src/outdated/outdated.ts +++ b/pnpm11/deps/inspection/commands/src/outdated/outdated.ts @@ -11,7 +11,7 @@ import { import { colorizeSemverDiff } from '@pnpm/colorize-semver-diff' import { createMatcher } from '@pnpm/config.matcher' import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader' -import { findOutdatedGitHubActions, isGitHubActionSelector, normalizeGitHubActionSelector } from '@pnpm/deps.github-actions' +import { findOutdatedGitHubActions, isGitHubActionSelector, normalizeGitHubActionSelector, shouldCheckGitHubActions } from '@pnpm/deps.github-actions' import { outdatedDepsOfProjects, type OutdatedPackage, @@ -51,6 +51,7 @@ export function rcOptionsTypes (): Record { export const cliOptionsTypes = (): Record => ({ ...rcOptionsTypes(), + 'include-github-actions': Boolean, recursive: Boolean, }) @@ -66,7 +67,7 @@ export const commandNames = ['outdated'] export function help (): string { return renderHelp({ - description: `Check for outdated package and GitHub Actions dependencies. The check can be limited to a subset of dependencies by providing arguments (patterns are supported). + description: `Check for outdated package dependencies. GitHub Actions dependencies can be included with --include-github-actions. The check can be limited to a subset of dependencies by providing arguments (patterns are supported). Examples: pnpm outdated @@ -115,6 +116,10 @@ For options that may be used with `-r`, see "pnpm help recursive"', description: 'Prints the outdated dependencies in the given format. Default is "table". Supported options: "table, list, json"', name: '--format ', }, + { + description: 'Also check GitHub Actions dependencies in workflow and action files', + name: '--include-github-actions', + }, { description: 'Specify the sorting method. Currently only `name` is supported.', name: '--sort-by', @@ -136,6 +141,7 @@ export const completion: CompletionFunc = async (cliOpts) => { export type OutdatedCommandOptions = { compatible?: boolean + includeGithubActions?: boolean long?: boolean recursive?: boolean format?: 'table' | 'list' | 'json' @@ -228,7 +234,7 @@ export async function handler ( timeout: opts.fetchTimeout, }) : [], - opts.global || !include.devDependencies || opts.updateConfig?.githubActions === false + opts.global || !include.devDependencies || !shouldCheckGitHubActions(opts) ? [] : findOutdatedGitHubActions({ compatible: opts.compatible, diff --git a/pnpm11/deps/inspection/commands/src/outdated/recursive.ts b/pnpm11/deps/inspection/commands/src/outdated/recursive.ts index b429d1ff5d..33832e1420 100644 --- a/pnpm11/deps/inspection/commands/src/outdated/recursive.ts +++ b/pnpm11/deps/inspection/commands/src/outdated/recursive.ts @@ -1,6 +1,6 @@ import { TABLE_OPTIONS } from '@pnpm/cli.utils' import { createMatcher } from '@pnpm/config.matcher' -import { findOutdatedGitHubActions, isGitHubActionSelector, normalizeGitHubActionSelector } from '@pnpm/deps.github-actions' +import { findOutdatedGitHubActions, isGitHubActionSelector, normalizeGitHubActionSelector, shouldCheckGitHubActions } from '@pnpm/deps.github-actions' import { outdatedDepsOfProjects, } from '@pnpm/deps.inspection.outdated' @@ -83,7 +83,7 @@ export async function outdatedRecursive ( outdatedMap[key].dependentPkgs.push({ location: rootDir, manifest }) } } - if (opts.include.devDependencies && opts.updateConfig?.githubActions !== false) { + if (opts.include.devDependencies && shouldCheckGitHubActions(opts)) { const outdatedActions = await findOutdatedGitHubActions({ compatible: opts.compatible, dir: opts.workspaceDir ?? opts.lockfileDir ?? opts.dir, diff --git a/pnpm11/deps/inspection/commands/test/outdated/githubActions.ts b/pnpm11/deps/inspection/commands/test/outdated/githubActions.ts new file mode 100644 index 0000000000..60579325be --- /dev/null +++ b/pnpm11/deps/inspection/commands/test/outdated/githubActions.ts @@ -0,0 +1,86 @@ +import fs from 'node:fs' +import path from 'node:path' + +import { beforeEach, expect, jest, test } from '@jest/globals' +import { prepare, preparePackages } from '@pnpm/prepare' +import { filterProjectsBySelectorObjectsFromDir } from '@pnpm/workspace.projects-filter' + +import { DEFAULT_OUTDATED_OPTS } from './utils/index.js' + +const originalModule = await import('@pnpm/deps.github-actions') +jest.unstable_mockModule('@pnpm/deps.github-actions', () => { + return { + ...originalModule, + findOutdatedGitHubActions: jest.fn(async () => []), + } +}) + +const { findOutdatedGitHubActions } = await import('@pnpm/deps.github-actions') +const { handler } = await import('../../src/outdated/outdated.js') + +beforeEach(() => { + jest.mocked(findOutdatedGitHubActions).mockClear() +}) + +test('outdated does not look at GitHub Actions by default', async () => { + prepare({}) + writeWorkflow() + + await handler({ ...DEFAULT_OUTDATED_OPTS, dir: process.cwd() }) + + expect(findOutdatedGitHubActions).not.toHaveBeenCalled() +}) + +test('outdated looks at GitHub Actions with --include-github-actions', async () => { + prepare({}) + writeWorkflow() + + await handler({ ...DEFAULT_OUTDATED_OPTS, dir: process.cwd(), includeGithubActions: true }) + + expect(findOutdatedGitHubActions).toHaveBeenCalledWith(expect.objectContaining({ dir: process.cwd() })) +}) + +test('outdated looks at GitHub Actions when update.githubActions is true', async () => { + prepare({}) + writeWorkflow() + + await handler({ ...DEFAULT_OUTDATED_OPTS, dir: process.cwd(), updateConfig: { githubActions: true } }) + + expect(findOutdatedGitHubActions).toHaveBeenCalledWith(expect.objectContaining({ dir: process.cwd() })) +}) + +test('recursive outdated does not look at GitHub Actions by default', async () => { + preparePackages([ + { + name: 'project-1', + version: '1.0.0', + }, + ]) + writeWorkflow() + const { allProjects, selectedProjectsGraph } = await filterProjectsBySelectorObjectsFromDir(process.cwd(), []) + const opts = { + ...DEFAULT_OUTDATED_OPTS, + allProjects, + dir: process.cwd(), + recursive: true, + selectedProjectsGraph, + workspaceDir: process.cwd(), + } + + await handler(opts) + + expect(findOutdatedGitHubActions).not.toHaveBeenCalled() + + await handler({ ...opts, includeGithubActions: true }) + + expect(findOutdatedGitHubActions).toHaveBeenCalledWith(expect.objectContaining({ dir: process.cwd() })) +}) + +function writeWorkflow (): void { + fs.mkdirSync(path.join('.github', 'workflows'), { recursive: true }) + fs.writeFileSync(path.join('.github', 'workflows', 'ci.yml'), `jobs: + test: + steps: + - uses: actions/checkout@v4.1.0 +`) +} diff --git a/pnpm11/installing/commands/src/update/index.ts b/pnpm11/installing/commands/src/update/index.ts index e15cf084fa..544550d439 100644 --- a/pnpm11/installing/commands/src/update/index.ts +++ b/pnpm11/installing/commands/src/update/index.ts @@ -9,7 +9,7 @@ import { } from '@pnpm/cli.utils' import { createMatcher } from '@pnpm/config.matcher' import { types as allTypes } from '@pnpm/config.reader' -import { findOutdatedGitHubActions, isGitHubActionSelector, normalizeGitHubActionSelector, updateGitHubActions } from '@pnpm/deps.github-actions' +import { findOutdatedGitHubActions, isGitHubActionSelector, normalizeGitHubActionSelector, shouldCheckGitHubActions, updateGitHubActions } from '@pnpm/deps.github-actions' import { outdatedDepsOfProjects } from '@pnpm/deps.inspection.outdated' import { PnpmError } from '@pnpm/error' import { handleGlobalUpdate } from '@pnpm/global.commands' @@ -243,8 +243,7 @@ async function interactiveUpdate ( timeout: opts.fetchTimeout, }) : projects.map(() => []), - include.devDependencies && opts.save !== false && !opts.lockfileOnly && - (opts.updateConfig?.githubActions !== false || opts.includeGithubActions === true) + shouldUpdateGitHubActions(opts, include) ? findOutdatedGitHubActions({ compatible: opts.latest !== true, dir: opts.workspaceDir ?? opts.lockfileDir ?? opts.dir, @@ -326,12 +325,7 @@ async function interactiveUpdate ( throw err } - // An explicit `update.githubActions: false` must survive into the update - // phase — only the `--include-github-actions` flag may override it. - return update(updatePkgNames, { - ...opts, - includeGithubActions: opts.includeGithubActions === true || opts.updateConfig?.githubActions !== false, - }, rebuildHandler) as Promise + return update(updatePkgNames, opts, rebuildHandler) as Promise } async function update ( @@ -340,10 +334,7 @@ async function update ( rebuildHandler?: CommandHandler ): Promise { const includeDirect = makeIncludeDependenciesFromCLI(opts.cliOptions) - const updateActions = includeDirect.devDependencies && - opts.save !== false && - !opts.lockfileOnly && - (opts.includeGithubActions === true || opts.updateConfig?.githubActions === true) + const updateActions = shouldUpdateGitHubActions(opts, includeDirect) if (opts.latest) { const dependenciesWithTags = dependencies.filter((name) => (!updateActions || !isGitHubActionSelector(name)) && parseUpdateParam(name).versionSpec != null) @@ -407,6 +398,13 @@ async function update ( } } +function shouldUpdateGitHubActions (opts: UpdateCommandOptions, include: IncludedDependencies): boolean { + return include.devDependencies && + opts.save !== false && + !opts.lockfileOnly && + shouldCheckGitHubActions(opts) +} + function makeIncludeDependenciesFromCLI (opts: { production?: boolean dev?: boolean diff --git a/pnpm11/installing/commands/test/update/githubActions.ts b/pnpm11/installing/commands/test/update/githubActions.ts new file mode 100644 index 0000000000..2bc72d9ab5 --- /dev/null +++ b/pnpm11/installing/commands/test/update/githubActions.ts @@ -0,0 +1,61 @@ +import fs from 'node:fs' +import path from 'node:path' + +import { beforeEach, expect, jest, test } from '@jest/globals' +import { prepare } from '@pnpm/prepare' + +import { DEFAULT_OPTS } from '../utils/index.js' + +const originalModule = await import('@pnpm/deps.github-actions') +jest.unstable_mockModule('@pnpm/deps.github-actions', () => { + return { + ...originalModule, + findOutdatedGitHubActions: jest.fn(async () => []), + } +}) + +const { findOutdatedGitHubActions } = await import('@pnpm/deps.github-actions') +const { handler } = await import('../../src/update/index.js') + +beforeEach(() => { + jest.mocked(findOutdatedGitHubActions).mockClear() + prepare({}) + fs.mkdirSync(path.join('.github', 'workflows'), { recursive: true }) + fs.writeFileSync(path.join('.github', 'workflows', 'ci.yml'), `jobs: + test: + steps: + - uses: actions/checkout@v4.1.0 +`) +}) + +test('update --interactive does not look for GitHub Actions updates by default', async () => { + await handler({ + ...DEFAULT_OPTS, + dir: process.cwd(), + interactive: true, + }) + + expect(findOutdatedGitHubActions).not.toHaveBeenCalled() +}) + +test('update --interactive looks for GitHub Actions updates with --include-github-actions', async () => { + await handler({ + ...DEFAULT_OPTS, + dir: process.cwd(), + includeGithubActions: true, + interactive: true, + }) + + expect(findOutdatedGitHubActions).toHaveBeenCalledWith(expect.objectContaining({ dir: process.cwd() })) +}) + +test('update --interactive looks for GitHub Actions updates when update.githubActions is true', async () => { + await handler({ + ...DEFAULT_OPTS, + dir: process.cwd(), + interactive: true, + updateConfig: { githubActions: true }, + }) + + expect(findOutdatedGitHubActions).toHaveBeenCalledWith(expect.objectContaining({ dir: process.cwd() })) +})