feat: add git-branch-lockfile config to generate lockfile in each branch (#4475)

Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: David Michon <dmichon@microsoft.com>
This commit is contained in:
Cheng
2022-06-15 04:31:16 +08:00
committed by GitHub
parent fb5bbfd7a8
commit 56cf04cb36
48 changed files with 853 additions and 36 deletions

View File

@@ -0,0 +1,14 @@
---
"@pnpm/config": minor
"@pnpm/core": minor
"@pnpm/get-context": minor
"@pnpm/git-utils": minor
"@pnpm/headless": minor
"@pnpm/lockfile-file": minor
"@pnpm/plugin-commands-installation": minor
"@pnpm/plugin-commands-publishing": minor
"pnpm": minor
"@pnpm/assert-project": minor
---
New settings added: use-git-branch-lockfile, merge-git-branch-lockfiles, merge-git-branch-lockfiles-branch-pattern.

View File

@@ -1,4 +1,4 @@
lockfileVersion: 5.3
lockfileVersion: 5.4
specifiers:
camelcase: denolib/camelcase#aeb6b15f9c9957c8fa56f9731e914c4d8a6d2f2b

View File

@@ -34,6 +34,8 @@
"dependencies": {
"@pnpm/constants": "workspace:6.1.0",
"@pnpm/error": "workspace:3.0.1",
"@pnpm/git-utils": "workspace:0.0.0",
"@pnpm/matcher": "workspace:3.0.0",
"@pnpm/npm-conf": "1.0.4",
"@pnpm/pnpmfile": "workspace:2.0.3",
"@pnpm/read-project-manifest": "workspace:3.0.3",

View File

@@ -130,6 +130,9 @@ export interface Config {
modulesDir?: string
sharedWorkspaceLockfile?: boolean
useLockfile: boolean
useGitBranchLockfile: boolean
mergeGitBranchLockfiles?: boolean
mergeGitBranchLockfilesBranchPattern?: string[]
globalPnpmfile?: string
npmPath?: string
gitChecks?: boolean

View File

@@ -6,6 +6,8 @@ import loadNpmConf from '@pnpm/npm-conf'
import npmTypes from '@pnpm/npm-conf/lib/types'
import { requireHooks } from '@pnpm/pnpmfile'
import { safeReadProjectManifestOnly } from '@pnpm/read-project-manifest'
import { getCurrentBranch } from '@pnpm/git-utils'
import matcher from '@pnpm/matcher'
import camelcase from 'camelcase'
import normalizeRegistryUrl from 'normalize-registry-url'
import fromPairs from 'ramda/src/fromPairs'
@@ -36,6 +38,8 @@ export const types = Object.assign({
bail: Boolean,
'cache-dir': String,
'child-concurrency': Number,
'merge-git-branch-lockfiles': Boolean,
'merge-git-branch-lockfiles-branch-pattern': Array,
color: ['always', 'auto', 'never'],
'config-dir': String,
dev: [null, true],
@@ -53,6 +57,7 @@ export const types = Object.assign({
'global-dir': String,
'global-path': String,
'global-pnpmfile': String,
'git-branch-lockfile': Boolean,
hoist: Boolean,
'hoist-pattern': Array,
'ignore-pnpmfile': Boolean,
@@ -185,6 +190,7 @@ export default async (
'bitbucket.org',
],
globalconfig: npmDefaults.globalconfig,
'git-branch-lockfile': false,
hoist: true,
'hoist-pattern': ['*'],
'ignore-workspace-root-check': false,
@@ -261,6 +267,21 @@ export default async (
if (typeof pnpmConfig['packageLock'] === 'boolean') return pnpmConfig['packageLock']
return false
})()
pnpmConfig.useGitBranchLockfile = (() => {
if (typeof pnpmConfig['gitBranchLockfile'] === 'boolean') return pnpmConfig['gitBranchLockfile']
return false
})()
pnpmConfig.mergeGitBranchLockfiles = await (async () => {
if (typeof pnpmConfig['mergeGitBranchLockfiles'] === 'boolean') return pnpmConfig['mergeGitBranchLockfiles']
if (pnpmConfig['mergeGitBranchLockfilesBranchPattern'] != null && pnpmConfig['mergeGitBranchLockfilesBranchPattern'].length > 0) {
const branch = await getCurrentBranch()
if (branch) {
const branchMatcher = matcher(pnpmConfig['mergeGitBranchLockfilesBranchPattern'])
return branchMatcher(branch)
}
}
return undefined
})()
pnpmConfig.pnpmHomeDir = getDataDir(process)
if (cliOptions['global']) {

View File

@@ -2,6 +2,7 @@
import { promises as fs } from 'fs'
import path from 'path'
import PATH from 'path-name'
import { getCurrentBranch } from '@pnpm/git-utils'
import getConfig from '@pnpm/config'
import PnpmError from '@pnpm/error'
import loadNpmConf from '@pnpm/npm-conf'
@@ -9,6 +10,8 @@ import prepare, { prepareEmpty } from '@pnpm/prepare'
import symlinkDir from 'symlink-dir'
jest.mock('@pnpm/git-utils', () => ({ getCurrentBranch: jest.fn() }))
// To override any local settings,
// we force the default values of config
delete process.env.npm_config_depth
@@ -805,3 +808,92 @@ test('getConfig() should read cafile', async () => {
expect(config.ca).toStrictEqual([`xxx
-----END CERTIFICATE-----`])
})
test('respect merge-git-branch-lockfiles-branch-pattern', async () => {
{
const { config } = await getConfig({
cliOptions: {},
packageManager: {
name: 'pnpm',
version: '1.0.0',
},
})
expect(config.mergeGitBranchLockfilesBranchPattern).toBeUndefined()
expect(config.mergeGitBranchLockfiles).toBeUndefined()
}
{
prepareEmpty()
const npmrc = [
'merge-git-branch-lockfiles-branch-pattern[]=main',
'merge-git-branch-lockfiles-branch-pattern[]=release/**',
].join('\n')
await fs.writeFile('.npmrc', npmrc, 'utf8')
const { config } = await getConfig({
cliOptions: {
global: false,
},
packageManager: {
name: 'pnpm',
version: '1.0.0',
},
})
expect(config.mergeGitBranchLockfilesBranchPattern).toEqual(['main', 'release/**'])
}
})
test('getConfig() sets merge-git-branch-lockfiles when branch matches merge-git-branch-lockfiles-branch-pattern', async () => {
prepareEmpty()
{
const npmrc = [
'merge-git-branch-lockfiles-branch-pattern[]=main',
'merge-git-branch-lockfiles-branch-pattern[]=release/**',
].join('\n')
await fs.writeFile('.npmrc', npmrc, 'utf8')
getCurrentBranch['mockReturnValue']('develop')
const { config } = await getConfig({
cliOptions: {
global: false,
},
packageManager: {
name: 'pnpm',
version: '1.0.0',
},
})
expect(config.mergeGitBranchLockfilesBranchPattern).toEqual(['main', 'release/**'])
expect(config.mergeGitBranchLockfiles).toBe(false)
}
{
getCurrentBranch['mockReturnValue']('main')
const { config } = await getConfig({
cliOptions: {
global: false,
},
packageManager: {
name: 'pnpm',
version: '1.0.0',
},
})
expect(config.mergeGitBranchLockfiles).toBe(true)
}
{
getCurrentBranch['mockReturnValue']('release/1.0.0')
const { config } = await getConfig({
cliOptions: {
global: false,
},
packageManager: {
name: 'pnpm',
version: '1.0.0',
},
})
expect(config.mergeGitBranchLockfiles).toBe(true)
}
})

View File

@@ -18,6 +18,12 @@
{
"path": "../error"
},
{
"path": "../git-utils"
},
{
"path": "../matcher"
},
{
"path": "../pnpmfile"
},

View File

@@ -72,6 +72,7 @@
"@pnpm/cafs": "workspace:4.0.4",
"@pnpm/client": "workspace:7.1.4",
"@pnpm/core": "workspace:5.3.1",
"@pnpm/git-utils": "workspace:0.0.0",
"@pnpm/logger": "^4.0.0",
"@pnpm/package-store": "workspace:13.0.7",
"@pnpm/prepare": "workspace:*",

View File

@@ -16,6 +16,7 @@ export type ListMissingPeersOptions = Partial<GetContextOptions>
| 'preferWorkspacePackages'
| 'saveWorkspaceProtocol'
| 'storeController'
| 'useGitBranchLockfile'
| 'workspacePackages'
>
& Pick<GetContextOptions, 'storeDir'>

View File

@@ -25,6 +25,8 @@ export interface StrictInstallOptions {
extraBinPaths: string[]
hoistingLimits?: HoistingLimits
useLockfile: boolean
useGitBranchLockfile: boolean
mergeGitBranchLockfiles: boolean
linkWorkspacePackagesDepth: number
lockfileOnly: boolean
fixLockfile: boolean
@@ -167,6 +169,8 @@ const defaults = async (opts: InstallOptions) => {
process.getuid() !== 0,
update: false,
useLockfile: true,
useGitBranchLockfile: false,
mergeGitBranchLockfiles: false,
userAgent: `${packageManager.name}/${packageManager.version} npm/? node/${process.version} ${process.platform} ${process.arch}`,
verifyStoreIntegrity: true,
workspacePackages: {},

View File

@@ -25,6 +25,7 @@ import {
writeCurrentLockfile,
writeLockfiles,
writeWantedLockfile,
cleanGitBranchLockfiles,
} from '@pnpm/lockfile-file'
import { writePnpFile } from '@pnpm/lockfile-to-pnp'
import { extendProjectsWithTargetDirs } from '@pnpm/lockfile-utils'
@@ -202,6 +203,10 @@ export async function mutateModules (
streamParser.removeListener('data', reporter)
}
if (opts.mergeGitBranchLockfiles) {
await cleanGitBranchLockfiles(ctx.lockfileDir)
}
return result
async function _install (): Promise<UpdatedProject[]> {
@@ -785,7 +790,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
}
const depsStateCache: DepsStateCache = {}
const lockfileOpts = { forceSharedFormat: opts.forceSharedLockfile }
const lockfileOpts = { forceSharedFormat: opts.forceSharedLockfile, useGitBranchLockfile: opts.useGitBranchLockfile, mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles }
if (!opts.lockfileOnly && opts.enableModulesDir) {
const result = await linkPackages(
projects,

View File

@@ -129,7 +129,7 @@ export default async function link (
} else {
newPkg = opts.manifest
}
const lockfileOpts = { forceSharedFormat: opts.forceSharedLockfile }
const lockfileOpts = { forceSharedFormat: opts.forceSharedLockfile, useGitBranchLockfile: opts.useGitBranchLockfile, mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles }
if (opts.useLockfile) {
await writeLockfiles({
currentLockfile: updatedCurrentLockfile,

View File

@@ -29,6 +29,9 @@ interface StrictLinkOptions {
publicHoistPattern: string[] | undefined
forcePublicHoistPattern: boolean
useGitBranchLockfile: boolean
mergeGitBranchLockfiles: boolean
}
export type LinkOptions =

View File

@@ -0,0 +1,146 @@
import fs from 'fs'
import path from 'path'
import { prepareEmpty, preparePackages } from '@pnpm/prepare'
import { install, mutateModules } from '@pnpm/core'
import { testDefaults } from '../utils'
import { WANTED_LOCKFILE } from '@pnpm/constants'
import { ProjectManifest } from '@pnpm/types'
import { getCurrentBranch } from '@pnpm/git-utils'
import writeYamlFile from 'write-yaml-file'
jest.mock('@pnpm/git-utils', () => ({ getCurrentBranch: jest.fn() }))
test('install with git-branch-lockfile = true', async () => {
prepareEmpty()
const branchName: string = 'main-branch'
getCurrentBranch['mockReturnValue'](branchName)
const opts = await testDefaults({
useGitBranchLockfile: true,
})
await install({
dependencies: {
'is-positive': '^3.0.0',
},
}, opts)
expect(fs.existsSync(`pnpm-lock.${branchName}.yaml`)).toBe(true)
expect(fs.existsSync(WANTED_LOCKFILE)).toBe(false)
})
test('install with git-branch-lockfile = true and no lockfile changes', async () => {
prepareEmpty()
const branchName: string = 'main-branch'
getCurrentBranch['mockReturnValue'](branchName)
const manifest: ProjectManifest = {
dependencies: {
'is-positive': '^3.0.0',
},
}
const opts1 = await testDefaults({
useGitBranchLockfile: false,
})
await install(manifest, opts1)
expect(fs.existsSync(WANTED_LOCKFILE)).toBe(true)
const opts2 = await testDefaults({
useGitBranchLockfile: true,
})
await install(manifest, opts2)
expect(fs.existsSync(WANTED_LOCKFILE)).toBe(true)
// Git branch lockfile is created only if there are changes in the lockfile
expect(fs.existsSync(`pnpm-lock.${branchName}.yaml`)).toBe(false)
})
test('install a workspace with git-branch-lockfile = true', async () => {
const rootManifest: ProjectManifest = {
name: 'root',
}
const project1Manifest: ProjectManifest = {
name: 'project-1',
dependencies: { 'is-positive': '1.0.0' },
}
const project2Manifest: ProjectManifest = {
name: 'project-2',
dependencies: { 'is-positive': '1.0.0' },
}
preparePackages([
{
location: '.',
package: rootManifest,
},
{
location: 'project-1',
package: project1Manifest,
},
{
location: 'project-2',
package: project2Manifest,
},
])
const branchName: string = 'main-branch'
getCurrentBranch['mockReturnValue'](branchName)
const opts = await testDefaults({
useGitBranchLockfile: true,
})
await mutateModules([
{
buildIndex: 0,
manifest: rootManifest,
mutation: 'install',
rootDir: process.cwd(),
},
{
buildIndex: 0,
manifest: project1Manifest,
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
mutation: 'install',
rootDir: path.resolve('project-2'),
},
], opts)
expect(fs.existsSync(`pnpm-lock.${branchName}.yaml`)).toBe(true)
expect(fs.existsSync(WANTED_LOCKFILE)).toBe(false)
})
test('install with --merge-git-branch-lockfiles', async () => {
prepareEmpty()
const branchName: string = 'main-branch'
getCurrentBranch['mockReturnValue'](branchName)
const otherLockfilePath: string = path.resolve('pnpm-lock.other.yaml')
await writeYamlFile(otherLockfilePath, {
whatever: 'whatever',
})
expect(fs.existsSync(otherLockfilePath)).toBe(true)
expect(fs.existsSync(WANTED_LOCKFILE)).toBe(false)
const opts = await testDefaults({
useGitBranchLockfile: true,
mergeGitBranchLockfiles: true,
})
await install({
dependencies: {
'is-positive': '^3.0.0',
},
}, opts)
expect(fs.existsSync(otherLockfilePath)).toBe(false)
expect(fs.existsSync(WANTED_LOCKFILE)).toBe(true)
})

View File

@@ -51,6 +51,9 @@
{
"path": "../get-context"
},
{
"path": "../git-utils"
},
{
"path": "../headless"
},

View File

@@ -82,6 +82,8 @@ export interface GetContextOptions {
registries: Registries
storeDir: string
useLockfile: boolean
useGitBranchLockfile?: boolean
mergeGitBranchLockfiles?: boolean
virtualStoreDir?: string
hoistPattern?: string[] | undefined
@@ -176,6 +178,8 @@ export default async function getContext<T> (
projects: importersContext.projects,
registry: opts.registries.default,
useLockfile: opts.useLockfile,
useGitBranchLockfile: opts.useGitBranchLockfile,
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
virtualStoreDir,
}),
}
@@ -376,6 +380,8 @@ export async function getContextForSingleImporter (
registries: Registries
storeDir: string
useLockfile: boolean
useGitBranchLockfile?: boolean
mergeGitBranchLockfiles?: boolean
virtualStoreDir?: string
hoistPattern?: string[] | undefined
@@ -479,6 +485,8 @@ export async function getContextForSingleImporter (
projects: [{ id: importerId, rootDir: opts.dir }],
registry: opts.registries.default,
useLockfile: opts.useLockfile,
useGitBranchLockfile: opts.useGitBranchLockfile,
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
virtualStoreDir,
}),
}

View File

@@ -34,6 +34,8 @@ export default async function (
lockfileDir: string
registry: string
useLockfile: boolean
useGitBranchLockfile?: boolean
mergeGitBranchLockfiles?: boolean
virtualStoreDir: string
}
): Promise<{
@@ -49,6 +51,8 @@ export default async function (
const lockfileOpts = {
ignoreIncompatible: opts.force || isCI,
wantedVersion: LOCKFILE_VERSION,
useGitBranchLockfile: opts.useGitBranchLockfile,
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
}
const fileReads = [] as Array<Promise<Lockfile | undefined | null>>
let lockfileHadConflicts: boolean = false
@@ -73,7 +77,7 @@ export default async function (
fileReads.push(readWantedLockfile(opts.lockfileDir, lockfileOpts))
}
} else {
if (await existsWantedLockfile(opts.lockfileDir)) {
if (await existsWantedLockfile(opts.lockfileDir, lockfileOpts)) {
logger.warn({
message: `A ${WANTED_LOCKFILE} file exists. The current configuration prohibits to read or write a lockfile`,
prefix: opts.lockfileDir,

View File

@@ -0,0 +1,32 @@
# @pnpm/git-utils
> Utilities for git
<!--@shields('npm')-->
[![npm version](https://img.shields.io/npm/v/@pnpm/git-utils.svg)](https://www.npmjs.com/package/@pnpm/git-utils)
<!--/@-->
## Installation
```
pnpm add @pnpm/git-utils
```
## Usage
<!--@example('./example.js')-->
```js
'use strict'
const { getCurrentBranchName } = require('@pnpm-utils').default
main()
async function main() {
const branchName = await getCurrentBranch();
console.log(branchName)
}
```
<!--/@-->
# License
MIT

View File

@@ -0,0 +1,8 @@
'use strict'
const { getCurrentBranch } = require('@pnpm-utils').default
main()
async function main() {
const branchName = await getCurrentBranch();
console.log(branchName)
}

View File

@@ -0,0 +1 @@
module.exports = require('../../jest.config.js')

View File

@@ -0,0 +1,46 @@
{
"name": "@pnpm/git-utils",
"version": "0.0.0",
"description": "Utilities for git",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib",
"!*.map"
],
"engines": {
"node": ">=14.6"
},
"scripts": {
"lint": "eslint src/**/*.ts test/**/*.ts",
"_test": "jest",
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm run compile",
"fix": "tslint -c tslint.json src/**/*.ts test/**/*.ts --fix",
"compile-only": "tsc --build",
"compile": "tsc --build && pnpm run lint --fix"
},
"repository": "https://github.com/pnpm/pnpm/blob/main/packages/git-utils",
"keywords": [
"pnpm7",
"pnpm",
"git",
"npm"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
"homepage": "https://github.com/pnpm/pnpm/blob/main/packages/git-utils#readme",
"dependencies": {
"execa": "npm:safe-execa@^0.1.1"
},
"devDependencies": {
"@pnpm/git-utils": "workspace:0.0.0",
"tempy": "^1.0.0"
},
"funding": "https://opencollective.com/pnpm",
"exports": {
".": "./lib/index.js"
}
}

View File

@@ -0,0 +1,39 @@
import tempy from 'tempy'
import execa from 'execa'
import { promises as fs } from 'fs'
import path from 'path'
import { getCurrentBranch, isGitRepo, isWorkingTreeClean } from '@pnpm/git-utils'
test('isGitRepo', async () => {
const tempDir = tempy.directory()
process.chdir(tempDir)
await expect(isGitRepo()).resolves.toBe(false)
await execa('git', ['init'])
await expect(isGitRepo()).resolves.toBe(true)
})
test('getCurrentBranch', async () => {
const tempDir = tempy.directory()
process.chdir(tempDir)
await execa('git', ['init'])
await execa('git', ['checkout', '-b', 'foo'])
await expect(getCurrentBranch()).resolves.toBe('foo')
})
test('isWorkingTreeClean', async () => {
const tempDir = tempy.directory()
process.chdir(tempDir)
await execa('git', ['init'])
await expect(isWorkingTreeClean()).resolves.toBe(true)
await fs.writeFile(path.join(tempDir, 'foo'), 'foo')
await expect(isWorkingTreeClean()).resolves.toBe(false)
})

View File

@@ -0,0 +1,13 @@
{
"extends": "@pnpm/tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src"
},
"include": [
"src/**/*.ts",
"../../typings/**/*.d.ts"
],
"references": [
]
}

View File

@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"include": [
"src/**/*.ts",
"test/**/*.ts",
"../../typings/**/*.d.ts"
]
}

View File

@@ -137,6 +137,7 @@ export interface HeadlessOptions {
skipped: Set<string>
enableModulesDir?: boolean
nodeLinker?: 'isolated' | 'hoisted' | 'pnp'
useGitBranchLockfile?: boolean
}
export default async (opts: HeadlessOptions) => {
@@ -146,7 +147,12 @@ export default async (opts: HeadlessOptions) => {
}
const lockfileDir = opts.lockfileDir
const wantedLockfile = opts.wantedLockfile ?? await readWantedLockfile(lockfileDir, { ignoreIncompatible: false })
const wantedLockfile = opts.wantedLockfile ?? await readWantedLockfile(lockfileDir, {
ignoreIncompatible: false,
useGitBranchLockfile: opts.useGitBranchLockfile,
// mergeGitBranchLockfiles is intentionally not supported in headless
mergeGitBranchLockfiles: false,
})
if (wantedLockfile == null) {
throw new Error(`Headless installation requires a ${WANTED_LOCKFILE} file`)

View File

@@ -49,6 +49,7 @@
"dependencies": {
"@pnpm/constants": "workspace:6.1.0",
"@pnpm/error": "workspace:3.0.1",
"@pnpm/git-utils": "workspace:0.0.0",
"@pnpm/lockfile-types": "workspace:4.0.2",
"@pnpm/merge-lockfile-changes": "workspace:3.0.2",
"@pnpm/types": "workspace:8.1.0",

View File

@@ -1,17 +1,28 @@
import fs from 'fs'
import path from 'path'
import { WANTED_LOCKFILE } from '@pnpm/constants'
import { getWantedLockfileName } from './lockfileName'
export default async (pkgPath: string) => new Promise((resolve, reject) => {
fs.access(path.join(pkgPath, WANTED_LOCKFILE), (err) => {
if (err == null) {
resolve(true)
return
}
if (err.code === 'ENOENT') {
resolve(false)
return
}
reject(err)
interface ExistsWantedLockfileOptions {
useGitBranchLockfile?: boolean
mergeGitBranchLockfiles?: boolean
}
export default async (pkgPath: string, opts: ExistsWantedLockfileOptions = {
useGitBranchLockfile: false,
mergeGitBranchLockfiles: false,
}) => {
const wantedLockfile: string = await getWantedLockfileName(opts)
return new Promise((resolve, reject) => {
fs.access(path.join(pkgPath, wantedLockfile), (err) => {
if (err == null) {
resolve(true)
return
}
if (err.code === 'ENOENT') {
resolve(false)
return
}
reject(err)
})
})
})
}

View File

@@ -0,0 +1,18 @@
import { promises as fs } from 'fs'
import path from 'path'
export async function getGitBranchLockfileNames (lockfileDir: string) {
const files = await fs.readdir(lockfileDir)
const gitBranchLockfileNames: string[] = files.filter(file => file.match(/^pnpm-lock.(?:.*).yaml$/))
return gitBranchLockfileNames
}
export async function cleanGitBranchLockfiles (lockfileDir: string) {
const gitBranchLockfiles: string[] = await getGitBranchLockfileNames(lockfileDir)
await Promise.all(
gitBranchLockfiles.map(async file => {
const filepath: string = path.join(lockfileDir, file)
await fs.unlink(filepath)
})
)
}

View File

@@ -15,3 +15,5 @@ export {
writeCurrentLockfile,
writeWantedLockfile,
}
export { cleanGitBranchLockfiles } from './gitBranchLockfile'

View File

@@ -0,0 +1,25 @@
import { WANTED_LOCKFILE } from '@pnpm/constants'
import { getCurrentBranch } from '@pnpm/git-utils'
export interface GetWantedLockfileNameOptions {
useGitBranchLockfile?: boolean
mergeGitBranchLockfiles?: boolean
}
export async function getWantedLockfileName (opts: GetWantedLockfileNameOptions = { useGitBranchLockfile: false, mergeGitBranchLockfiles: false }) {
if (opts.useGitBranchLockfile && !opts.mergeGitBranchLockfiles) {
const currentBranchName = await getCurrentBranch()
if (currentBranchName) {
return WANTED_LOCKFILE.replace('.yaml', `.${stringifyBranchName(currentBranchName)}.yaml`)
}
}
return WANTED_LOCKFILE
}
/**
* 1. Git branch name may contains slashes, which is not allowed in filenames
* 2. Filesystem may be case-insensitive, so we need to convert branch name to lowercase
*/
function stringifyBranchName (branchName: string = '') {
return branchName.replace(/[^a-zA-Z0-9-_.]/g, '!').toLowerCase()
}

View File

@@ -5,6 +5,7 @@ import {
WANTED_LOCKFILE,
} from '@pnpm/constants'
import PnpmError from '@pnpm/error'
import mergeLockfileChanges from '@pnpm/merge-lockfile-changes'
import { Lockfile } from '@pnpm/lockfile-types'
import { DEPENDENCIES_FIELDS } from '@pnpm/types'
import comverToSemver from 'comver-to-semver'
@@ -15,6 +16,8 @@ import { LockfileBreakingChangeError } from './errors'
import { autofixMergeConflicts, isDiff } from './gitMergeFile'
import logger from './logger'
import { LockfileFile } from './write'
import { getWantedLockfileName } from './lockfileName'
import { getGitBranchLockfileNames } from './gitBranchLockfile'
export async function readCurrentLockfile (
virtualStoreDir: string,
@@ -32,13 +35,17 @@ export async function readWantedLockfileAndAutofixConflicts (
opts: {
wantedVersion?: number
ignoreIncompatible: boolean
useGitBranchLockfile?: boolean
mergeGitBranchLockfiles?: boolean
}
): Promise<{
lockfile: Lockfile | null
hadConflicts: boolean
}> {
const lockfilePath = path.join(pkgPath, WANTED_LOCKFILE)
return _read(lockfilePath, pkgPath, { ...opts, autofixMergeConflicts: true })
return _readWantedLockfile(pkgPath, {
...opts,
autofixMergeConflicts: true,
})
}
export async function readWantedLockfile (
@@ -46,10 +53,11 @@ export async function readWantedLockfile (
opts: {
wantedVersion?: number
ignoreIncompatible: boolean
useGitBranchLockfile?: boolean
mergeGitBranchLockfiles?: boolean
}
): Promise<Lockfile | null> {
const lockfilePath = path.join(pkgPath, WANTED_LOCKFILE)
return (await _read(lockfilePath, pkgPath, opts)).lockfile
return (await _readWantedLockfile(pkgPath, opts)).lockfile
}
async function _read (
@@ -136,6 +144,83 @@ export function createLockfileObject (
}
}
async function _readWantedLockfile (
pkgPath: string,
opts: {
wantedVersion?: number
ignoreIncompatible: boolean
useGitBranchLockfile?: boolean
mergeGitBranchLockfiles?: boolean
autofixMergeConflicts?: boolean
}
): Promise<{
lockfile: Lockfile | null
hadConflicts: boolean
}> {
const lockfileNames: string[] = [WANTED_LOCKFILE]
if (opts.useGitBranchLockfile) {
const gitBranchLockfileName: string = await getWantedLockfileName(opts)
if (gitBranchLockfileName !== WANTED_LOCKFILE) {
lockfileNames.unshift(gitBranchLockfileName)
}
}
let result: { lockfile: Lockfile | null, hadConflicts: boolean } = { lockfile: null, hadConflicts: false }
for (const lockfileName of lockfileNames) {
result = await _read(path.join(pkgPath, lockfileName), pkgPath, { ...opts, autofixMergeConflicts: true })
if (result.lockfile) {
if (opts.mergeGitBranchLockfiles) {
result.lockfile = await _mergeGitBranchLockfiles(result.lockfile, pkgPath, pkgPath, opts)
}
break
}
}
return result
}
async function _mergeGitBranchLockfiles (
lockfile: Lockfile | null,
lockfileDir: string,
prefix: string,
opts: {
autofixMergeConflicts?: boolean
wantedVersion?: number
ignoreIncompatible: boolean
}
): Promise<Lockfile | null> {
if (!lockfile) {
return lockfile
}
const gitBranchLockfiles: Array<(Lockfile | null)> = (await _readGitBranchLockfiles(lockfileDir, prefix, opts)).map(({ lockfile }) => lockfile)
let mergedLockfile: Lockfile = lockfile
for (const gitBranchLockfile of gitBranchLockfiles) {
if (!gitBranchLockfile) {
continue
}
mergedLockfile = mergeLockfileChanges(mergedLockfile, gitBranchLockfile)
}
return mergedLockfile
}
async function _readGitBranchLockfiles (
lockfileDir: string,
prefix: string,
opts: {
autofixMergeConflicts?: boolean
wantedVersion?: number
ignoreIncompatible: boolean
}
): Promise<Array<{
lockfile: Lockfile | null
hadConflicts: boolean
}>> {
const files = await getGitBranchLockfileNames(lockfileDir)
return Promise.all(files.map((file) => _read(path.join(lockfileDir, file), prefix, opts)))
}
/**
* Reverts changes from the "forceSharedFormat" write option if necessary.
*/

View File

@@ -10,6 +10,7 @@ import isEmpty from 'ramda/src/isEmpty'
import writeFileAtomicCB from 'write-file-atomic'
import logger from './logger'
import { sortLockfileKeys } from './sortLockfileKeys'
import { getWantedLockfileName } from './lockfileName'
async function writeFileAtomic (filename: string, data: string) {
return new Promise<void>((resolve, reject) => writeFileAtomicCB(filename, data, {}, (err?: Error) => (err != null) ? reject(err) : resolve()))
@@ -28,9 +29,12 @@ export async function writeWantedLockfile (
wantedLockfile: Lockfile,
opts?: {
forceSharedFormat?: boolean
useGitBranchLockfile?: boolean
mergeGitBranchLockfiles?: boolean
}
) {
return writeLockfile(WANTED_LOCKFILE, pkgPath, wantedLockfile, opts)
const wantedLockfileName: string = await getWantedLockfileName(opts)
return writeLockfile(wantedLockfileName, pkgPath, wantedLockfile, opts)
}
export async function writeCurrentLockfile (
@@ -142,9 +146,12 @@ export default async function writeLockfiles (
wantedLockfileDir: string
currentLockfile: Lockfile
currentLockfileDir: string
useGitBranchLockfile?: boolean
mergeGitBranchLockfiles?: boolean
}
) {
const wantedLockfilePath = path.join(opts.wantedLockfileDir, WANTED_LOCKFILE)
const wantedLockfileName: string = await getWantedLockfileName(opts)
const wantedLockfilePath = path.join(opts.wantedLockfileDir, wantedLockfileName)
const currentLockfilePath = path.join(opts.currentLockfileDir, 'lock.yaml')
// empty lockfile is not saved

View File

View File

@@ -0,0 +1,9 @@
lockfileVersion: 5.3
specifiers:
is-positive: '2.0.0'
packages:
/is-positive/2.0.0:
resolution: {integrity: 'sha1-ChbBDewTLAqLCzb793Fo5VDvg/g='}

View File

@@ -0,0 +1,8 @@
lockfileVersion: 5.3
specifiers:
is-positive: '1.0.0'
packages:
/is-positive/1.0.0:
resolution: {integrity: 'sha1-ChbBDewTLAqLCzb793Fo5VDvg/g='}

View File

@@ -0,0 +1,10 @@
import path from 'path'
import { getGitBranchLockfileNames } from '../lib/gitBranchLockfile'
process.chdir(__dirname)
test('getGitBranchLockfileNames()', async () => {
const lockfileDir: string = path.join('fixtures', '6')
const gitBranchLockfileNames = await getGitBranchLockfileNames(lockfileDir)
expect(gitBranchLockfileNames).toEqual(['pnpm-lock.branch.yaml'])
})

View File

@@ -0,0 +1,30 @@
import { WANTED_LOCKFILE } from '@pnpm/constants'
import { getCurrentBranch } from '@pnpm/git-utils'
import { getWantedLockfileName } from '../lib/lockfileName'
jest.mock('@pnpm/git-utils', () => ({ getCurrentBranch: jest.fn() }))
describe('lockfileName', () => {
afterEach(() => {
getCurrentBranch['mockReset']()
})
test('returns default lockfile name if useGitBranchLockfile is off', async () => {
await expect(getWantedLockfileName()).resolves.toBe(WANTED_LOCKFILE)
})
test('returns git branch lockfile name', async () => {
getCurrentBranch['mockReturnValue']('main')
await expect(getWantedLockfileName({ useGitBranchLockfile: true })).resolves.toBe('pnpm-lock.main.yaml')
})
test('returns git branch lockfile name when git branch contains clashes', async () => {
getCurrentBranch['mockReturnValue']('a/b/c')
await expect(getWantedLockfileName({ useGitBranchLockfile: true })).resolves.toBe('pnpm-lock.a!b!c.yaml')
})
test('returns git branch lockfile name when git branch contains uppercase', async () => {
getCurrentBranch['mockReturnValue']('aBc')
await expect(getWantedLockfileName({ useGitBranchLockfile: true })).resolves.toBe('pnpm-lock.abc.yaml')
})
})

View File

@@ -1,4 +1,5 @@
import path from 'path'
import { getCurrentBranch } from '@pnpm/git-utils'
import {
existsWantedLockfile,
readCurrentLockfile,
@@ -8,6 +9,8 @@ import {
} from '@pnpm/lockfile-file'
import tempy from 'tempy'
jest.mock('@pnpm/git-utils', () => ({ getCurrentBranch: jest.fn() }))
process.chdir(__dirname)
test('readWantedLockfile()', async () => {
@@ -190,3 +193,71 @@ test('existsWantedLockfile()', async () => {
})
expect(await existsWantedLockfile(projectPath)).toBe(true)
})
test('readWantedLockfile() when useGitBranchLockfile', async () => {
getCurrentBranch['mockReturnValue']('branch')
const lockfile = await readWantedLockfile(path.join('fixtures', '6'), {
ignoreIncompatible: false,
})
expect(lockfile?.importers).toEqual({
'.': {
specifiers: {
'is-positive': '1.0.0',
},
},
})
expect(lockfile?.packages).toStrictEqual({
'/is-positive/1.0.0': {
resolution: {
integrity: 'sha1-ChbBDewTLAqLCzb793Fo5VDvg/g=',
},
},
})
const gitBranchLockfile = await readWantedLockfile(path.join('fixtures', '6'), {
ignoreIncompatible: false,
useGitBranchLockfile: true,
})
expect(gitBranchLockfile?.importers).toEqual({
'.': {
specifiers: {
'is-positive': '2.0.0',
},
},
})
expect(gitBranchLockfile?.packages).toStrictEqual({
'/is-positive/2.0.0': {
resolution: {
integrity: 'sha1-ChbBDewTLAqLCzb793Fo5VDvg/g=',
},
},
})
})
test('readWantedLockfile() when useGitBranchLockfile and mergeGitBranchLockfiles', async () => {
getCurrentBranch['mockReturnValue']('branch')
const lockfile = await readWantedLockfile(path.join('fixtures', '6'), {
ignoreIncompatible: false,
useGitBranchLockfile: true,
mergeGitBranchLockfiles: true,
})
expect(lockfile?.importers).toEqual({
'.': {
specifiers: {
'is-positive': '2.0.0',
},
},
})
expect(lockfile?.packages).toStrictEqual({
'/is-positive/1.0.0': {
resolution: {
integrity: 'sha1-ChbBDewTLAqLCzb793Fo5VDvg/g=',
},
},
'/is-positive/2.0.0': {
resolution: {
integrity: 'sha1-ChbBDewTLAqLCzb793Fo5VDvg/g=',
},
},
})
})

View File

@@ -8,6 +8,9 @@ import {
} from '@pnpm/lockfile-file'
import tempy from 'tempy'
import yaml from 'yaml-tag'
import { getCurrentBranch } from '@pnpm/git-utils'
jest.mock('@pnpm/git-utils', () => ({ getCurrentBranch: jest.fn() }))
test('writeLockfiles()', async () => {
const projectPath = tempy.directory()
@@ -192,3 +195,39 @@ test('writeLockfiles() does not fail if the lockfile has undefined properties',
wantedLockfileDir: projectPath,
})
})
test('writeLockfiles() when useGitBranchLockfile', async () => {
const branchName: string = 'branch'
getCurrentBranch['mockReturnValue'](branchName)
const projectPath = tempy.directory()
const wantedLockfile = {
importers: {
'.': {
dependencies: {
foo: '1.0.0',
},
specifiers: {
foo: '^1.0.0',
},
},
},
lockfileVersion: LOCKFILE_VERSION,
packages: {
'/foo/1.0.0': {
resolution: {
integrity: 'sha1-ChbBDewTLAqLCzb793Fo5VDvg/g=',
},
},
},
}
await writeLockfiles({
currentLockfile: wantedLockfile,
currentLockfileDir: projectPath,
wantedLockfile,
wantedLockfileDir: projectPath,
useGitBranchLockfile: true,
})
expect(fs.existsSync(path.join(projectPath, WANTED_LOCKFILE))).toBeFalsy()
expect(fs.existsSync(path.join(projectPath, `pnpm-lock.${branchName}.yaml`))).toBeTruthy()
})

View File

@@ -15,6 +15,9 @@
{
"path": "../error"
},
{
"path": "../git-utils"
},
{
"path": "../lockfile-types"
},

View File

@@ -33,6 +33,8 @@ export function rcOptionsTypes () {
'lockfile-directory',
'lockfile-only',
'lockfile',
'merge-git-branch-lockfiles',
'merge-git-branch-lockfiles-branch-pattern',
'modules-dir',
'network-concurrency',
'node-linker',
@@ -139,6 +141,10 @@ For options that may be used with `-r`, see "pnpm help recursive"',
description: 'Fix broken lockfile entries automatically',
name: '--fix-lockfile',
},
{
description: 'Merge lockfiles were generated on git branch',
name: '--merge-git-branch-lockfiles',
},
{
description: 'The directory in which dependencies will be installed (instead of node_modules)',
name: '--modules-dir <dir>',

View File

@@ -63,6 +63,7 @@
"@pnpm/config": "workspace:15.2.1",
"@pnpm/error": "workspace:3.0.1",
"@pnpm/exportable-manifest": "workspace:3.0.3",
"@pnpm/git-utils": "workspace:0.0.0",
"@pnpm/lifecycle": "workspace:13.0.4",
"@pnpm/package-bins": "workspace:6.0.2",
"@pnpm/pick-registry-for-package": "workspace:3.0.2",

View File

@@ -6,6 +6,7 @@ import PnpmError from '@pnpm/error'
import runLifecycleHooks, { RunLifecycleHookOptions } from '@pnpm/lifecycle'
import runNpm from '@pnpm/run-npm'
import { ProjectManifest } from '@pnpm/types'
import { getCurrentBranch, isGitRepo, isRemoteHistoryClean, isWorkingTreeClean } from '@pnpm/git-utils'
import { prompt } from 'enquirer'
import rimraf from '@zkochan/rimraf'
import pick from 'ramda/src/pick'
@@ -14,7 +15,6 @@ import renderHelp from 'render-help'
import tempy from 'tempy'
import * as pack from './pack'
import recursivePublish, { PublishRecursiveOpts } from './recursivePublish'
import { getCurrentBranch, isGitRepo, isRemoteHistoryClean, isWorkingTreeClean } from './gitChecks'
export function rcOptionsTypes () {
return pick([

View File

@@ -30,6 +30,9 @@
{
"path": "../filter-workspace-packages"
},
{
"path": "../git-utils"
},
{
"path": "../lifecycle"
},

View File

@@ -157,6 +157,7 @@
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm compile && npm cache clear --force && publish-packed --prune --npm-client=pnpm --dest=dist",
"postpublish": "publish-packed",
"_compile": "tsc --build",
"compile": "tsc --build && pnpm run lint --fix && rimraf dist bin/nodes && pnpm run bundle && shx cp -r node-gyp-bin dist/node-gyp-bin && shx cp -r node_modules/@pnpm/tabtab/lib/scripts dist/scripts && shx cp -r node_modules/ps-list/vendor dist/vendor && shx cp pnpmrc dist/pnpmrc"
},
"publishConfig": {

37
pnpm-lock.yaml generated
View File

@@ -345,6 +345,8 @@ importers:
'@pnpm/config': workspace:15.2.1
'@pnpm/constants': workspace:6.1.0
'@pnpm/error': workspace:3.0.1
'@pnpm/git-utils': workspace:0.0.0
'@pnpm/matcher': workspace:3.0.0
'@pnpm/npm-conf': 1.0.4
'@pnpm/pnpmfile': workspace:2.0.3
'@pnpm/prepare': workspace:*
@@ -364,6 +366,8 @@ importers:
dependencies:
'@pnpm/constants': link:../constants
'@pnpm/error': link:../error
'@pnpm/git-utils': link:../git-utils
'@pnpm/matcher': link:../matcher
'@pnpm/npm-conf': 1.0.4
'@pnpm/pnpmfile': link:../pnpmfile
'@pnpm/read-project-manifest': link:../read-project-manifest
@@ -403,6 +407,7 @@ importers:
'@pnpm/error': workspace:3.0.1
'@pnpm/filter-lockfile': workspace:6.0.5
'@pnpm/get-context': workspace:6.1.3
'@pnpm/git-utils': workspace:0.0.0
'@pnpm/graph-sequencer': 1.0.0
'@pnpm/headless': workspace:18.1.11
'@pnpm/hoist': workspace:6.1.3
@@ -530,6 +535,7 @@ importers:
'@pnpm/cafs': link:../cafs
'@pnpm/client': link:../client
'@pnpm/core': 'link:'
'@pnpm/git-utils': link:../git-utils
'@pnpm/logger': 4.0.0
'@pnpm/package-store': link:../package-store
'@pnpm/prepare': link:../../privatePackages/prepare
@@ -1080,6 +1086,17 @@ importers:
'@types/semver': 7.3.9
is-windows: 1.0.2
packages/git-utils:
specifiers:
'@pnpm/git-utils': workspace:0.0.0
execa: npm:safe-execa@^0.1.1
tempy: ^1.0.0
dependencies:
execa: /safe-execa/0.1.1
devDependencies:
'@pnpm/git-utils': 'link:'
tempy: 1.0.1
packages/graceful-fs:
specifiers:
'@pnpm/graceful-fs': workspace:2.0.0
@@ -1379,6 +1396,7 @@ importers:
specifiers:
'@pnpm/constants': workspace:6.1.0
'@pnpm/error': workspace:3.0.1
'@pnpm/git-utils': workspace:0.0.0
'@pnpm/lockfile-file': workspace:5.0.4
'@pnpm/lockfile-types': workspace:4.0.2
'@pnpm/logger': ^4.0.0
@@ -1404,6 +1422,7 @@ importers:
dependencies:
'@pnpm/constants': link:../constants
'@pnpm/error': link:../error
'@pnpm/git-utils': link:../git-utils
'@pnpm/lockfile-types': link:../lockfile-types
'@pnpm/merge-lockfile-changes': link:../merge-lockfile-changes
'@pnpm/types': link:../types
@@ -2408,6 +2427,7 @@ importers:
'@pnpm/error': workspace:3.0.1
'@pnpm/exportable-manifest': workspace:3.0.3
'@pnpm/filter-workspace-packages': workspace:5.0.12
'@pnpm/git-utils': workspace:0.0.0
'@pnpm/lifecycle': workspace:13.0.4
'@pnpm/logger': ^4.0.0
'@pnpm/package-bins': workspace:6.0.2
@@ -2453,6 +2473,7 @@ importers:
'@pnpm/config': link:../config
'@pnpm/error': link:../error
'@pnpm/exportable-manifest': link:../exportable-manifest
'@pnpm/git-utils': link:../git-utils
'@pnpm/lifecycle': link:../lifecycle
'@pnpm/package-bins': link:../package-bins
'@pnpm/pick-registry-for-package': link:../pick-registry-for-package
@@ -8967,7 +8988,7 @@ packages:
pkg-dir: 4.2.0
/findup/0.1.5:
resolution: {integrity: sha512-Udxo3C9A6alt2GZ2MNsgnIvX7De0V3VGxeP/x98NSVgSlizcDHdmJza61LI7zJy4OEtSiJyE72s0/+tBl5/ZxA==}
resolution: {integrity: sha1-itkpozk7rGJ5V6fl3kYjsGsOLOs=}
engines: {node: '>=0.6'}
hasBin: true
dependencies:
@@ -9310,7 +9331,7 @@ packages:
dev: true
/github-from-package/0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
resolution: {integrity: sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=}
dev: true
/glob-parent/5.1.2:
@@ -11185,7 +11206,7 @@ packages:
dev: true
/manage-path/2.0.0:
resolution: {integrity: sha512-NJhyB+PJYTpxhxZJ3lecIGgh4kwIY2RAh44XvAz9UlqthlQwtPBf62uBVR8XaD8CRuSjQ6TnZH2lNJkbLPZM2A==}
resolution: {integrity: sha1-9M+EV7km7u4qg7FzUBQUvHbrlZc=}
dev: true
/map-age-cleaner/0.1.3:
@@ -11581,7 +11602,7 @@ packages:
dev: true
/mute-stream/0.0.7:
resolution: {integrity: sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==}
resolution: {integrity: sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=}
dev: true
/mv/2.1.1:
@@ -11996,7 +12017,7 @@ packages:
mimic-fn: 2.1.0
/opt-cli/1.5.1:
resolution: {integrity: sha512-iRFQBiQjXZ+LX/8pis04prUhS6FOYcJiZRouofN3rUJEB282b/e0s3jp9vT7aHgXY6TUpgPwu12f0i+qF40Kjw==}
resolution: {integrity: sha1-BNtEexPJa5kusxaFJm9O0NlzbcI=}
hasBin: true
dependencies:
commander: 2.9.0
@@ -13611,11 +13632,11 @@ packages:
engines: {node: '>=0.10.0'}
/spawn-command/0.0.2:
resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==}
resolution: {integrity: sha1-lUThpDygRfhTGqwaSMspva5iM44=}
dev: true
/spawn-command/0.0.2-1:
resolution: {integrity: sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==}
resolution: {integrity: sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=}
dev: true
/spawndamnit/2.0.0:
@@ -14091,7 +14112,7 @@ packages:
next-tick: 1.1.0
/tiny-each-async/2.0.3:
resolution: {integrity: sha512-5ROII7nElnAirvFn8g7H7MtpfV1daMcyfTGQwsn/x2VtyV+VPiO5CjReCJtWLvoKTDEDmZocf3cNPraiMnBXLA==}
resolution: {integrity: sha1-jru/1tYpXxNwAD+7NxYq/loKUdE=}
dev: false
/tinylogic/1.0.3:

View File

@@ -39,7 +39,7 @@ export interface Project {
*
* https://github.com/microsoft/TypeScript/pull/32695 might help with this.
*/
readLockfile: () => Promise<Required<RawLockfile>>
readLockfile: (lockfileName?: string) => Promise<Required<RawLockfile>>
writePackageJson: (pkgJson: object) => Promise<void>
}
@@ -143,9 +143,9 @@ export default (projectPath: string, encodedRegistryName?: string): Project => {
}
},
readModulesManifest: async () => readModules(modules),
async readLockfile () {
async readLockfile (lockfileName: string = WANTED_LOCKFILE) {
try {
return await readYamlFile(path.join(projectPath, WANTED_LOCKFILE)) // eslint-disable-line
return await readYamlFile(path.join(projectPath, lockfileName)) // eslint-disable-line
} catch (err: any) { // eslint-disable-line
if (err.code === 'ENOENT') return null!
throw err