feat: add a new setting for telling pnpm if the env is CI (#9616)

This is an addition to https://github.com/pnpm/pnpm/pull/8190. The global virtual store isn't a good choice for CI. So, we disable it even if the setting sets `enableGlobalVirtualStore` to `true`.
This commit is contained in:
Zoltan Kochan
2025-06-08 15:00:22 +02:00
committed by GitHub
parent 6489c871f4
commit b217bbb4a7
19 changed files with 45 additions and 22 deletions

View File

@@ -0,0 +1,9 @@
---
"@pnpm/plugin-commands-installation": minor
"@pnpm/get-context": minor
"@pnpm/core": minor
"@pnpm/config": minor
"pnpm": minor
---
Added a new setting called `ci` for explicitly telling pnpm if the current environment is a CI or not.

View File

@@ -49,6 +49,7 @@
"camelcase": "catalog:",
"camelcase-keys": "catalog:",
"can-write-to-dir": "catalog:",
"ci-info": "catalog:",
"is-subdir": "catalog:",
"is-windows": "catalog:",
"lodash.kebabcase": "catalog:",

View File

@@ -223,6 +223,7 @@ export interface Config extends OptionsFromRootManifest {
initPackageManager: boolean
initType: 'commonjs' | 'module'
dangerouslyAllowAllBuilds: boolean
ci: boolean
}
export interface ConfigWithDeprecatedSettings extends Config {

View File

@@ -1,6 +1,7 @@
import path from 'path'
import fs from 'fs'
import os from 'os'
import { isCI } from 'ci-info'
import { getCatalogsFromWorkspaceManifest } from '@pnpm/catalogs.config'
import { LAYOUT_VERSION } from '@pnpm/constants'
import { PnpmError } from '@pnpm/error'
@@ -122,6 +123,7 @@ export async function getConfig (opts: {
'auto-install-peers': true,
bail: true,
'catalog-mode': 'manual',
ci: isCI,
color: 'auto',
'dangerously-allow-all-builds': false,
'deploy-all-files': false,
@@ -531,6 +533,11 @@ export async function getConfig (opts: {
}
pnpmConfig.neverBuiltDependencies = []
}
if (pnpmConfig.ci) {
// Using a global virtual store in CI makes little sense,
// as there is never a warm cache in that environment.
pnpmConfig.enableGlobalVirtualStore = false
}
transformPathKeys(pnpmConfig, os.homedir())

View File

@@ -3,6 +3,7 @@ import npmTypes from '@pnpm/npm-conf/lib/types'
export const types = Object.assign({
'auto-install-peers': Boolean,
bail: Boolean,
ci: Boolean,
'cache-dir': String,
'catalog-mode': ['strict', 'prefer', 'manual'],
'child-concurrency': Number,

View File

@@ -104,7 +104,6 @@
"@pnpm/symlink-dependency": "workspace:*",
"@pnpm/types": "workspace:*",
"@zkochan/rimraf": "catalog:",
"ci-info": "catalog:",
"enquirer": "catalog:",
"is-inner-link": "catalog:",
"is-subdir": "catalog:",

View File

@@ -163,6 +163,7 @@ export interface StrictInstallOptions {
prepareExecutionEnv?: PrepareExecutionEnv
returnListOfDepsRequiringBuild?: boolean
injectWorkspacePackages?: boolean
ci?: boolean
}
export type InstallOptions =

View File

@@ -88,7 +88,6 @@ import {
import { linkPackages } from './link'
import { reportPeerDependencyIssues } from './reportPeerDependencyIssues'
import { validateModules } from './validateModules'
import { isCI } from 'ci-info'
import semver from 'semver'
import { CatalogVersionMismatchError } from './checkCompatibility/CatalogVersionMismatchError'
@@ -292,7 +291,7 @@ export async function mutateModules (
storeDir: opts.storeDir,
virtualStoreDir: ctx.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
confirmModulesPurge: opts.confirmModulesPurge && !isCI,
confirmModulesPurge: opts.confirmModulesPurge && !opts.ci,
forceHoistPattern: opts.forceHoistPattern,
hoistPattern: opts.hoistPattern,

View File

@@ -38,7 +38,6 @@
"@pnpm/read-projects-context": "workspace:*",
"@pnpm/resolver-base": "workspace:*",
"@pnpm/types": "workspace:*",
"ci-info": "catalog:",
"path-absolute": "catalog:",
"ramda": "catalog:"
},

View File

@@ -82,6 +82,7 @@ interface HookOptions {
export interface GetContextOptions {
autoInstallPeers: boolean
ci?: boolean
excludeLinksFromLockfile: boolean
peersSuffixMaxLength: number
allProjects: Array<ProjectOptions & HookOptions>
@@ -174,6 +175,7 @@ export async function getContext (
workspacePackages: opts.workspacePackages ?? arrayOfWorkspacePackagesToMap(opts.allProjects),
...await readLockfiles({
autoInstallPeers: opts.autoInstallPeers,
ci: opts.ci,
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
peersSuffixMaxLength: opts.peersSuffixMaxLength,
force: opts.force,
@@ -232,6 +234,7 @@ export async function getContextForSingleImporter (
manifest: ProjectManifest,
opts: {
autoInstallPeers: boolean
ci?: boolean
enableGlobalVirtualStore?: boolean
excludeLinksFromLockfile: boolean
peersSuffixMaxLength: number
@@ -331,6 +334,7 @@ export async function getContextForSingleImporter (
virtualStoreDir,
...await readLockfiles({
autoInstallPeers: opts.autoInstallPeers,
ci: opts.ci,
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
peersSuffixMaxLength: opts.peersSuffixMaxLength,
force: opts.force,

View File

@@ -13,7 +13,6 @@ import {
} from '@pnpm/lockfile.fs'
import { logger } from '@pnpm/logger'
import { type ProjectId, type ProjectRootDir } from '@pnpm/types'
import { isCI } from 'ci-info'
import clone from 'ramda/src/clone'
import equals from 'ramda/src/equals'
@@ -30,6 +29,7 @@ export async function readLockfiles (
autoInstallPeers: boolean
excludeLinksFromLockfile: boolean
peersSuffixMaxLength: number
ci?: boolean
force: boolean
frozenLockfile: boolean
projects: Array<{
@@ -57,7 +57,7 @@ export async function readLockfiles (
// ignore `pnpm-lock.yaml` on CI servers
// a latest pnpm should not break all the builds
const lockfileOpts = {
ignoreIncompatible: opts.force || isCI,
ignoreIncompatible: opts.force || opts.ci === true,
wantedVersions: [LOCKFILE_VERSION],
useGitBranchLockfile: opts.useGitBranchLockfile,
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,

View File

@@ -77,7 +77,6 @@
"@zkochan/rimraf": "catalog:",
"@zkochan/table": "catalog:",
"chalk": "catalog:",
"ci-info": "catalog:",
"enquirer": "catalog:",
"get-npm-tarball-url": "catalog:",
"is-subdir": "catalog:",
@@ -111,6 +110,7 @@
"@types/which": "catalog:",
"@types/yarnpkg__lockfile": "catalog:",
"@types/zkochan__table": "catalog:",
"ci-info": "catalog:",
"delay": "catalog:",
"jest-diff": "catalog:",
"path-name": "catalog:",

View File

@@ -4,7 +4,6 @@ import { type Config, types as allTypes } from '@pnpm/config'
import { WANTED_LOCKFILE } from '@pnpm/constants'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import { type CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
import { isCI } from 'ci-info'
import pick from 'ramda/src/pick'
import renderHelp from 'render-help'
import { installDeps, type InstallDepsOptions } from './installDeps'
@@ -334,7 +333,7 @@ export type InstallCommandOptions = Pick<Config,
workspace?: boolean
includeOnlyPackageFiles?: boolean
confirmModulesPurge?: boolean
} & Partial<Pick<Config, 'modulesCacheMaxAge' | 'pnpmHomeDir' | 'preferWorkspacePackages' | 'useLockfile' | 'symlink'>>
} & Partial<Pick<Config, 'ci' | 'modulesCacheMaxAge' | 'pnpmHomeDir' | 'preferWorkspacePackages' | 'useLockfile' | 'symlink'>>
export async function handler (opts: InstallCommandOptions): Promise<void> {
const include = {
@@ -348,7 +347,7 @@ export async function handler (opts: InstallCommandOptions): Promise<void> {
const installDepsOptions: InstallDepsOptions = {
...opts,
frozenLockfileIfExists: opts.frozenLockfileIfExists ?? (
isCI && !opts.lockfileOnly &&
opts.ci && !opts.lockfileOnly &&
typeof opts.rawLocalConfig['frozen-lockfile'] === 'undefined' &&
typeof opts.rawLocalConfig['prefer-frozen-lockfile'] === 'undefined'
),

View File

@@ -11,6 +11,7 @@ export const DEFAULT_OPTS = {
ca: undefined,
cacheDir: '../cache',
cert: undefined,
ci: false,
excludeLinksFromLockfile: false,
extraEnv: {},
cliOptions: {},

18
pnpm-lock.yaml generated
View File

@@ -1514,6 +1514,9 @@ importers:
can-write-to-dir:
specifier: 'catalog:'
version: 1.1.1
ci-info:
specifier: 'catalog:'
version: 3.9.0
is-subdir:
specifier: 'catalog:'
version: 1.2.0
@@ -4637,9 +4640,6 @@ importers:
'@zkochan/rimraf':
specifier: 'catalog:'
version: 3.0.2
ci-info:
specifier: 'catalog:'
version: 3.9.0
enquirer:
specifier: 'catalog:'
version: 2.4.1
@@ -4737,6 +4737,9 @@ importers:
'@yarnpkg/core':
specifier: 'catalog:'
version: 4.0.5(typanion@3.14.0)
ci-info:
specifier: 'catalog:'
version: 3.9.0
deep-require-cwd:
specifier: 'catalog:'
version: 1.0.0
@@ -4828,9 +4831,6 @@ importers:
'@pnpm/types':
specifier: workspace:*
version: link:../../packages/types
ci-info:
specifier: 'catalog:'
version: 3.9.0
path-absolute:
specifier: 'catalog:'
version: 1.0.1
@@ -5508,9 +5508,6 @@ importers:
chalk:
specifier: 'catalog:'
version: 4.1.2
ci-info:
specifier: 'catalog:'
version: 3.9.0
enquirer:
specifier: 'catalog:'
version: 2.4.1
@@ -5596,6 +5593,9 @@ importers:
'@types/zkochan__table':
specifier: 'catalog:'
version: '@types/table@6.0.0'
ci-info:
specifier: 'catalog:'
version: 3.9.0
delay:
specifier: 'catalog:'
version: 5.0.0

View File

@@ -19,7 +19,6 @@ import { type ParsedCliArgs } from '@pnpm/parse-cli-args'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import { finishWorkers } from '@pnpm/worker'
import chalk from 'chalk'
import { isCI } from 'ci-info'
import path from 'path'
import isEmpty from 'ramda/src/isEmpty'
import { stripVTControlCharacters as stripAnsi } from 'util'
@@ -159,7 +158,7 @@ export async function main (inputArgv: string[]): Promise<void> {
const reporterType: ReporterType = (() => {
if (config.loglevel === 'silent') return 'silent'
if (config.reporter) return config.reporter as ReporterType
if (isCI || !process.stdout.isTTY) return 'append-only'
if (config.ci || !process.stdout.isTTY) return 'append-only'
return 'default'
})()
@@ -249,7 +248,7 @@ export async function main (inputArgv: string[]): Promise<void> {
if (
config.updateNotifier !== false &&
!isCI &&
!config.ci &&
cmd !== 'self-update' &&
!config.offline &&
!config.preferOffline &&

View File

@@ -13,6 +13,7 @@ test('using a global virtual store', async () => {
const storeDir = path.resolve('store')
const globalVirtualStoreDir = path.join(storeDir, 'v10/links')
writeYamlFile(path.resolve('pnpm-workspace.yaml'), {
ci: false, // We force CI=false because enableGlobalVirtualStore is always disabled in CI
enableGlobalVirtualStore: true,
storeDir,
privateHoistPattern: '*',

View File

@@ -10,6 +10,7 @@ export const DEFAULT_OPTS = {
bin: 'node_modules/.bin',
ca: undefined,
cert: undefined,
ci: false,
excludeLinksFromLockfile: false,
extraEnv: {},
cliOptions: {},

View File

@@ -11,6 +11,7 @@ export const DEFAULT_OPTS = {
ca: undefined,
cacheDir: '../cache',
cert: undefined,
ci: false,
excludeLinksFromLockfile: false,
extraEnv: {},
cliOptions: {},