mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-12 15:06:27 +00:00
refactor(manager): final strict null checks (#15185)
* refactor(manager): final strict null checks * refactor: fix type issues * test: fix mocking
This commit is contained in:
parent
1c0073d9a9
commit
868ebbef63
34 changed files with 337 additions and 254 deletions
|
@ -7,6 +7,8 @@ const lockFileContent = loadFixture('Gemfile.rubyci.lock');
|
||||||
describe('modules/manager/bundler/update-locked', () => {
|
describe('modules/manager/bundler/update-locked', () => {
|
||||||
it('detects already updated', () => {
|
it('detects already updated', () => {
|
||||||
const config: UpdateLockedConfig = {
|
const config: UpdateLockedConfig = {
|
||||||
|
packageFile: 'Gemfile',
|
||||||
|
lockFile: 'Gemfile.lock',
|
||||||
lockFileContent,
|
lockFileContent,
|
||||||
depName: 'activejob',
|
depName: 'activejob',
|
||||||
newVersion: '5.2.3',
|
newVersion: '5.2.3',
|
||||||
|
@ -16,6 +18,8 @@ describe('modules/manager/bundler/update-locked', () => {
|
||||||
|
|
||||||
it('returns unsupported', () => {
|
it('returns unsupported', () => {
|
||||||
const config: UpdateLockedConfig = {
|
const config: UpdateLockedConfig = {
|
||||||
|
packageFile: 'Gemfile',
|
||||||
|
lockFile: 'Gemfile.lock',
|
||||||
lockFileContent,
|
lockFileContent,
|
||||||
depName: 'activejob',
|
depName: 'activejob',
|
||||||
newVersion: '5.2.0',
|
newVersion: '5.2.0',
|
||||||
|
|
|
@ -2,11 +2,15 @@ import { loadFixture } from '../../../../test/util';
|
||||||
import type { UpdateLockedConfig } from '../types';
|
import type { UpdateLockedConfig } from '../types';
|
||||||
import { updateLockedDependency } from '.';
|
import { updateLockedDependency } from '.';
|
||||||
|
|
||||||
|
const lockFile = 'compose.lock';
|
||||||
|
|
||||||
const lockFileContent = loadFixture('composer5.lock');
|
const lockFileContent = loadFixture('composer5.lock');
|
||||||
|
|
||||||
describe('modules/manager/composer/update-locked', () => {
|
describe('modules/manager/composer/update-locked', () => {
|
||||||
it('detects already updated', () => {
|
it('detects already updated', () => {
|
||||||
const config: UpdateLockedConfig = {
|
const config: UpdateLockedConfig = {
|
||||||
|
packageFile: 'composer.json',
|
||||||
|
lockFile,
|
||||||
lockFileContent,
|
lockFileContent,
|
||||||
depName: 'awesome/git',
|
depName: 'awesome/git',
|
||||||
newVersion: '1.2.0',
|
newVersion: '1.2.0',
|
||||||
|
@ -16,6 +20,8 @@ describe('modules/manager/composer/update-locked', () => {
|
||||||
|
|
||||||
it('returns unsupported', () => {
|
it('returns unsupported', () => {
|
||||||
const config: UpdateLockedConfig = {
|
const config: UpdateLockedConfig = {
|
||||||
|
packageFile: 'composer.json',
|
||||||
|
lockFile,
|
||||||
lockFileContent,
|
lockFileContent,
|
||||||
depName: 'awesome/git',
|
depName: 'awesome/git',
|
||||||
newVersion: '1.0.0',
|
newVersion: '1.0.0',
|
||||||
|
|
|
@ -17,7 +17,7 @@ const languageList = Object.values(ProgrammingLanguage);
|
||||||
export function get<T extends keyof ManagerApi>(
|
export function get<T extends keyof ManagerApi>(
|
||||||
manager: string,
|
manager: string,
|
||||||
name: T
|
name: T
|
||||||
): ManagerApi[T] | null {
|
): ManagerApi[T] | undefined {
|
||||||
return managers.get(manager)?.[name];
|
return managers.get(manager)?.[name];
|
||||||
}
|
}
|
||||||
export const getLanguageList = (): string[] => languageList;
|
export const getLanguageList = (): string[] => languageList;
|
||||||
|
@ -27,7 +27,7 @@ export const getManagers = (): Map<string, ManagerApi> => managers;
|
||||||
export async function detectAllGlobalConfig(): Promise<GlobalManagerConfig> {
|
export async function detectAllGlobalConfig(): Promise<GlobalManagerConfig> {
|
||||||
let config: GlobalManagerConfig = {};
|
let config: GlobalManagerConfig = {};
|
||||||
for (const managerName of managerList) {
|
for (const managerName of managerList) {
|
||||||
const manager = managers.get(managerName);
|
const manager = managers.get(managerName)!;
|
||||||
if (manager.detectGlobalConfig) {
|
if (manager.detectGlobalConfig) {
|
||||||
// This should use mergeChildConfig once more than one manager is supported, but introduces a cyclic dependency
|
// This should use mergeChildConfig once more than one manager is supported, but introduces a cyclic dependency
|
||||||
config = { ...config, ...(await manager.detectGlobalConfig()) };
|
config = { ...config, ...(await manager.detectGlobalConfig()) };
|
||||||
|
@ -44,7 +44,7 @@ export async function extractAllPackageFiles(
|
||||||
if (!managers.has(manager)) {
|
if (!managers.has(manager)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const m = managers.get(manager);
|
const m = managers.get(manager)!;
|
||||||
if (m.extractAllPackageFiles) {
|
if (m.extractAllPackageFiles) {
|
||||||
const res = await m.extractAllPackageFiles(config, files);
|
const res = await m.extractAllPackageFiles(config, files);
|
||||||
// istanbul ignore if
|
// istanbul ignore if
|
||||||
|
@ -65,18 +65,18 @@ export function extractPackageFile(
|
||||||
if (!managers.has(manager)) {
|
if (!managers.has(manager)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const m = managers.get(manager);
|
const m = managers.get(manager)!;
|
||||||
return m.extractPackageFile
|
return m.extractPackageFile
|
||||||
? m.extractPackageFile(content, fileName, config)
|
? m.extractPackageFile(content, fileName, config)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRangeStrategy(config: RangeConfig): RangeStrategy {
|
export function getRangeStrategy(config: RangeConfig): RangeStrategy | null {
|
||||||
const { manager, rangeStrategy } = config;
|
const { manager, rangeStrategy } = config;
|
||||||
if (!managers.has(manager)) {
|
if (!manager || !managers.has(manager)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const m = managers.get(manager);
|
const m = managers.get(manager)!;
|
||||||
if (m.getRangeStrategy) {
|
if (m.getRangeStrategy) {
|
||||||
// Use manager's own function if it exists
|
// Use manager's own function if it exists
|
||||||
const managerRangeStrategy = m.getRangeStrategy(config);
|
const managerRangeStrategy = m.getRangeStrategy(config);
|
||||||
|
|
|
@ -66,7 +66,7 @@ export async function extractPackageFile(
|
||||||
`npm file ${fileName} has name ${JSON.stringify(packageJsonName)}`
|
`npm file ${fileName} has name ${JSON.stringify(packageJsonName)}`
|
||||||
);
|
);
|
||||||
const packageFileVersion = packageJson.version;
|
const packageFileVersion = packageJson.version;
|
||||||
let yarnWorkspacesPackages: string[];
|
let yarnWorkspacesPackages: string[] | undefined;
|
||||||
if (is.array(packageJson.workspaces)) {
|
if (is.array(packageJson.workspaces)) {
|
||||||
yarnWorkspacesPackages = packageJson.workspaces;
|
yarnWorkspacesPackages = packageJson.workspaces;
|
||||||
} else {
|
} else {
|
||||||
|
@ -83,7 +83,10 @@ export async function extractPackageFile(
|
||||||
pnpmShrinkwrap: 'pnpm-lock.yaml',
|
pnpmShrinkwrap: 'pnpm-lock.yaml',
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const [key, val] of Object.entries(lockFiles)) {
|
for (const [key, val] of Object.entries(lockFiles) as [
|
||||||
|
'yarnLock' | 'packageLock' | 'shrinkwrapJson' | 'pnpmShrinkwrap',
|
||||||
|
string
|
||||||
|
][]) {
|
||||||
const filePath = getSiblingFileName(fileName, val);
|
const filePath = getSiblingFileName(fileName, val);
|
||||||
if (await readLocalFile(filePath, 'utf8')) {
|
if (await readLocalFile(filePath, 'utf8')) {
|
||||||
lockFiles[key] = filePath;
|
lockFiles[key] = filePath;
|
||||||
|
@ -95,7 +98,7 @@ export async function extractPackageFile(
|
||||||
delete lockFiles.packageLock;
|
delete lockFiles.packageLock;
|
||||||
delete lockFiles.shrinkwrapJson;
|
delete lockFiles.shrinkwrapJson;
|
||||||
|
|
||||||
let npmrc: string;
|
let npmrc: string | undefined;
|
||||||
const npmrcFileName = getSiblingFileName(fileName, '.npmrc');
|
const npmrcFileName = getSiblingFileName(fileName, '.npmrc');
|
||||||
let repoNpmrc = await readLocalFile(npmrcFileName, 'utf8');
|
let repoNpmrc = await readLocalFile(npmrcFileName, 'utf8');
|
||||||
if (is.string(repoNpmrc)) {
|
if (is.string(repoNpmrc)) {
|
||||||
|
@ -135,15 +138,17 @@ export async function extractPackageFile(
|
||||||
const yarnrcYmlFileName = getSiblingFileName(fileName, '.yarnrc.yml');
|
const yarnrcYmlFileName = getSiblingFileName(fileName, '.yarnrc.yml');
|
||||||
const yarnZeroInstall = await isZeroInstall(yarnrcYmlFileName);
|
const yarnZeroInstall = await isZeroInstall(yarnrcYmlFileName);
|
||||||
|
|
||||||
let lernaJsonFile: string;
|
let lernaJsonFile: string | undefined;
|
||||||
let lernaPackages: string[];
|
let lernaPackages: string[] | undefined;
|
||||||
let lernaClient: 'yarn' | 'npm';
|
let lernaClient: 'yarn' | 'npm' | undefined;
|
||||||
let hasFancyRefs = false;
|
let hasFancyRefs = false;
|
||||||
let lernaJson: {
|
let lernaJson:
|
||||||
|
| {
|
||||||
packages: string[];
|
packages: string[];
|
||||||
npmClient: string;
|
npmClient: string;
|
||||||
useWorkspaces?: boolean;
|
useWorkspaces?: boolean;
|
||||||
};
|
}
|
||||||
|
| undefined;
|
||||||
try {
|
try {
|
||||||
lernaJsonFile = getSiblingFileName(fileName, 'lerna.json');
|
lernaJsonFile = getSiblingFileName(fileName, 'lerna.json');
|
||||||
lernaJson = JSON.parse(await readLocalFile(lernaJsonFile, 'utf8'));
|
lernaJson = JSON.parse(await readLocalFile(lernaJsonFile, 'utf8'));
|
||||||
|
@ -332,14 +337,16 @@ export async function extractPackageFile(
|
||||||
return dep;
|
return dep;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const depType of Object.keys(depTypes)) {
|
for (const depType of Object.keys(depTypes) as (keyof typeof depTypes)[]) {
|
||||||
let dependencies = packageJson[depType];
|
let dependencies = packageJson[depType];
|
||||||
if (dependencies) {
|
if (dependencies) {
|
||||||
try {
|
try {
|
||||||
if (depType === 'packageManager') {
|
if (depType === 'packageManager') {
|
||||||
const match = regEx('^(?<name>.+)@(?<range>.+)$').exec(dependencies);
|
const match = regEx('^(?<name>.+)@(?<range>.+)$').exec(
|
||||||
|
dependencies as string
|
||||||
|
);
|
||||||
// istanbul ignore next
|
// istanbul ignore next
|
||||||
if (!match) {
|
if (!match?.groups) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dependencies = { [match.groups.name]: match.groups.range };
|
dependencies = { [match.groups.name]: match.groups.range };
|
||||||
|
@ -446,6 +453,6 @@ export async function extractAllPackageFiles(
|
||||||
logger.debug({ packageFile }, 'packageFile has no content');
|
logger.debug({ packageFile }, 'packageFile has no content');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await postExtract(npmFiles, config.updateInternalDeps);
|
await postExtract(npmFiles, !!config.updateInternalDeps);
|
||||||
return npmFiles;
|
return npmFiles;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ export async function getLockedVersions(
|
||||||
logger.debug('Finding locked versions');
|
logger.debug('Finding locked versions');
|
||||||
for (const packageFile of packageFiles) {
|
for (const packageFile of packageFiles) {
|
||||||
const { yarnLock, npmLock, pnpmShrinkwrap } = packageFile;
|
const { yarnLock, npmLock, pnpmShrinkwrap } = packageFile;
|
||||||
const lockFiles = [];
|
const lockFiles: string[] = [];
|
||||||
if (yarnLock) {
|
if (yarnLock) {
|
||||||
logger.trace('Found yarnLock');
|
logger.trace('Found yarnLock');
|
||||||
lockFiles.push(yarnLock);
|
lockFiles.push(yarnLock);
|
||||||
|
@ -22,14 +22,17 @@ export async function getLockedVersions(
|
||||||
}
|
}
|
||||||
const { lockfileVersion, isYarn1 } = lockFileCache[yarnLock];
|
const { lockfileVersion, isYarn1 } = lockFileCache[yarnLock];
|
||||||
if (!isYarn1) {
|
if (!isYarn1) {
|
||||||
if (lockfileVersion >= 8) {
|
if (lockfileVersion && lockfileVersion >= 8) {
|
||||||
// https://github.com/yarnpkg/berry/commit/9bcd27ae34aee77a567dd104947407532fa179b3
|
// https://github.com/yarnpkg/berry/commit/9bcd27ae34aee77a567dd104947407532fa179b3
|
||||||
packageFile.constraints.yarn = '^3.0.0';
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
} else if (lockfileVersion >= 6) {
|
packageFile.constraints!.yarn = '^3.0.0';
|
||||||
|
} else if (lockfileVersion && lockfileVersion >= 6) {
|
||||||
// https://github.com/yarnpkg/berry/commit/f753790380cbda5b55d028ea84b199445129f9ba
|
// https://github.com/yarnpkg/berry/commit/f753790380cbda5b55d028ea84b199445129f9ba
|
||||||
packageFile.constraints.yarn = '^2.2.0';
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
packageFile.constraints!.yarn = '^2.2.0';
|
||||||
} else {
|
} else {
|
||||||
packageFile.constraints.yarn = '^2.0.0';
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
packageFile.constraints!.yarn = '^2.0.0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const dep of packageFile.deps) {
|
for (const dep of packageFile.deps) {
|
||||||
|
@ -54,18 +57,20 @@ export async function getLockedVersions(
|
||||||
}
|
}
|
||||||
const { lockfileVersion } = lockFileCache[npmLock];
|
const { lockfileVersion } = lockFileCache[npmLock];
|
||||||
if (lockfileVersion === 1) {
|
if (lockfileVersion === 1) {
|
||||||
if (packageFile.constraints.npm) {
|
if (packageFile.constraints?.npm) {
|
||||||
// Add a <7 constraint if it's not already a fixed version
|
// Add a <7 constraint if it's not already a fixed version
|
||||||
if (!semver.valid(packageFile.constraints.npm)) {
|
if (!semver.valid(packageFile.constraints.npm)) {
|
||||||
packageFile.constraints.npm += ' <7';
|
packageFile.constraints.npm += ' <7';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
packageFile.constraints.npm = '<7';
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
packageFile.constraints!.npm = '<7';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const dep of packageFile.deps) {
|
for (const dep of packageFile.deps) {
|
||||||
dep.lockedVersion = semver.valid(
|
dep.lockedVersion = semver.valid(
|
||||||
lockFileCache[npmLock].lockedVersions[dep.depName]
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
lockFileCache[npmLock].lockedVersions[dep.depName!]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (pnpmShrinkwrap) {
|
} else if (pnpmShrinkwrap) {
|
||||||
|
|
|
@ -28,10 +28,13 @@ export async function detectMonorepos(
|
||||||
if (packages?.length) {
|
if (packages?.length) {
|
||||||
const internalPackagePatterns = (
|
const internalPackagePatterns = (
|
||||||
is.array(packages) ? packages : [packages]
|
is.array(packages) ? packages : [packages]
|
||||||
).map((pattern) => getSiblingFileName(packageFile, pattern));
|
)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
.map((pattern) => getSiblingFileName(packageFile!, pattern));
|
||||||
const internalPackageFiles = packageFiles.filter((sp) =>
|
const internalPackageFiles = packageFiles.filter((sp) =>
|
||||||
matchesAnyPattern(
|
matchesAnyPattern(
|
||||||
getSubDirectory(sp.packageFile),
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
getSubDirectory(sp.packageFile!),
|
||||||
internalPackagePatterns
|
internalPackagePatterns
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,7 +24,7 @@ describe('modules/manager/npm/extract/pnpm', () => {
|
||||||
'..'
|
'..'
|
||||||
);
|
);
|
||||||
const res = await extractPnpmFilters(workSpaceFilePath);
|
const res = await extractPnpmFilters(workSpaceFilePath);
|
||||||
expect(res).toBeNull();
|
expect(res).toBeUndefined();
|
||||||
expect(logger.logger.trace).toHaveBeenCalledWith(
|
expect(logger.logger.trace).toHaveBeenCalledWith(
|
||||||
{
|
{
|
||||||
fileName: expect.any(String),
|
fileName: expect.any(String),
|
||||||
|
@ -39,7 +39,7 @@ describe('modules/manager/npm/extract/pnpm', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await extractPnpmFilters('pnpm-workspace.yml');
|
const res = await extractPnpmFilters('pnpm-workspace.yml');
|
||||||
expect(res).toBeNull();
|
expect(res).toBeUndefined();
|
||||||
expect(logger.logger.trace).toHaveBeenCalledWith(
|
expect(logger.logger.trace).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
fileName: expect.any(String),
|
fileName: expect.any(String),
|
||||||
|
|
|
@ -15,7 +15,7 @@ import type { PnpmWorkspaceFile } from './types';
|
||||||
|
|
||||||
export async function extractPnpmFilters(
|
export async function extractPnpmFilters(
|
||||||
fileName: string
|
fileName: string
|
||||||
): Promise<string[] | null> {
|
): Promise<string[] | undefined> {
|
||||||
try {
|
try {
|
||||||
const contents = load(await readLocalFile(fileName, 'utf8'), {
|
const contents = load(await readLocalFile(fileName, 'utf8'), {
|
||||||
json: true,
|
json: true,
|
||||||
|
@ -28,12 +28,12 @@ export async function extractPnpmFilters(
|
||||||
{ fileName },
|
{ fileName },
|
||||||
'Failed to find required "packages" array in pnpm-workspace.yaml'
|
'Failed to find required "packages" array in pnpm-workspace.yaml'
|
||||||
);
|
);
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
return contents.packages;
|
return contents.packages;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.trace({ fileName, err }, 'Failed to parse pnpm-workspace.yaml');
|
logger.trace({ fileName, err }, 'Failed to parse pnpm-workspace.yaml');
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,8 @@ export async function detectPnpmWorkspaces(
|
||||||
}
|
}
|
||||||
|
|
||||||
// search for corresponding pnpm workspace
|
// search for corresponding pnpm workspace
|
||||||
const pnpmWorkspace = await findPnpmWorkspace(packageFile);
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
const pnpmWorkspace = await findPnpmWorkspace(packageFile!);
|
||||||
if (pnpmWorkspace === null) {
|
if (pnpmWorkspace === null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +114,8 @@ export async function detectPnpmWorkspaces(
|
||||||
const packagePaths = packagePathCache.get(workspaceYamlPath);
|
const packagePaths = packagePathCache.get(workspaceYamlPath);
|
||||||
|
|
||||||
const isPackageInWorkspace = packagePaths?.some((p) =>
|
const isPackageInWorkspace = packagePaths?.some((p) =>
|
||||||
p.endsWith(packageFile)
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
p.endsWith(packageFile!)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isPackageInWorkspace) {
|
if (isPackageInWorkspace) {
|
||||||
|
|
|
@ -13,6 +13,8 @@ export interface NpmPackage extends PackageJson {
|
||||||
_id?: any;
|
_id?: any;
|
||||||
dependenciesMeta?: DependenciesMeta;
|
dependenciesMeta?: DependenciesMeta;
|
||||||
packageManager?: string;
|
packageManager?: string;
|
||||||
|
|
||||||
|
volta?: PackageJson.Dependency;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LockFileEntry = Record<
|
export type LockFileEntry = Record<
|
||||||
|
|
|
@ -14,7 +14,7 @@ export async function getYarnLock(filePath: string): Promise<LockFile> {
|
||||||
try {
|
try {
|
||||||
const parsed = parseSyml(yarnLockRaw);
|
const parsed = parseSyml(yarnLockRaw);
|
||||||
const lockedVersions: Record<string, string> = {};
|
const lockedVersions: Record<string, string> = {};
|
||||||
let lockfileVersion: number;
|
let lockfileVersion: number | undefined;
|
||||||
|
|
||||||
for (const [key, val] of Object.entries(parsed)) {
|
for (const [key, val] of Object.entries(parsed)) {
|
||||||
if (key === '__metadata') {
|
if (key === '__metadata') {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { ensureTrailingSlash } from '../../../../util/url';
|
||||||
import { NpmDatasource } from '../../../datasource/npm';
|
import { NpmDatasource } from '../../../datasource/npm';
|
||||||
import type { PackageFile, PostUpdateConfig, Upgrade } from '../../types';
|
import type { PackageFile, PostUpdateConfig, Upgrade } from '../../types';
|
||||||
import { getZeroInstallPaths } from '../extract/yarn';
|
import { getZeroInstallPaths } from '../extract/yarn';
|
||||||
|
import type { NpmDepType } from '../types';
|
||||||
import { composeLockFile, parseLockFile } from '../utils';
|
import { composeLockFile, parseLockFile } from '../utils';
|
||||||
import * as lerna from './lerna';
|
import * as lerna from './lerna';
|
||||||
import * as npm from './npm';
|
import * as npm from './npm';
|
||||||
|
@ -44,18 +45,18 @@ import * as yarn from './yarn';
|
||||||
|
|
||||||
// Strips empty values, deduplicates, and returns the directories from filenames
|
// Strips empty values, deduplicates, and returns the directories from filenames
|
||||||
// istanbul ignore next
|
// istanbul ignore next
|
||||||
const getDirs = (arr: string[]): string[] =>
|
const getDirs = (arr: (string | null | undefined)[]): string[] =>
|
||||||
Array.from(new Set(arr.filter(Boolean)));
|
Array.from(new Set(arr.filter(is.string)));
|
||||||
|
|
||||||
// istanbul ignore next
|
// istanbul ignore next
|
||||||
export function determineLockFileDirs(
|
export function determineLockFileDirs(
|
||||||
config: PostUpdateConfig,
|
config: PostUpdateConfig,
|
||||||
packageFiles: AdditionalPackageFiles
|
packageFiles: AdditionalPackageFiles
|
||||||
): DetermineLockFileDirsResult {
|
): DetermineLockFileDirsResult {
|
||||||
const npmLockDirs = [];
|
const npmLockDirs: (string | undefined)[] = [];
|
||||||
const yarnLockDirs = [];
|
const yarnLockDirs: (string | undefined)[] = [];
|
||||||
const pnpmShrinkwrapDirs = [];
|
const pnpmShrinkwrapDirs: (string | undefined)[] = [];
|
||||||
const lernaJsonFiles = [];
|
const lernaJsonFiles: (string | undefined)[] = [];
|
||||||
|
|
||||||
for (const upgrade of config.upgrades) {
|
for (const upgrade of config.upgrades) {
|
||||||
if (upgrade.updateType === 'lockFileMaintenance' || upgrade.isRemediation) {
|
if (upgrade.updateType === 'lockFileMaintenance' || upgrade.isRemediation) {
|
||||||
|
@ -91,7 +92,8 @@ export function determineLockFileDirs(
|
||||||
|
|
||||||
function getPackageFile(fileName: string): Partial<PackageFile> {
|
function getPackageFile(fileName: string): Partial<PackageFile> {
|
||||||
logger.trace('Looking for packageFile: ' + fileName);
|
logger.trace('Looking for packageFile: ' + fileName);
|
||||||
for (const packageFile of packageFiles.npm) {
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
for (const packageFile of packageFiles.npm!) {
|
||||||
if (packageFile.packageFile === fileName) {
|
if (packageFile.packageFile === fileName) {
|
||||||
logger.trace({ packageFile }, 'Found packageFile');
|
logger.trace({ packageFile }, 'Found packageFile');
|
||||||
return packageFile;
|
return packageFile;
|
||||||
|
@ -101,7 +103,8 @@ export function determineLockFileDirs(
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const p of config.updatedPackageFiles) {
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
for (const p of config.updatedPackageFiles!) {
|
||||||
logger.trace(`Checking ${String(p.path)} for lock files`);
|
logger.trace(`Checking ${String(p.path)} for lock files`);
|
||||||
const packageFile = getPackageFile(p.path);
|
const packageFile = getPackageFile(p.path);
|
||||||
// lerna first
|
// lerna first
|
||||||
|
@ -147,7 +150,8 @@ export async function writeExistingFiles(
|
||||||
for (const packageFile of npmFiles) {
|
for (const packageFile of npmFiles) {
|
||||||
const basedir = upath.join(
|
const basedir = upath.join(
|
||||||
localDir,
|
localDir,
|
||||||
upath.dirname(packageFile.packageFile)
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
upath.dirname(packageFile.packageFile!)
|
||||||
);
|
);
|
||||||
const npmrc: string = packageFile.npmrc || config.npmrc;
|
const npmrc: string = packageFile.npmrc || config.npmrc;
|
||||||
const npmrcFilename = upath.join(basedir, '.npmrc');
|
const npmrcFilename = upath.join(basedir, '.npmrc');
|
||||||
|
@ -171,9 +175,10 @@ export async function writeExistingFiles(
|
||||||
logger.debug(`Writing ${npmLock}`);
|
logger.debug(`Writing ${npmLock}`);
|
||||||
let existingNpmLock: string;
|
let existingNpmLock: string;
|
||||||
try {
|
try {
|
||||||
existingNpmLock = await getFile(npmLock);
|
existingNpmLock = (await getFile(npmLock)) ?? '';
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn({ err }, 'Error reading npm lock file');
|
logger.warn({ err }, 'Error reading npm lock file');
|
||||||
|
existingNpmLock = '';
|
||||||
}
|
}
|
||||||
const { detectedIndent, lockFileParsed: npmLockParsed } =
|
const { detectedIndent, lockFileParsed: npmLockParsed } =
|
||||||
parseLockFile(existingNpmLock);
|
parseLockFile(existingNpmLock);
|
||||||
|
@ -182,14 +187,15 @@ export async function writeExistingFiles(
|
||||||
'packages' in npmLockParsed
|
'packages' in npmLockParsed
|
||||||
? Object.keys(npmLockParsed.packages)
|
? Object.keys(npmLockParsed.packages)
|
||||||
: [];
|
: [];
|
||||||
const widens = [];
|
const widens: string[] = [];
|
||||||
let lockFileChanged = false;
|
let lockFileChanged = false;
|
||||||
for (const upgrade of config.upgrades) {
|
for (const upgrade of config.upgrades) {
|
||||||
if (
|
if (
|
||||||
upgrade.rangeStrategy === 'widen' &&
|
upgrade.rangeStrategy === 'widen' &&
|
||||||
upgrade.npmLock === npmLock
|
upgrade.npmLock === npmLock
|
||||||
) {
|
) {
|
||||||
widens.push(upgrade.depName);
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
widens.push(upgrade.depName!);
|
||||||
}
|
}
|
||||||
const { depName } = upgrade;
|
const { depName } = upgrade;
|
||||||
for (const packageName of packageNames) {
|
for (const packageName of packageNames) {
|
||||||
|
@ -215,7 +221,8 @@ export async function writeExistingFiles(
|
||||||
npmLockParsed.dependencies
|
npmLockParsed.dependencies
|
||||||
) {
|
) {
|
||||||
widens.forEach((depName) => {
|
widens.forEach((depName) => {
|
||||||
delete npmLockParsed.dependencies[depName];
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
delete npmLockParsed.dependencies![depName];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -284,9 +291,12 @@ export async function writeUpdatedPackageFiles(
|
||||||
});
|
});
|
||||||
for (const upgrade of config.upgrades) {
|
for (const upgrade of config.upgrades) {
|
||||||
if (upgrade.gitRef && upgrade.packageFile === packageFile.path) {
|
if (upgrade.gitRef && upgrade.packageFile === packageFile.path) {
|
||||||
massagedFile[upgrade.depType][upgrade.depName] = massagedFile[
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
upgrade.depType
|
massagedFile[upgrade.depType as NpmDepType][upgrade.depName!] =
|
||||||
][upgrade.depName].replace(
|
massagedFile[
|
||||||
|
upgrade.depType as NpmDepType
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
][upgrade.depName!].replace(
|
||||||
'git+https://github.com',
|
'git+https://github.com',
|
||||||
`git+https://${token}@github.com`
|
`git+https://${token}@github.com`
|
||||||
);
|
);
|
||||||
|
@ -305,7 +315,7 @@ export async function writeUpdatedPackageFiles(
|
||||||
// istanbul ignore next
|
// istanbul ignore next
|
||||||
async function getNpmrcContent(dir: string): Promise<string | null> {
|
async function getNpmrcContent(dir: string): Promise<string | null> {
|
||||||
const npmrcFilePath = upath.join(dir, '.npmrc');
|
const npmrcFilePath = upath.join(dir, '.npmrc');
|
||||||
let originalNpmrcContent = null;
|
let originalNpmrcContent: string | null = null;
|
||||||
try {
|
try {
|
||||||
originalNpmrcContent = await readFile(npmrcFilePath, 'utf8');
|
originalNpmrcContent = await readFile(npmrcFilePath, 'utf8');
|
||||||
logger.debug('npmrc file found in repository');
|
logger.debug('npmrc file found in repository');
|
||||||
|
@ -320,7 +330,7 @@ async function getNpmrcContent(dir: string): Promise<string | null> {
|
||||||
// istanbul ignore next
|
// istanbul ignore next
|
||||||
async function updateNpmrcContent(
|
async function updateNpmrcContent(
|
||||||
dir: string,
|
dir: string,
|
||||||
originalContent: string,
|
originalContent: string | null,
|
||||||
additionalLines: string[]
|
additionalLines: string[]
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const npmrcFilePath = upath.join(dir, '.npmrc');
|
const npmrcFilePath = upath.join(dir, '.npmrc');
|
||||||
|
@ -341,7 +351,7 @@ async function updateNpmrcContent(
|
||||||
// istanbul ignore next
|
// istanbul ignore next
|
||||||
async function resetNpmrcContent(
|
async function resetNpmrcContent(
|
||||||
dir: string,
|
dir: string,
|
||||||
originalContent: string
|
originalContent: string | null
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const npmrcFilePath = upath.join(dir, '.npmrc');
|
const npmrcFilePath = upath.join(dir, '.npmrc');
|
||||||
if (originalContent) {
|
if (originalContent) {
|
||||||
|
@ -422,7 +432,7 @@ export async function updateYarnBinary(
|
||||||
let yarnrcYml = existingYarnrcYmlContent;
|
let yarnrcYml = existingYarnrcYmlContent;
|
||||||
try {
|
try {
|
||||||
const yarnrcYmlFilename = upath.join(lockFileDir, '.yarnrc.yml');
|
const yarnrcYmlFilename = upath.join(lockFileDir, '.yarnrc.yml');
|
||||||
yarnrcYml ||= await getFile(yarnrcYmlFilename);
|
yarnrcYml ||= (await getFile(yarnrcYmlFilename)) ?? undefined;
|
||||||
const newYarnrcYml = await readLocalFile(yarnrcYmlFilename, 'utf8');
|
const newYarnrcYml = await readLocalFile(yarnrcYmlFilename, 'utf8');
|
||||||
if (!is.string(yarnrcYml) || !is.string(newYarnrcYml)) {
|
if (!is.string(yarnrcYml) || !is.string(newYarnrcYml)) {
|
||||||
return existingYarnrcYmlContent;
|
return existingYarnrcYmlContent;
|
||||||
|
@ -518,7 +528,7 @@ export async function getAdditionalFiles(
|
||||||
|
|
||||||
let token = '';
|
let token = '';
|
||||||
try {
|
try {
|
||||||
({ token } = hostRules.find({
|
({ token = '' } = hostRules.find({
|
||||||
hostType: config.platform,
|
hostType: config.platform,
|
||||||
url: 'https://api.github.com/',
|
url: 'https://api.github.com/',
|
||||||
}));
|
}));
|
||||||
|
@ -527,7 +537,7 @@ export async function getAdditionalFiles(
|
||||||
logger.warn({ err }, 'Error getting token for packageFile');
|
logger.warn({ err }, 'Error getting token for packageFile');
|
||||||
}
|
}
|
||||||
const tokenRe = regEx(`${token}`, 'g', false);
|
const tokenRe = regEx(`${token}`, 'g', false);
|
||||||
const { localDir } = GlobalConfig.get();
|
const localDir = GlobalConfig.get('localDir')!;
|
||||||
for (const npmLock of dirs.npmLockDirs) {
|
for (const npmLock of dirs.npmLockDirs) {
|
||||||
const lockFileDir = upath.dirname(npmLock);
|
const lockFileDir = upath.dirname(npmLock);
|
||||||
const fullLockFileDir = upath.join(localDir, lockFileDir);
|
const fullLockFileDir = upath.join(localDir, lockFileDir);
|
||||||
|
@ -585,7 +595,9 @@ export async function getAdditionalFiles(
|
||||||
updatedArtifacts.push({
|
updatedArtifacts.push({
|
||||||
type: 'addition',
|
type: 'addition',
|
||||||
path: npmLock,
|
path: npmLock,
|
||||||
contents: res.lockFile.replace(tokenRe, ''),
|
// TODO: can this be undefined?
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
contents: res.lockFile!.replace(tokenRe, ''),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -601,8 +613,8 @@ export async function getAdditionalFiles(
|
||||||
npmrcContent,
|
npmrcContent,
|
||||||
additionalNpmrcContent
|
additionalNpmrcContent
|
||||||
);
|
);
|
||||||
let yarnRcYmlFilename: string;
|
let yarnRcYmlFilename: string | undefined;
|
||||||
let existingYarnrcYmlContent: string;
|
let existingYarnrcYmlContent: string | undefined;
|
||||||
if (additionalYarnRcYml) {
|
if (additionalYarnRcYml) {
|
||||||
yarnRcYmlFilename = getSiblingFileName(yarnLock, '.yarnrc.yml');
|
yarnRcYmlFilename = getSiblingFileName(yarnLock, '.yarnrc.yml');
|
||||||
existingYarnrcYmlContent = await readLocalFile(yarnRcYmlFilename, 'utf8');
|
existingYarnrcYmlContent = await readLocalFile(yarnRcYmlFilename, 'utf8');
|
||||||
|
@ -674,7 +686,9 @@ export async function getAdditionalFiles(
|
||||||
updatedArtifacts.push({
|
updatedArtifacts.push({
|
||||||
type: 'addition',
|
type: 'addition',
|
||||||
path: lockFileName,
|
path: lockFileName,
|
||||||
contents: res.lockFile,
|
//
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
contents: res.lockFile!,
|
||||||
});
|
});
|
||||||
await updateYarnOffline(lockFileDir, localDir, updatedArtifacts);
|
await updateYarnOffline(lockFileDir, localDir, updatedArtifacts);
|
||||||
}
|
}
|
||||||
|
@ -689,7 +703,8 @@ export async function getAdditionalFiles(
|
||||||
}
|
}
|
||||||
await resetNpmrcContent(fullLockFileDir, npmrcContent);
|
await resetNpmrcContent(fullLockFileDir, npmrcContent);
|
||||||
if (existingYarnrcYmlContent) {
|
if (existingYarnrcYmlContent) {
|
||||||
await writeLocalFile(yarnRcYmlFilename, existingYarnrcYmlContent);
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
await writeLocalFile(yarnRcYmlFilename!, existingYarnrcYmlContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,7 +765,9 @@ export async function getAdditionalFiles(
|
||||||
updatedArtifacts.push({
|
updatedArtifacts.push({
|
||||||
type: 'addition',
|
type: 'addition',
|
||||||
path: pnpmShrinkwrap,
|
path: pnpmShrinkwrap,
|
||||||
contents: res.lockFile,
|
// TODO: can be undefined?
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
contents: res.lockFile!,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -761,7 +778,8 @@ export async function getAdditionalFiles(
|
||||||
let lockFile: string;
|
let lockFile: string;
|
||||||
logger.debug(`Finding package.json for lerna location "${lernaJsonFile}"`);
|
logger.debug(`Finding package.json for lerna location "${lernaJsonFile}"`);
|
||||||
const lernaPackageFile = packageFiles.npm.find(
|
const lernaPackageFile = packageFiles.npm.find(
|
||||||
(p) => getSubDirectory(p.packageFile) === getSubDirectory(lernaJsonFile)
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
(p) => getSubDirectory(p.packageFile!) === getSubDirectory(lernaJsonFile)
|
||||||
);
|
);
|
||||||
if (!lernaPackageFile) {
|
if (!lernaPackageFile) {
|
||||||
logger.debug('No matching package.json found');
|
logger.debug('No matching package.json found');
|
||||||
|
@ -842,7 +860,8 @@ export async function getAdditionalFiles(
|
||||||
const filename = packageFile.npmLock || packageFile.yarnLock;
|
const filename = packageFile.npmLock || packageFile.yarnLock;
|
||||||
logger.trace('Checking for ' + filename);
|
logger.trace('Checking for ' + filename);
|
||||||
const existingContent = await getFile(
|
const existingContent = await getFile(
|
||||||
filename,
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
filename!,
|
||||||
config.reuseExistingBranch ? config.branchName : config.baseBranch
|
config.reuseExistingBranch ? config.branchName : config.baseBranch
|
||||||
);
|
);
|
||||||
if (existingContent) {
|
if (existingContent) {
|
||||||
|
@ -868,7 +887,8 @@ export async function getAdditionalFiles(
|
||||||
logger.debug('File is updated: ' + lockFilePath);
|
logger.debug('File is updated: ' + lockFilePath);
|
||||||
updatedArtifacts.push({
|
updatedArtifacts.push({
|
||||||
type: 'addition',
|
type: 'addition',
|
||||||
path: filename,
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
path: filename!,
|
||||||
contents: newContent,
|
contents: newContent,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,30 @@
|
||||||
import { exec as _exec } from 'child_process';
|
import { envMock, exec, mockExecAll } from '../../../../../test/exec-util';
|
||||||
import { envMock, mockExecAll } from '../../../../../test/exec-util';
|
import { env, partial } from '../../../../../test/util';
|
||||||
import { mocked } from '../../../../../test/util';
|
|
||||||
import { GlobalConfig } from '../../../../config/global';
|
import { GlobalConfig } from '../../../../config/global';
|
||||||
import * as _env from '../../../../util/exec/env';
|
import type { PackageFile, PostUpdateConfig } from '../../types';
|
||||||
import * as _lernaHelper from './lerna';
|
import * as lernaHelper from './lerna';
|
||||||
|
|
||||||
jest.mock('child_process');
|
jest.mock('child_process');
|
||||||
jest.mock('../../../../util/exec/env');
|
jest.mock('../../../../util/exec/env');
|
||||||
jest.mock('../../npm/post-update/node-version');
|
jest.mock('../../npm/post-update/node-version');
|
||||||
|
|
||||||
const exec: jest.Mock<typeof _exec> = _exec as any;
|
function lernaPkgFile(lernaClient: string): Partial<PackageFile> {
|
||||||
const env = mocked(_env);
|
|
||||||
const lernaHelper = mocked(_lernaHelper);
|
|
||||||
|
|
||||||
function lernaPkgFile(lernaClient: string) {
|
|
||||||
return {
|
return {
|
||||||
lernaClient,
|
lernaClient,
|
||||||
deps: [{ depName: 'lerna', currentValue: '2.0.0' }],
|
deps: [{ depName: 'lerna', currentValue: '2.0.0' }],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function lernaPkgFileWithoutLernaDep(lernaClient: string) {
|
function lernaPkgFileWithoutLernaDep(
|
||||||
|
lernaClient: string
|
||||||
|
): Partial<PackageFile> {
|
||||||
return {
|
return {
|
||||||
lernaClient,
|
lernaClient,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const config = partial<PostUpdateConfig>({});
|
||||||
|
|
||||||
describe('modules/manager/npm/post-update/lerna', () => {
|
describe('modules/manager/npm/post-update/lerna', () => {
|
||||||
describe('generateLockFiles()', () => {
|
describe('generateLockFiles()', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -35,7 +34,12 @@ describe('modules/manager/npm/post-update/lerna', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns if no lernaClient', async () => {
|
it('returns if no lernaClient', async () => {
|
||||||
const res = await lernaHelper.generateLockFiles({}, 'some-dir', {}, {});
|
const res = await lernaHelper.generateLockFiles(
|
||||||
|
{},
|
||||||
|
'some-dir',
|
||||||
|
config,
|
||||||
|
{}
|
||||||
|
);
|
||||||
expect(res.error).toBeFalse();
|
expect(res.error).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -43,7 +47,7 @@ describe('modules/manager/npm/post-update/lerna', () => {
|
||||||
const res = await lernaHelper.generateLockFiles(
|
const res = await lernaHelper.generateLockFiles(
|
||||||
lernaPkgFile('foo'),
|
lernaPkgFile('foo'),
|
||||||
'some-dir',
|
'some-dir',
|
||||||
{},
|
config,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
expect(res.error).toBeFalse();
|
expect(res.error).toBeFalse();
|
||||||
|
@ -55,7 +59,7 @@ describe('modules/manager/npm/post-update/lerna', () => {
|
||||||
const res = await lernaHelper.generateLockFiles(
|
const res = await lernaHelper.generateLockFiles(
|
||||||
lernaPkgFile('npm'),
|
lernaPkgFile('npm'),
|
||||||
'some-dir',
|
'some-dir',
|
||||||
{},
|
config,
|
||||||
{},
|
{},
|
||||||
skipInstalls
|
skipInstalls
|
||||||
);
|
);
|
||||||
|
@ -69,7 +73,7 @@ describe('modules/manager/npm/post-update/lerna', () => {
|
||||||
const res = await lernaHelper.generateLockFiles(
|
const res = await lernaHelper.generateLockFiles(
|
||||||
lernaPkgFile('npm'),
|
lernaPkgFile('npm'),
|
||||||
'some-dir',
|
'some-dir',
|
||||||
{},
|
config,
|
||||||
{},
|
{},
|
||||||
skipInstalls
|
skipInstalls
|
||||||
);
|
);
|
||||||
|
@ -82,7 +86,7 @@ describe('modules/manager/npm/post-update/lerna', () => {
|
||||||
const res = await lernaHelper.generateLockFiles(
|
const res = await lernaHelper.generateLockFiles(
|
||||||
lernaPkgFile('yarn'),
|
lernaPkgFile('yarn'),
|
||||||
'some-dir',
|
'some-dir',
|
||||||
{ constraints: { yarn: '^1.10.0' } },
|
{ ...config, constraints: { yarn: '^1.10.0' } },
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
expect(execSnapshots).toMatchSnapshot();
|
expect(execSnapshots).toMatchSnapshot();
|
||||||
|
@ -94,7 +98,7 @@ describe('modules/manager/npm/post-update/lerna', () => {
|
||||||
const res = await lernaHelper.generateLockFiles(
|
const res = await lernaHelper.generateLockFiles(
|
||||||
lernaPkgFileWithoutLernaDep('npm'),
|
lernaPkgFileWithoutLernaDep('npm'),
|
||||||
'some-dir',
|
'some-dir',
|
||||||
{},
|
config,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
expect(res.error).toBeFalse();
|
expect(res.error).toBeFalse();
|
||||||
|
@ -107,7 +111,7 @@ describe('modules/manager/npm/post-update/lerna', () => {
|
||||||
const res = await lernaHelper.generateLockFiles(
|
const res = await lernaHelper.generateLockFiles(
|
||||||
lernaPkgFile('npm'),
|
lernaPkgFile('npm'),
|
||||||
'some-dir',
|
'some-dir',
|
||||||
{ constraints: { npm: '^6.0.0' } },
|
{ ...config, constraints: { npm: '^6.0.0' } },
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
expect(res.error).toBeFalse();
|
expect(res.error).toBeFalse();
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { GlobalConfig } from '../../../../config/global';
|
||||||
import { TEMPORARY_ERROR } from '../../../../constants/error-messages';
|
import { TEMPORARY_ERROR } from '../../../../constants/error-messages';
|
||||||
import { logger } from '../../../../logger';
|
import { logger } from '../../../../logger';
|
||||||
import { exec } from '../../../../util/exec';
|
import { exec } from '../../../../util/exec';
|
||||||
import type { ExecOptions } from '../../../../util/exec/types';
|
import type { ExecOptions, ExtraEnv } from '../../../../util/exec/types';
|
||||||
import type { PackageFile, PostUpdateConfig } from '../../types';
|
import type { PackageFile, PostUpdateConfig } from '../../types';
|
||||||
import { getNodeConstraint } from './node-version';
|
import { getNodeConstraint } from './node-version';
|
||||||
import type { GenerateLockFileResult } from './types';
|
import type { GenerateLockFileResult } from './types';
|
||||||
|
@ -21,7 +21,8 @@ export function getLernaVersion(
|
||||||
);
|
);
|
||||||
return 'latest';
|
return 'latest';
|
||||||
}
|
}
|
||||||
return lernaDep.currentValue;
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
return lernaDep.currentValue!;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateLockFiles(
|
export async function generateLockFiles(
|
||||||
|
@ -37,8 +38,8 @@ export async function generateLockFiles(
|
||||||
return { error: false };
|
return { error: false };
|
||||||
}
|
}
|
||||||
logger.debug(`Spawning lerna with ${lernaClient} to create lock files`);
|
logger.debug(`Spawning lerna with ${lernaClient} to create lock files`);
|
||||||
const preCommands = [];
|
const preCommands: string[] = [];
|
||||||
const cmd = [];
|
const cmd: string[] = [];
|
||||||
let cmdOptions = '';
|
let cmdOptions = '';
|
||||||
try {
|
try {
|
||||||
if (lernaClient === 'yarn') {
|
if (lernaClient === 'yarn') {
|
||||||
|
@ -74,12 +75,13 @@ export async function generateLockFiles(
|
||||||
}
|
}
|
||||||
lernaCommand += cmdOptions;
|
lernaCommand += cmdOptions;
|
||||||
const tagConstraint = await getNodeConstraint(config);
|
const tagConstraint = await getNodeConstraint(config);
|
||||||
const execOptions: ExecOptions = {
|
const extraEnv: ExtraEnv = {
|
||||||
cwd,
|
|
||||||
extraEnv: {
|
|
||||||
NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE,
|
NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE,
|
||||||
npm_config_store: env.npm_config_store,
|
npm_config_store: env.npm_config_store,
|
||||||
},
|
};
|
||||||
|
const execOptions: ExecOptions = {
|
||||||
|
cwd,
|
||||||
|
extraEnv,
|
||||||
docker: {
|
docker: {
|
||||||
image: 'node',
|
image: 'node',
|
||||||
tagScheme: 'node',
|
tagScheme: 'node',
|
||||||
|
@ -89,8 +91,8 @@ export async function generateLockFiles(
|
||||||
};
|
};
|
||||||
// istanbul ignore if
|
// istanbul ignore if
|
||||||
if (GlobalConfig.get('exposeAllEnv')) {
|
if (GlobalConfig.get('exposeAllEnv')) {
|
||||||
execOptions.extraEnv.NPM_AUTH = env.NPM_AUTH;
|
extraEnv.NPM_AUTH = env.NPM_AUTH;
|
||||||
execOptions.extraEnv.NPM_EMAIL = env.NPM_EMAIL;
|
extraEnv.NPM_EMAIL = env.NPM_EMAIL;
|
||||||
}
|
}
|
||||||
const lernaVersion = getLernaVersion(lernaPackageFile);
|
const lernaVersion = getLernaVersion(lernaPackageFile);
|
||||||
logger.debug('Using lerna version ' + lernaVersion);
|
logger.debug('Using lerna version ' + lernaVersion);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { getSiblingFileName, readLocalFile } from '../../../../util/fs';
|
||||||
import { newlineRegex, regEx } from '../../../../util/regex';
|
import { newlineRegex, regEx } from '../../../../util/regex';
|
||||||
import type { PostUpdateConfig } from '../../types';
|
import type { PostUpdateConfig } from '../../types';
|
||||||
|
|
||||||
async function getNodeFile(filename: string): Promise<string> | null {
|
async function getNodeFile(filename: string): Promise<string | null> {
|
||||||
try {
|
try {
|
||||||
const constraint = (await readLocalFile(filename, 'utf8'))
|
const constraint = (await readLocalFile(filename, 'utf8'))
|
||||||
.split(newlineRegex)[0]
|
.split(newlineRegex)[0]
|
||||||
|
@ -19,7 +19,9 @@ async function getNodeFile(filename: string): Promise<string> | null {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPackageJsonConstraint(config: PostUpdateConfig): string | null {
|
function getPackageJsonConstraint(
|
||||||
|
config: Partial<PostUpdateConfig>
|
||||||
|
): string | null {
|
||||||
const constraint: string = config.constraints?.node;
|
const constraint: string = config.constraints?.node;
|
||||||
if (constraint && semver.validRange(constraint)) {
|
if (constraint && semver.validRange(constraint)) {
|
||||||
logger.debug(`Using node constraint "${constraint}" from package.json`);
|
logger.debug(`Using node constraint "${constraint}" from package.json`);
|
||||||
|
@ -29,8 +31,8 @@ function getPackageJsonConstraint(config: PostUpdateConfig): string | null {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNodeConstraint(
|
export async function getNodeConstraint(
|
||||||
config: PostUpdateConfig
|
config: Partial<PostUpdateConfig>
|
||||||
): Promise<string> | null {
|
): Promise<string | null> {
|
||||||
const { packageFile } = config;
|
const { packageFile } = config;
|
||||||
const constraint =
|
const constraint =
|
||||||
(await getNodeFile(getSiblingFileName(packageFile, '.nvmrc'))) ||
|
(await getNodeFile(getSiblingFileName(packageFile, '.nvmrc'))) ||
|
||||||
|
|
|
@ -6,7 +6,11 @@ import {
|
||||||
} from '../../../../constants/error-messages';
|
} from '../../../../constants/error-messages';
|
||||||
import { logger } from '../../../../logger';
|
import { logger } from '../../../../logger';
|
||||||
import { exec } from '../../../../util/exec';
|
import { exec } from '../../../../util/exec';
|
||||||
import type { ExecOptions, ToolConstraint } from '../../../../util/exec/types';
|
import type {
|
||||||
|
ExecOptions,
|
||||||
|
ExtraEnv,
|
||||||
|
ToolConstraint,
|
||||||
|
} from '../../../../util/exec/types';
|
||||||
import { move, pathExists, readFile, remove } from '../../../../util/fs';
|
import { move, pathExists, readFile, remove } from '../../../../util/fs';
|
||||||
import type { PostUpdateConfig, Upgrade } from '../../types';
|
import type { PostUpdateConfig, Upgrade } from '../../types';
|
||||||
import { composeLockFile, parseLockFile } from '../utils';
|
import { composeLockFile, parseLockFile } from '../utils';
|
||||||
|
@ -17,19 +21,19 @@ export async function generateLockFile(
|
||||||
cwd: string,
|
cwd: string,
|
||||||
env: NodeJS.ProcessEnv,
|
env: NodeJS.ProcessEnv,
|
||||||
filename: string,
|
filename: string,
|
||||||
config: PostUpdateConfig = {},
|
config: Partial<PostUpdateConfig> = {},
|
||||||
upgrades: Upgrade[] = []
|
upgrades: Upgrade[] = []
|
||||||
): Promise<GenerateLockFileResult> {
|
): Promise<GenerateLockFileResult> {
|
||||||
logger.debug(`Spawning npm install to create ${cwd}/${filename}`);
|
logger.debug(`Spawning npm install to create ${cwd}/${filename}`);
|
||||||
const { skipInstalls, postUpdateOptions } = config;
|
const { skipInstalls, postUpdateOptions } = config;
|
||||||
|
|
||||||
let lockFile = null;
|
let lockFile: string | null = null;
|
||||||
try {
|
try {
|
||||||
const npmToolConstraint: ToolConstraint = {
|
const npmToolConstraint: ToolConstraint = {
|
||||||
toolName: 'npm',
|
toolName: 'npm',
|
||||||
constraint: config.constraints?.npm,
|
constraint: config.constraints?.npm,
|
||||||
};
|
};
|
||||||
const commands = [];
|
const commands: string[] = [];
|
||||||
let cmdOptions = '';
|
let cmdOptions = '';
|
||||||
if (postUpdateOptions?.includes('npmDedupe') || skipInstalls === false) {
|
if (postUpdateOptions?.includes('npmDedupe') || skipInstalls === false) {
|
||||||
logger.debug('Performing node_modules install');
|
logger.debug('Performing node_modules install');
|
||||||
|
@ -44,12 +48,13 @@ export async function generateLockFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagConstraint = await getNodeConstraint(config);
|
const tagConstraint = await getNodeConstraint(config);
|
||||||
const execOptions: ExecOptions = {
|
const extraEnv: ExtraEnv = {
|
||||||
cwd,
|
|
||||||
extraEnv: {
|
|
||||||
NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE,
|
NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE,
|
||||||
npm_config_store: env.npm_config_store,
|
npm_config_store: env.npm_config_store,
|
||||||
},
|
};
|
||||||
|
const execOptions: ExecOptions = {
|
||||||
|
cwd,
|
||||||
|
extraEnv,
|
||||||
toolConstraints: [npmToolConstraint],
|
toolConstraints: [npmToolConstraint],
|
||||||
docker: {
|
docker: {
|
||||||
image: 'node',
|
image: 'node',
|
||||||
|
@ -59,8 +64,8 @@ export async function generateLockFile(
|
||||||
};
|
};
|
||||||
// istanbul ignore if
|
// istanbul ignore if
|
||||||
if (GlobalConfig.get('exposeAllEnv')) {
|
if (GlobalConfig.get('exposeAllEnv')) {
|
||||||
execOptions.extraEnv.NPM_AUTH = env.NPM_AUTH;
|
extraEnv.NPM_AUTH = env.NPM_AUTH;
|
||||||
execOptions.extraEnv.NPM_EMAIL = env.NPM_EMAIL;
|
extraEnv.NPM_EMAIL = env.NPM_EMAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!upgrades.every((upgrade) => upgrade.isLockfileUpdate)) {
|
if (!upgrades.every((upgrade) => upgrade.isLockfileUpdate)) {
|
||||||
|
@ -131,14 +136,18 @@ export async function generateLockFile(
|
||||||
const { detectedIndent, lockFileParsed } = parseLockFile(lockFile);
|
const { detectedIndent, lockFileParsed } = parseLockFile(lockFile);
|
||||||
if (lockFileParsed?.lockfileVersion === 2) {
|
if (lockFileParsed?.lockfileVersion === 2) {
|
||||||
lockUpdates.forEach((lockUpdate) => {
|
lockUpdates.forEach((lockUpdate) => {
|
||||||
|
const depType = lockUpdate.depType as
|
||||||
|
| 'dependencies'
|
||||||
|
| 'optionalDependencies';
|
||||||
if (
|
if (
|
||||||
lockFileParsed.packages?.['']?.[lockUpdate.depType]?.[
|
lockFileParsed.packages?.['']?.[depType]?.[
|
||||||
lockUpdate.depName
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
lockUpdate.depName!
|
||||||
]
|
]
|
||||||
) {
|
) {
|
||||||
lockFileParsed.packages[''][lockUpdate.depType][
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
lockUpdate.depName
|
lockFileParsed.packages[''][depType]![lockUpdate.depName!] =
|
||||||
] = lockUpdate.newValue;
|
lockUpdate.newValue!;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
lockFile = composeLockFile(lockFileParsed, detectedIndent);
|
lockFile = composeLockFile(lockFileParsed, detectedIndent);
|
||||||
|
|
|
@ -1,34 +1,28 @@
|
||||||
import { exec as _exec } from 'child_process';
|
import { envMock, exec, mockExecAll } from '../../../../../test/exec-util';
|
||||||
import { envMock, mockExecAll } from '../../../../../test/exec-util';
|
|
||||||
import { Fixtures } from '../../../../../test/fixtures';
|
import { Fixtures } from '../../../../../test/fixtures';
|
||||||
import { mocked } from '../../../../../test/util';
|
import { env, fs, partial } from '../../../../../test/util';
|
||||||
import * as _env from '../../../../util/exec/env';
|
|
||||||
import * as _fs from '../../../../util/fs/proxies';
|
|
||||||
import type { PostUpdateConfig } from '../../types';
|
import type { PostUpdateConfig } from '../../types';
|
||||||
import * as _pnpmHelper from './pnpm';
|
import * as pnpmHelper from './pnpm';
|
||||||
|
|
||||||
jest.mock('child_process');
|
jest.mock('child_process');
|
||||||
jest.mock('../../../../util/exec/env');
|
jest.mock('../../../../util/exec/env');
|
||||||
jest.mock('../../../../util/fs/proxies');
|
jest.mock('../../../../util/fs/proxies');
|
||||||
jest.mock('./node-version');
|
jest.mock('./node-version');
|
||||||
|
|
||||||
const exec: jest.Mock<typeof _exec> = _exec as any;
|
|
||||||
const env = mocked(_env);
|
|
||||||
const fs = mocked(_fs);
|
|
||||||
const pnpmHelper = mocked(_pnpmHelper);
|
|
||||||
delete process.env.NPM_CONFIG_CACHE;
|
delete process.env.NPM_CONFIG_CACHE;
|
||||||
|
|
||||||
describe('modules/manager/npm/post-update/pnpm', () => {
|
describe('modules/manager/npm/post-update/pnpm', () => {
|
||||||
let config: PostUpdateConfig;
|
let config: PostUpdateConfig;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
config = { cacheDir: 'some-cache-dir', constraints: { pnpm: '^2.0.0' } };
|
jest.resetAllMocks();
|
||||||
|
config = partial<PostUpdateConfig>({ constraints: { pnpm: '^2.0.0' } });
|
||||||
env.getChildProcessEnv.mockReturnValue(envMock.basic);
|
env.getChildProcessEnv.mockReturnValue(envMock.basic);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates lock files', async () => {
|
it('generates lock files', async () => {
|
||||||
const execSnapshots = mockExecAll(exec);
|
const execSnapshots = mockExecAll(exec);
|
||||||
fs.readFile = jest.fn(() => 'package-lock-contents') as never;
|
fs.readFile.mockResolvedValue('package-lock-contents');
|
||||||
const res = await pnpmHelper.generateLockFile('some-dir', {}, config);
|
const res = await pnpmHelper.generateLockFile('some-dir', {}, config);
|
||||||
expect(fs.readFile).toHaveBeenCalledTimes(1);
|
expect(fs.readFile).toHaveBeenCalledTimes(1);
|
||||||
expect(res.lockFile).toBe('package-lock-contents');
|
expect(res.lockFile).toBe('package-lock-contents');
|
||||||
|
@ -37,9 +31,9 @@ describe('modules/manager/npm/post-update/pnpm', () => {
|
||||||
|
|
||||||
it('catches errors', async () => {
|
it('catches errors', async () => {
|
||||||
const execSnapshots = mockExecAll(exec);
|
const execSnapshots = mockExecAll(exec);
|
||||||
fs.readFile = jest.fn(() => {
|
fs.readFile.mockImplementation(() => {
|
||||||
throw new Error('not found');
|
throw new Error('not found');
|
||||||
}) as never;
|
});
|
||||||
const res = await pnpmHelper.generateLockFile('some-dir', {}, config);
|
const res = await pnpmHelper.generateLockFile('some-dir', {}, config);
|
||||||
expect(fs.readFile).toHaveBeenCalledTimes(1);
|
expect(fs.readFile).toHaveBeenCalledTimes(1);
|
||||||
expect(res.error).toBeTrue();
|
expect(res.error).toBeTrue();
|
||||||
|
@ -49,7 +43,7 @@ describe('modules/manager/npm/post-update/pnpm', () => {
|
||||||
|
|
||||||
it('finds pnpm globally', async () => {
|
it('finds pnpm globally', async () => {
|
||||||
const execSnapshots = mockExecAll(exec);
|
const execSnapshots = mockExecAll(exec);
|
||||||
fs.readFile = jest.fn(() => 'package-lock-contents') as never;
|
fs.readFile.mockResolvedValue('package-lock-contents');
|
||||||
const res = await pnpmHelper.generateLockFile('some-dir', {}, config);
|
const res = await pnpmHelper.generateLockFile('some-dir', {}, config);
|
||||||
expect(fs.readFile).toHaveBeenCalledTimes(1);
|
expect(fs.readFile).toHaveBeenCalledTimes(1);
|
||||||
expect(res.lockFile).toBe('package-lock-contents');
|
expect(res.lockFile).toBe('package-lock-contents');
|
||||||
|
@ -58,7 +52,7 @@ describe('modules/manager/npm/post-update/pnpm', () => {
|
||||||
|
|
||||||
it('performs lock file maintenance', async () => {
|
it('performs lock file maintenance', async () => {
|
||||||
const execSnapshots = mockExecAll(exec);
|
const execSnapshots = mockExecAll(exec);
|
||||||
fs.readFile = jest.fn(() => 'package-lock-contents') as never;
|
fs.readFile.mockResolvedValue('package-lock-contents');
|
||||||
const res = await pnpmHelper.generateLockFile('some-dir', {}, config, [
|
const res = await pnpmHelper.generateLockFile('some-dir', {}, config, [
|
||||||
{ isLockFileMaintenance: true },
|
{ isLockFileMaintenance: true },
|
||||||
]);
|
]);
|
||||||
|
@ -70,7 +64,7 @@ describe('modules/manager/npm/post-update/pnpm', () => {
|
||||||
|
|
||||||
it('uses the new version if packageManager is updated', async () => {
|
it('uses the new version if packageManager is updated', async () => {
|
||||||
const execSnapshots = mockExecAll(exec);
|
const execSnapshots = mockExecAll(exec);
|
||||||
fs.readFile = jest.fn(() => 'package-lock-contents') as never;
|
fs.readFile.mockResolvedValue('package-lock-contents');
|
||||||
const res = await pnpmHelper.generateLockFile('some-dir', {}, config, [
|
const res = await pnpmHelper.generateLockFile('some-dir', {}, config, [
|
||||||
{
|
{
|
||||||
depType: 'packageManager',
|
depType: 'packageManager',
|
||||||
|
@ -86,12 +80,11 @@ describe('modules/manager/npm/post-update/pnpm', () => {
|
||||||
|
|
||||||
it('uses constraint version if parent json has constraints', async () => {
|
it('uses constraint version if parent json has constraints', async () => {
|
||||||
const execSnapshots = mockExecAll(exec);
|
const execSnapshots = mockExecAll(exec);
|
||||||
const configTemp = { cacheDir: 'some-cache-dir' };
|
const configTemp = partial<PostUpdateConfig>({});
|
||||||
const fileContent = Fixtures.get('parent/package.json');
|
const fileContent = Fixtures.get('parent/package.json');
|
||||||
fs.readFile = jest
|
fs.readFile
|
||||||
.fn()
|
.mockResolvedValueOnce(fileContent)
|
||||||
.mockReturnValueOnce(fileContent)
|
.mockResolvedValue('package-lock-contents');
|
||||||
.mockReturnValue('package-lock-contents');
|
|
||||||
const res = await pnpmHelper.generateLockFile(
|
const res = await pnpmHelper.generateLockFile(
|
||||||
'some-folder',
|
'some-folder',
|
||||||
{},
|
{},
|
||||||
|
@ -129,12 +122,11 @@ describe('modules/manager/npm/post-update/pnpm', () => {
|
||||||
|
|
||||||
it('uses packageManager version and puts it into constraint', async () => {
|
it('uses packageManager version and puts it into constraint', async () => {
|
||||||
const execSnapshots = mockExecAll(exec);
|
const execSnapshots = mockExecAll(exec);
|
||||||
const configTemp = { cacheDir: 'some-cache-dir' };
|
const configTemp = partial<PostUpdateConfig>({});
|
||||||
const fileContent = Fixtures.get('manager-field/package.json');
|
const fileContent = Fixtures.get('manager-field/package.json');
|
||||||
fs.readFile = jest
|
fs.readFile
|
||||||
.fn()
|
.mockResolvedValueOnce(fileContent)
|
||||||
.mockReturnValueOnce(fileContent)
|
.mockResolvedValue('package-lock-contents');
|
||||||
.mockReturnValue('package-lock-contents');
|
|
||||||
const res = await pnpmHelper.generateLockFile(
|
const res = await pnpmHelper.generateLockFile(
|
||||||
'some-folder',
|
'some-folder',
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -3,7 +3,11 @@ import { GlobalConfig } from '../../../../config/global';
|
||||||
import { TEMPORARY_ERROR } from '../../../../constants/error-messages';
|
import { TEMPORARY_ERROR } from '../../../../constants/error-messages';
|
||||||
import { logger } from '../../../../logger';
|
import { logger } from '../../../../logger';
|
||||||
import { exec } from '../../../../util/exec';
|
import { exec } from '../../../../util/exec';
|
||||||
import type { ExecOptions, ToolConstraint } from '../../../../util/exec/types';
|
import type {
|
||||||
|
ExecOptions,
|
||||||
|
ExtraEnv,
|
||||||
|
ToolConstraint,
|
||||||
|
} from '../../../../util/exec/types';
|
||||||
import { readFile, remove } from '../../../../util/fs';
|
import { readFile, remove } from '../../../../util/fs';
|
||||||
import type { PostUpdateConfig, Upgrade } from '../../types';
|
import type { PostUpdateConfig, Upgrade } from '../../types';
|
||||||
import type { NpmPackage } from '../extract/types';
|
import type { NpmPackage } from '../extract/types';
|
||||||
|
@ -18,9 +22,9 @@ export async function generateLockFile(
|
||||||
): Promise<GenerateLockFileResult> {
|
): Promise<GenerateLockFileResult> {
|
||||||
const lockFileName = upath.join(cwd, 'pnpm-lock.yaml');
|
const lockFileName = upath.join(cwd, 'pnpm-lock.yaml');
|
||||||
logger.debug(`Spawning pnpm install to create ${lockFileName}`);
|
logger.debug(`Spawning pnpm install to create ${lockFileName}`);
|
||||||
let lockFile = null;
|
let lockFile: string | null = null;
|
||||||
let stdout: string;
|
let stdout: string | undefined;
|
||||||
let stderr: string;
|
let stderr: string | undefined;
|
||||||
let cmd = 'pnpm';
|
let cmd = 'pnpm';
|
||||||
try {
|
try {
|
||||||
const pnpmToolConstraint: ToolConstraint = {
|
const pnpmToolConstraint: ToolConstraint = {
|
||||||
|
@ -28,12 +32,13 @@ export async function generateLockFile(
|
||||||
constraint: config.constraints?.pnpm ?? (await getPnpmContraint(cwd)),
|
constraint: config.constraints?.pnpm ?? (await getPnpmContraint(cwd)),
|
||||||
};
|
};
|
||||||
const tagConstraint = await getNodeConstraint(config);
|
const tagConstraint = await getNodeConstraint(config);
|
||||||
const execOptions: ExecOptions = {
|
const extraEnv: ExtraEnv = {
|
||||||
cwd,
|
|
||||||
extraEnv: {
|
|
||||||
NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE,
|
NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE,
|
||||||
npm_config_store: env.npm_config_store,
|
npm_config_store: env.npm_config_store,
|
||||||
},
|
};
|
||||||
|
const execOptions: ExecOptions = {
|
||||||
|
cwd,
|
||||||
|
extraEnv,
|
||||||
docker: {
|
docker: {
|
||||||
image: 'node',
|
image: 'node',
|
||||||
tagScheme: 'node',
|
tagScheme: 'node',
|
||||||
|
@ -43,8 +48,8 @@ export async function generateLockFile(
|
||||||
};
|
};
|
||||||
// istanbul ignore if
|
// istanbul ignore if
|
||||||
if (GlobalConfig.get('exposeAllEnv')) {
|
if (GlobalConfig.get('exposeAllEnv')) {
|
||||||
execOptions.extraEnv.NPM_AUTH = env.NPM_AUTH;
|
extraEnv.NPM_AUTH = env.NPM_AUTH;
|
||||||
execOptions.extraEnv.NPM_EMAIL = env.NPM_EMAIL;
|
extraEnv.NPM_EMAIL = env.NPM_EMAIL;
|
||||||
}
|
}
|
||||||
cmd = 'pnpm';
|
cmd = 'pnpm';
|
||||||
let args = 'install --recursive --lockfile-only';
|
let args = 'install --recursive --lockfile-only';
|
||||||
|
@ -89,8 +94,8 @@ export async function generateLockFile(
|
||||||
return { lockFile };
|
return { lockFile };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPnpmContraint(cwd: string): Promise<string> {
|
async function getPnpmContraint(cwd: string): Promise<string | undefined> {
|
||||||
let result;
|
let result: string | undefined;
|
||||||
const rootPackageJson = upath.join(cwd, 'package.json');
|
const rootPackageJson = upath.join(cwd, 'package.json');
|
||||||
const content = await readFile(rootPackageJson, 'utf8');
|
const content = await readFile(rootPackageJson, 'utf8');
|
||||||
if (content) {
|
if (content) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ export interface AdditionalPackageFiles {
|
||||||
|
|
||||||
export interface ArtifactError {
|
export interface ArtifactError {
|
||||||
lockFile: string;
|
lockFile: string;
|
||||||
stderr: string;
|
stderr?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WriteExistingFilesResult {
|
export interface WriteExistingFilesResult {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
import { logger } from '../../../../logger';
|
import { logger } from '../../../../logger';
|
||||||
import { ExternalHostError } from '../../../../types/errors/external-host-error';
|
import { ExternalHostError } from '../../../../types/errors/external-host-error';
|
||||||
import { exec } from '../../../../util/exec';
|
import { exec } from '../../../../util/exec';
|
||||||
import type { ExecOptions } from '../../../../util/exec/types';
|
import type { ExecOptions, ExtraEnv } from '../../../../util/exec/types';
|
||||||
import { exists, readFile, remove, writeFile } from '../../../../util/fs';
|
import { exists, readFile, remove, writeFile } from '../../../../util/fs';
|
||||||
import { newlineRegex, regEx } from '../../../../util/regex';
|
import { newlineRegex, regEx } from '../../../../util/regex';
|
||||||
import { uniqueStrings } from '../../../../util/string';
|
import { uniqueStrings } from '../../../../util/string';
|
||||||
|
@ -23,7 +23,7 @@ export async function checkYarnrc(
|
||||||
cwd: string
|
cwd: string
|
||||||
): Promise<{ offlineMirror: boolean; yarnPath: string | null }> {
|
): Promise<{ offlineMirror: boolean; yarnPath: string | null }> {
|
||||||
let offlineMirror = false;
|
let offlineMirror = false;
|
||||||
let yarnPath: string = null;
|
let yarnPath: string | null = null;
|
||||||
try {
|
try {
|
||||||
const yarnrc = await readFile(`${cwd}/.yarnrc`, 'utf8');
|
const yarnrc = await readFile(`${cwd}/.yarnrc`, 'utf8');
|
||||||
if (is.string(yarnrc)) {
|
if (is.string(yarnrc)) {
|
||||||
|
@ -37,7 +37,7 @@ export async function checkYarnrc(
|
||||||
if (pathLine) {
|
if (pathLine) {
|
||||||
yarnPath = pathLine.replace(regEx(/^yarn-path\s+"?(.+?)"?$/), '$1');
|
yarnPath = pathLine.replace(regEx(/^yarn-path\s+"?(.+?)"?$/), '$1');
|
||||||
}
|
}
|
||||||
const yarnBinaryExists = await exists(yarnPath);
|
const yarnBinaryExists = yarnPath ? await exists(yarnPath) : false;
|
||||||
if (!yarnBinaryExists) {
|
if (!yarnBinaryExists) {
|
||||||
const scrubbedYarnrc = yarnrc.replace(
|
const scrubbedYarnrc = yarnrc.replace(
|
||||||
regEx(/^yarn-path\s+"?.+?"?$/gm),
|
regEx(/^yarn-path\s+"?.+?"?$/gm),
|
||||||
|
@ -66,12 +66,12 @@ export function isYarnUpdate(upgrade: Upgrade): boolean {
|
||||||
export async function generateLockFile(
|
export async function generateLockFile(
|
||||||
cwd: string,
|
cwd: string,
|
||||||
env: NodeJS.ProcessEnv,
|
env: NodeJS.ProcessEnv,
|
||||||
config: PostUpdateConfig = {},
|
config: Partial<PostUpdateConfig> = {},
|
||||||
upgrades: Upgrade[] = []
|
upgrades: Upgrade[] = []
|
||||||
): Promise<GenerateLockFileResult> {
|
): Promise<GenerateLockFileResult> {
|
||||||
const lockFileName = upath.join(cwd, 'yarn.lock');
|
const lockFileName = upath.join(cwd, 'yarn.lock');
|
||||||
logger.debug(`Spawning yarn install to create ${lockFileName}`);
|
logger.debug(`Spawning yarn install to create ${lockFileName}`);
|
||||||
let lockFile = null;
|
let lockFile: string | null = null;
|
||||||
try {
|
try {
|
||||||
const yarnUpdate = upgrades.find(isYarnUpdate);
|
const yarnUpdate = upgrades.find(isYarnUpdate);
|
||||||
const yarnCompatibility = yarnUpdate
|
const yarnCompatibility = yarnUpdate
|
||||||
|
@ -93,13 +93,13 @@ export async function generateLockFile(
|
||||||
|
|
||||||
const preCommands = [installYarn];
|
const preCommands = [installYarn];
|
||||||
|
|
||||||
const extraEnv: ExecOptions['extraEnv'] = {
|
const extraEnv: ExtraEnv = {
|
||||||
NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE,
|
NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE,
|
||||||
npm_config_store: env.npm_config_store,
|
npm_config_store: env.npm_config_store,
|
||||||
CI: 'true',
|
CI: 'true',
|
||||||
};
|
};
|
||||||
|
|
||||||
const commands = [];
|
const commands: string[] = [];
|
||||||
let cmdOptions = ''; // should have a leading space
|
let cmdOptions = ''; // should have a leading space
|
||||||
if (config.skipInstalls !== false) {
|
if (config.skipInstalls !== false) {
|
||||||
if (isYarn1) {
|
if (isYarn1) {
|
||||||
|
@ -157,8 +157,8 @@ export async function generateLockFile(
|
||||||
};
|
};
|
||||||
// istanbul ignore if
|
// istanbul ignore if
|
||||||
if (GlobalConfig.get('exposeAllEnv')) {
|
if (GlobalConfig.get('exposeAllEnv')) {
|
||||||
execOptions.extraEnv.NPM_AUTH = env.NPM_AUTH;
|
extraEnv.NPM_AUTH = env.NPM_AUTH;
|
||||||
execOptions.extraEnv.NPM_EMAIL = env.NPM_EMAIL;
|
extraEnv.NPM_EMAIL = env.NPM_EMAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (yarnUpdate && !isYarn1) {
|
if (yarnUpdate && !isYarn1) {
|
||||||
|
@ -179,6 +179,7 @@ export async function generateLockFile(
|
||||||
commands.push(
|
commands.push(
|
||||||
`yarn upgrade ${lockUpdates
|
`yarn upgrade ${lockUpdates
|
||||||
.map((update) => update.depName)
|
.map((update) => update.depName)
|
||||||
|
.filter(is.string)
|
||||||
.filter(uniqueStrings)
|
.filter(uniqueStrings)
|
||||||
.join(' ')}${cmdOptions}`
|
.join(' ')}${cmdOptions}`
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,8 @@ import type { RangeConfig } from '../types';
|
||||||
export function getRangeStrategy(config: RangeConfig): RangeStrategy {
|
export function getRangeStrategy(config: RangeConfig): RangeStrategy {
|
||||||
const { depType, depName, packageJsonType, currentValue, rangeStrategy } =
|
const { depType, depName, packageJsonType, currentValue, rangeStrategy } =
|
||||||
config;
|
config;
|
||||||
const isComplexRange = parseRange(currentValue).length > 1;
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
const isComplexRange = parseRange(currentValue!).length > 1;
|
||||||
if (rangeStrategy === 'bump' && isComplexRange) {
|
if (rangeStrategy === 'bump' && isComplexRange) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
{ currentValue },
|
{ currentValue },
|
||||||
|
|
|
@ -62,3 +62,10 @@ export interface ParseLockFileResult {
|
||||||
detectedIndent: string;
|
detectedIndent: string;
|
||||||
lockFileParsed: LockFile | undefined;
|
lockFileParsed: LockFile | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NpmDepType =
|
||||||
|
| 'dependencies'
|
||||||
|
| 'devDependencies'
|
||||||
|
| 'optionalDependencies'
|
||||||
|
| 'peerDependencies'
|
||||||
|
| 'resolutions';
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { escapeRegExp, regEx } from '../../../../../util/regex';
|
||||||
import { matchAt, replaceAt } from '../../../../../util/string';
|
import { matchAt, replaceAt } from '../../../../../util/string';
|
||||||
import type { UpdateDependencyConfig } from '../../../types';
|
import type { UpdateDependencyConfig } from '../../../types';
|
||||||
import type { DependenciesMeta, NpmPackage } from '../../extract/types';
|
import type { DependenciesMeta, NpmPackage } from '../../extract/types';
|
||||||
|
import type { NpmDepType } from '../../types';
|
||||||
|
|
||||||
function renameObjKey(
|
function renameObjKey(
|
||||||
oldObj: DependenciesMeta,
|
oldObj: DependenciesMeta,
|
||||||
|
@ -18,35 +19,36 @@ function renameObjKey(
|
||||||
acc[key] = oldObj[key];
|
acc[key] = oldObj[key];
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {} as DependenciesMeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceAsString(
|
function replaceAsString(
|
||||||
parsedContents: NpmPackage,
|
parsedContents: NpmPackage,
|
||||||
fileContent: string,
|
fileContent: string,
|
||||||
depType: string,
|
depType: NpmDepType | 'dependenciesMeta' | 'packageManager',
|
||||||
depName: string,
|
depName: string,
|
||||||
oldValue: string,
|
oldValue: string,
|
||||||
newValue: string
|
newValue: string
|
||||||
): string | null {
|
): string {
|
||||||
if (depType === 'packageManager') {
|
if (depType === 'packageManager') {
|
||||||
parsedContents[depType] = newValue;
|
parsedContents[depType] = newValue;
|
||||||
} else if (depName === oldValue) {
|
} else if (depName === oldValue) {
|
||||||
// The old value is the name of the dependency itself
|
// The old value is the name of the dependency itself
|
||||||
delete Object.assign(parsedContents[depType], {
|
delete Object.assign(parsedContents[depType], {
|
||||||
[newValue]: parsedContents[depType][oldValue],
|
[newValue]: parsedContents[depType]![oldValue],
|
||||||
})[oldValue];
|
})[oldValue];
|
||||||
} else if (depType === 'dependenciesMeta') {
|
} else if (depType === 'dependenciesMeta') {
|
||||||
if (oldValue !== newValue) {
|
if (oldValue !== newValue) {
|
||||||
parsedContents.dependenciesMeta = renameObjKey(
|
parsedContents.dependenciesMeta = renameObjKey(
|
||||||
parsedContents.dependenciesMeta,
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
parsedContents.dependenciesMeta!,
|
||||||
oldValue,
|
oldValue,
|
||||||
newValue
|
newValue
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The old value is the version of the dependency
|
// The old value is the version of the dependency
|
||||||
parsedContents[depType][depName] = newValue;
|
parsedContents[depType]![depName] = newValue;
|
||||||
}
|
}
|
||||||
// Look for the old version number
|
// Look for the old version number
|
||||||
const searchString = `"${oldValue}"`;
|
const searchString = `"${oldValue}"`;
|
||||||
|
@ -55,9 +57,9 @@ function replaceAsString(
|
||||||
const escapedDepName = escapeRegExp(depName);
|
const escapedDepName = escapeRegExp(depName);
|
||||||
const patchRe = regEx(`^(patch:${escapedDepName}@(npm:)?).*#`);
|
const patchRe = regEx(`^(patch:${escapedDepName}@(npm:)?).*#`);
|
||||||
const match = patchRe.exec(oldValue);
|
const match = patchRe.exec(oldValue);
|
||||||
if (match) {
|
if (match && depType === 'resolutions') {
|
||||||
const patch = oldValue.replace(match[0], `${match[1]}${newValue}#`);
|
const patch = oldValue.replace(match[0], `${match[1]}${newValue}#`);
|
||||||
parsedContents[depType][depName] = patch;
|
parsedContents[depType]![depName] = patch;
|
||||||
newString = `"${patch}"`;
|
newString = `"${patch}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +100,8 @@ export function updateDependency({
|
||||||
logger.debug('Updating package.json git digest');
|
logger.debug('Updating package.json git digest');
|
||||||
newValue = upgrade.currentRawValue.replace(
|
newValue = upgrade.currentRawValue.replace(
|
||||||
upgrade.currentDigest,
|
upgrade.currentDigest,
|
||||||
upgrade.newDigest.substring(0, upgrade.currentDigest.length)
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
upgrade.newDigest!.substring(0, upgrade.currentDigest.length)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
logger.debug('Updating package.json git version tag');
|
logger.debug('Updating package.json git version tag');
|
||||||
|
@ -115,36 +118,38 @@ export function updateDependency({
|
||||||
try {
|
try {
|
||||||
const parsedContents: NpmPackage = JSON.parse(fileContent);
|
const parsedContents: NpmPackage = JSON.parse(fileContent);
|
||||||
// Save the old version
|
// Save the old version
|
||||||
let oldVersion: string;
|
let oldVersion: string | undefined;
|
||||||
if (depType === 'packageManager') {
|
if (depType === 'packageManager') {
|
||||||
oldVersion = parsedContents[depType];
|
oldVersion = parsedContents[depType];
|
||||||
newValue = `${depName}@${newValue}`;
|
newValue = `${depName}@${newValue}`;
|
||||||
} else {
|
} else {
|
||||||
oldVersion = parsedContents[depType][depName];
|
oldVersion = parsedContents[depType as NpmDepType]![depName];
|
||||||
}
|
}
|
||||||
if (oldVersion === newValue) {
|
if (oldVersion === newValue) {
|
||||||
logger.trace('Version is already updated');
|
logger.trace('Version is already updated');
|
||||||
return fileContent;
|
return fileContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
|
||||||
let newFileContent = replaceAsString(
|
let newFileContent = replaceAsString(
|
||||||
parsedContents,
|
parsedContents,
|
||||||
fileContent,
|
fileContent,
|
||||||
depType,
|
depType as NpmDepType,
|
||||||
depName,
|
depName,
|
||||||
oldVersion,
|
oldVersion!,
|
||||||
newValue
|
newValue!
|
||||||
);
|
);
|
||||||
if (upgrade.newName) {
|
if (upgrade.newName) {
|
||||||
newFileContent = replaceAsString(
|
newFileContent = replaceAsString(
|
||||||
parsedContents,
|
parsedContents,
|
||||||
newFileContent,
|
newFileContent,
|
||||||
depType,
|
depType as NpmDepType,
|
||||||
depName,
|
depName,
|
||||||
depName,
|
depName,
|
||||||
upgrade.newName
|
upgrade.newName
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
/* eslint-enable @typescript-eslint/no-unnecessary-type-assertion */
|
||||||
// istanbul ignore if
|
// istanbul ignore if
|
||||||
if (!newFileContent) {
|
if (!newFileContent) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
@ -154,7 +159,7 @@ export function updateDependency({
|
||||||
return fileContent;
|
return fileContent;
|
||||||
}
|
}
|
||||||
if (parsedContents?.resolutions) {
|
if (parsedContents?.resolutions) {
|
||||||
let depKey: string;
|
let depKey: string | undefined;
|
||||||
if (parsedContents.resolutions[depName]) {
|
if (parsedContents.resolutions[depName]) {
|
||||||
depKey = depName;
|
depKey = depName;
|
||||||
} else if (parsedContents.resolutions[`**/${depName}`]) {
|
} else if (parsedContents.resolutions[`**/${depName}`]) {
|
||||||
|
@ -179,7 +184,8 @@ export function updateDependency({
|
||||||
'resolutions',
|
'resolutions',
|
||||||
depKey,
|
depKey,
|
||||||
parsedContents.resolutions[depKey],
|
parsedContents.resolutions[depKey],
|
||||||
newValue
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
newValue!
|
||||||
);
|
);
|
||||||
if (upgrade.newName) {
|
if (upgrade.newName) {
|
||||||
if (depKey === `**/${depName}`) {
|
if (depKey === `**/${depName}`) {
|
||||||
|
|
|
@ -75,7 +75,7 @@ export async function findFirstParentVersion(
|
||||||
for (const parentVersion of parentVersions) {
|
for (const parentVersion of parentVersions) {
|
||||||
const constraint = parentDep.releases.find(
|
const constraint = parentDep.releases.find(
|
||||||
(release) => release.version === parentVersion
|
(release) => release.version === parentVersion
|
||||||
).dependencies?.[targetDepName];
|
)?.dependencies?.[targetDepName];
|
||||||
if (!constraint) {
|
if (!constraint) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`${targetDepName} has been removed from ${parentName}@${parentVersion}`
|
`${targetDepName} has been removed from ${parentName}@${parentVersion}`
|
||||||
|
|
|
@ -30,11 +30,14 @@ export async function updateLockedDependency(
|
||||||
try {
|
try {
|
||||||
let packageJson: PackageJson;
|
let packageJson: PackageJson;
|
||||||
let packageLockJson: PackageLockOrEntry;
|
let packageLockJson: PackageLockOrEntry;
|
||||||
const detectedIndent = detectIndent(lockFileContent).indent || ' ';
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
let newPackageJsonContent: string;
|
const detectedIndent = detectIndent(lockFileContent!).indent || ' ';
|
||||||
|
let newPackageJsonContent: string | null | undefined;
|
||||||
try {
|
try {
|
||||||
packageJson = JSON.parse(packageFileContent);
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
packageLockJson = JSON.parse(lockFileContent);
|
packageJson = JSON.parse(packageFileContent!);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
packageLockJson = JSON.parse(lockFileContent!);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn({ err }, 'Failed to parse files');
|
logger.warn({ err }, 'Failed to parse files');
|
||||||
return { status: 'update-failed' };
|
return { status: 'update-failed' };
|
||||||
|
@ -43,7 +46,8 @@ export async function updateLockedDependency(
|
||||||
const lockedDeps = getLockedDependencies(
|
const lockedDeps = getLockedDependencies(
|
||||||
packageLockJson,
|
packageLockJson,
|
||||||
depName,
|
depName,
|
||||||
currentVersion
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
currentVersion!
|
||||||
);
|
);
|
||||||
if (lockedDeps.some((dep) => dep.bundled)) {
|
if (lockedDeps.some((dep) => dep.bundled)) {
|
||||||
logger.info(
|
logger.info(
|
||||||
|
@ -109,10 +113,12 @@ export async function updateLockedDependency(
|
||||||
// Don't return {} if we're a parent update or else the whole update will fail
|
// Don't return {} if we're a parent update or else the whole update will fail
|
||||||
// istanbul ignore if: too hard to replicate
|
// istanbul ignore if: too hard to replicate
|
||||||
if (isParentUpdate) {
|
if (isParentUpdate) {
|
||||||
const res: UpdateLockedResult = { status, files: {} };
|
const files: Record<string, string> = {};
|
||||||
res.files[packageFile] = packageFileContent;
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
res.files[lockFile] = lockFileContent;
|
files[packageFile!] = packageFileContent!;
|
||||||
return res;
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
files[lockFile!] = lockFileContent!;
|
||||||
|
return { status, files: files };
|
||||||
}
|
}
|
||||||
return { status };
|
return { status };
|
||||||
}
|
}
|
||||||
|
@ -123,7 +129,8 @@ export async function updateLockedDependency(
|
||||||
packageJson,
|
packageJson,
|
||||||
packageLockJson,
|
packageLockJson,
|
||||||
depName,
|
depName,
|
||||||
currentVersion,
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
currentVersion!,
|
||||||
newVersion
|
newVersion
|
||||||
);
|
);
|
||||||
logger.trace({ deps: lockedDeps, constraints }, 'Matching details');
|
logger.trace({ deps: lockedDeps, constraints }, 'Matching details');
|
||||||
|
@ -134,7 +141,7 @@ export async function updateLockedDependency(
|
||||||
);
|
);
|
||||||
return { status: 'update-failed' };
|
return { status: 'update-failed' };
|
||||||
}
|
}
|
||||||
const parentUpdates: UpdateLockedConfig[] = [];
|
const parentUpdates: Partial<UpdateLockedConfig>[] = [];
|
||||||
for (const {
|
for (const {
|
||||||
parentDepName,
|
parentDepName,
|
||||||
parentVersion,
|
parentVersion,
|
||||||
|
@ -172,7 +179,7 @@ export async function updateLockedDependency(
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Update of ${depName} to ${newVersion} can be achieved due to parent ${parentDepName}`
|
`Update of ${depName} to ${newVersion} can be achieved due to parent ${parentDepName}`
|
||||||
);
|
);
|
||||||
const parentUpdate: UpdateLockedConfig = {
|
const parentUpdate: Partial<UpdateLockedConfig> = {
|
||||||
depName: parentDepName,
|
depName: parentDepName,
|
||||||
currentVersion: parentVersion,
|
currentVersion: parentVersion,
|
||||||
newVersion: parentNewVersion,
|
newVersion: parentNewVersion,
|
||||||
|
@ -187,15 +194,17 @@ export async function updateLockedDependency(
|
||||||
return { status: 'update-failed' };
|
return { status: 'update-failed' };
|
||||||
}
|
}
|
||||||
} else if (depType) {
|
} else if (depType) {
|
||||||
|
// TODO: `newValue` can probably null
|
||||||
// The constaint comes from the package.json file, so we need to update it
|
// The constaint comes from the package.json file, so we need to update it
|
||||||
const newValue = semver.getNewValue({
|
const newValue = semver.getNewValue({
|
||||||
currentValue: constraint,
|
currentValue: constraint,
|
||||||
rangeStrategy: 'replace',
|
rangeStrategy: 'replace',
|
||||||
currentVersion,
|
currentVersion,
|
||||||
newVersion,
|
newVersion,
|
||||||
});
|
})!;
|
||||||
newPackageJsonContent = updateDependency({
|
newPackageJsonContent = updateDependency({
|
||||||
fileContent: packageFileContent,
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
fileContent: packageFileContent!,
|
||||||
upgrade: { depName, depType, newValue },
|
upgrade: { depName, depType, newValue },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -215,9 +224,9 @@ export async function updateLockedDependency(
|
||||||
for (const parentUpdate of parentUpdates) {
|
for (const parentUpdate of parentUpdates) {
|
||||||
const parentUpdateConfig = {
|
const parentUpdateConfig = {
|
||||||
...config,
|
...config,
|
||||||
|
...parentUpdate,
|
||||||
lockFileContent: newLockFileContent,
|
lockFileContent: newLockFileContent,
|
||||||
packageFileContent: newPackageJsonContent || packageFileContent,
|
packageFileContent: newPackageJsonContent || packageFileContent,
|
||||||
...parentUpdate,
|
|
||||||
};
|
};
|
||||||
const parentUpdateResult = await updateLockedDependency(
|
const parentUpdateResult = await updateLockedDependency(
|
||||||
parentUpdateConfig,
|
parentUpdateConfig,
|
||||||
|
@ -235,7 +244,7 @@ export async function updateLockedDependency(
|
||||||
newLockFileContent =
|
newLockFileContent =
|
||||||
parentUpdateResult.files[lockFile] || newLockFileContent;
|
parentUpdateResult.files[lockFile] || newLockFileContent;
|
||||||
}
|
}
|
||||||
const files = {};
|
const files: Record<string, string> = {};
|
||||||
if (newLockFileContent) {
|
if (newLockFileContent) {
|
||||||
files[lockFile] = newLockFileContent;
|
files[lockFile] = newLockFileContent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { loadFixture } from '../../../../../../../test/util';
|
import { loadFixture, partial } from '../../../../../../../test/util';
|
||||||
import type { UpdateLockedConfig } from '../../../../types';
|
import type { UpdateLockedConfig } from '../../../../types';
|
||||||
import { updateLockedDependency } from '.';
|
import { updateLockedDependency } from '.';
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ describe('modules/manager/npm/update/locked-dependency/yarn-lock/index', () => {
|
||||||
let config: UpdateLockedConfig;
|
let config: UpdateLockedConfig;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
config = {};
|
config = partial<UpdateLockedConfig>({ packageFile: 'package.json' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns if cannot parse lock file', () => {
|
it('returns if cannot parse lock file', () => {
|
||||||
|
|
|
@ -16,7 +16,8 @@ export function updateLockedDependency(
|
||||||
);
|
);
|
||||||
let yarnLock: YarnLock;
|
let yarnLock: YarnLock;
|
||||||
try {
|
try {
|
||||||
yarnLock = parseSyml(lockFileContent);
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
yarnLock = parseSyml(lockFileContent!);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn({ err }, 'Failed to parse yarn files');
|
logger.warn({ err }, 'Failed to parse yarn files');
|
||||||
return { status: 'update-failed' };
|
return { status: 'update-failed' };
|
||||||
|
@ -26,7 +27,12 @@ export function updateLockedDependency(
|
||||||
return { status: 'unsupported' };
|
return { status: 'unsupported' };
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const lockedDeps = getLockedDependencies(yarnLock, depName, currentVersion);
|
const lockedDeps = getLockedDependencies(
|
||||||
|
yarnLock,
|
||||||
|
depName,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
currentVersion!
|
||||||
|
);
|
||||||
if (!lockedDeps.length) {
|
if (!lockedDeps.length) {
|
||||||
const newLockedDeps = getLockedDependencies(
|
const newLockedDeps = getLockedDependencies(
|
||||||
yarnLock,
|
yarnLock,
|
||||||
|
@ -61,7 +67,8 @@ export function updateLockedDependency(
|
||||||
);
|
);
|
||||||
return { status: 'update-failed' };
|
return { status: 'update-failed' };
|
||||||
}
|
}
|
||||||
let newLockFileContent = lockFileContent;
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
|
let newLockFileContent = lockFileContent!;
|
||||||
for (const dependency of updateLockedDeps) {
|
for (const dependency of updateLockedDeps) {
|
||||||
const { depName, constraint, newVersion } = dependency;
|
const { depName, constraint, newVersion } = dependency;
|
||||||
newLockFileContent = replaceConstraintVersion(
|
newLockFileContent = replaceConstraintVersion(
|
||||||
|
|
|
@ -12,7 +12,7 @@ export function bumpPackageVersion(
|
||||||
{ bumpVersion, currentValue },
|
{ bumpVersion, currentValue },
|
||||||
'Checking if we should bump package.json version'
|
'Checking if we should bump package.json version'
|
||||||
);
|
);
|
||||||
let newPjVersion: string;
|
let newPjVersion: string | null;
|
||||||
let bumpedContent = content;
|
let bumpedContent = content;
|
||||||
try {
|
try {
|
||||||
if (bumpVersion.startsWith('mirror:')) {
|
if (bumpVersion.startsWith('mirror:')) {
|
||||||
|
|
|
@ -2,11 +2,16 @@ import { loadFixture } from '../../../../test/util';
|
||||||
import type { UpdateLockedConfig } from '../types';
|
import type { UpdateLockedConfig } from '../types';
|
||||||
import { updateLockedDependency } from '.';
|
import { updateLockedDependency } from '.';
|
||||||
|
|
||||||
const lockFileContent = loadFixture('pyproject.11.toml.lock');
|
const lockFile = 'pyproject.11.toml.lock';
|
||||||
|
const packageFile = 'pyproject.11.toml';
|
||||||
|
|
||||||
|
const lockFileContent = loadFixture(lockFile);
|
||||||
|
|
||||||
describe('modules/manager/poetry/update-locked', () => {
|
describe('modules/manager/poetry/update-locked', () => {
|
||||||
it('detects already updated', () => {
|
it('detects already updated', () => {
|
||||||
const config: UpdateLockedConfig = {
|
const config: UpdateLockedConfig = {
|
||||||
|
packageFile,
|
||||||
|
lockFile,
|
||||||
lockFileContent,
|
lockFileContent,
|
||||||
depName: 'urllib3',
|
depName: 'urllib3',
|
||||||
newVersion: '1.26.3',
|
newVersion: '1.26.3',
|
||||||
|
@ -16,6 +21,8 @@ describe('modules/manager/poetry/update-locked', () => {
|
||||||
|
|
||||||
it('returns unsupported', () => {
|
it('returns unsupported', () => {
|
||||||
const config: UpdateLockedConfig = {
|
const config: UpdateLockedConfig = {
|
||||||
|
packageFile,
|
||||||
|
lockFile,
|
||||||
lockFileContent,
|
lockFileContent,
|
||||||
depName: 'urllib3',
|
depName: 'urllib3',
|
||||||
newVersion: '1.26.4',
|
newVersion: '1.26.4',
|
||||||
|
|
|
@ -133,7 +133,7 @@ export async function updateArtifacts({
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = writeLockUpdates(updates, lockFilePath, lockFileContent);
|
const res = writeLockUpdates(updates, lockFilePath, lockFileContent);
|
||||||
return res ? [res] : null;
|
return [res];
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -123,7 +123,7 @@ export interface Package<T> extends ManagerData<T> {
|
||||||
pinDigests?: boolean;
|
pinDigests?: boolean;
|
||||||
currentRawValue?: string;
|
currentRawValue?: string;
|
||||||
major?: { enabled?: boolean };
|
major?: { enabled?: boolean };
|
||||||
prettyDepType?: any;
|
prettyDepType?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LookupUpdate {
|
export interface LookupUpdate {
|
||||||
|
@ -159,7 +159,7 @@ export interface PackageDependency<T = Record<string, any>> extends Package<T> {
|
||||||
digestOneAndOnly?: boolean;
|
digestOneAndOnly?: boolean;
|
||||||
fixedVersion?: string;
|
fixedVersion?: string;
|
||||||
currentVersion?: string;
|
currentVersion?: string;
|
||||||
lockedVersion?: string;
|
lockedVersion?: string | null;
|
||||||
propSource?: string;
|
propSource?: string;
|
||||||
registryUrls?: string[] | null;
|
registryUrls?: string[] | null;
|
||||||
rangeStrategy?: RangeStrategy;
|
rangeStrategy?: RangeStrategy;
|
||||||
|
@ -224,13 +224,13 @@ export interface BumpPackageVersionResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateLockedConfig {
|
export interface UpdateLockedConfig {
|
||||||
packageFile?: string;
|
packageFile: string;
|
||||||
packageFileContent?: string;
|
packageFileContent?: string;
|
||||||
lockFile?: string;
|
lockFile: string;
|
||||||
lockFileContent?: string;
|
lockFileContent?: string;
|
||||||
depName?: string;
|
depName: string;
|
||||||
currentVersion?: string;
|
currentVersion?: string;
|
||||||
newVersion?: string;
|
newVersion: string;
|
||||||
allowParentUpdates?: boolean;
|
allowParentUpdates?: boolean;
|
||||||
allowHigherOrRemoved?: boolean;
|
allowHigherOrRemoved?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -296,9 +296,9 @@ export interface PostUpdateConfig<T = Record<string, any>>
|
||||||
ignoreScripts?: boolean;
|
ignoreScripts?: boolean;
|
||||||
|
|
||||||
platform?: string;
|
platform?: string;
|
||||||
upgrades?: Upgrade[];
|
upgrades: Upgrade[];
|
||||||
npmLock?: string;
|
npmLock?: string;
|
||||||
yarnLock?: string;
|
yarnLock?: string;
|
||||||
branchName?: string;
|
branchName: string;
|
||||||
reuseExistingBranch?: boolean;
|
reuseExistingBranch?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export interface VersioningApi {
|
||||||
isSingleVersion(version: string): boolean;
|
isSingleVersion(version: string): boolean;
|
||||||
isStable(version: string): boolean;
|
isStable(version: string): boolean;
|
||||||
isValid(input: string): boolean;
|
isValid(input: string): boolean;
|
||||||
isVersion(input: string): boolean;
|
isVersion(input: string | undefined | null): boolean;
|
||||||
|
|
||||||
// digestion of version
|
// digestion of version
|
||||||
getMajor(version: string | SemVer): null | number;
|
getMajor(version: string | SemVer): null | number;
|
||||||
|
|
|
@ -75,7 +75,11 @@ export async function getUpdatedPackageFiles(
|
||||||
} else if (upgrade.isRemediation) {
|
} else if (upgrade.isRemediation) {
|
||||||
const { status, files } = await updateLockedDependency({
|
const { status, files } = await updateLockedDependency({
|
||||||
...upgrade,
|
...upgrade,
|
||||||
|
depName,
|
||||||
|
newVersion,
|
||||||
|
packageFile,
|
||||||
packageFileContent,
|
packageFileContent,
|
||||||
|
lockFile,
|
||||||
lockFileContent,
|
lockFileContent,
|
||||||
allowParentUpdates: true,
|
allowParentUpdates: true,
|
||||||
allowHigherOrRemoved: true,
|
allowHigherOrRemoved: true,
|
||||||
|
@ -100,8 +104,11 @@ export async function getUpdatedPackageFiles(
|
||||||
if (updateLockedDependency) {
|
if (updateLockedDependency) {
|
||||||
const { status, files } = await updateLockedDependency({
|
const { status, files } = await updateLockedDependency({
|
||||||
...upgrade,
|
...upgrade,
|
||||||
lockFile,
|
depName,
|
||||||
|
newVersion,
|
||||||
|
packageFile,
|
||||||
packageFileContent,
|
packageFileContent,
|
||||||
|
lockFile,
|
||||||
lockFileContent,
|
lockFileContent,
|
||||||
allowParentUpdates: false,
|
allowParentUpdates: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,7 +10,11 @@ import type { PostUpdateConfig } from '../../../../../modules/manager/types';
|
||||||
import * as _fs from '../../../../../util/fs/proxies';
|
import * as _fs from '../../../../../util/fs/proxies';
|
||||||
import * as _hostRules from '../../../../../util/host-rules';
|
import * as _hostRules from '../../../../../util/host-rules';
|
||||||
|
|
||||||
const config: PostUpdateConfig = getConfig();
|
const config: PostUpdateConfig = {
|
||||||
|
...getConfig(),
|
||||||
|
upgrades: [],
|
||||||
|
branchName: 'some-branch',
|
||||||
|
};
|
||||||
|
|
||||||
const fs = mocked(_fs);
|
const fs = mocked(_fs);
|
||||||
const lockFiles = mocked(_lockFiles);
|
const lockFiles = mocked(_lockFiles);
|
||||||
|
|
|
@ -39,35 +39,6 @@
|
||||||
"lib/config/validation-helpers/managers.ts",
|
"lib/config/validation-helpers/managers.ts",
|
||||||
"lib/config/validation.ts",
|
"lib/config/validation.ts",
|
||||||
"lib/modules/datasource/github-releases/test/index.ts",
|
"lib/modules/datasource/github-releases/test/index.ts",
|
||||||
"lib/modules/manager/api.ts",
|
|
||||||
"lib/modules/manager/index.ts",
|
|
||||||
"lib/modules/manager/npm/detect.ts",
|
|
||||||
"lib/modules/manager/npm/extract/index.ts",
|
|
||||||
"lib/modules/manager/npm/extract/locked-versions.ts",
|
|
||||||
"lib/modules/manager/npm/extract/monorepo.ts",
|
|
||||||
"lib/modules/manager/npm/extract/npm.ts",
|
|
||||||
"lib/modules/manager/npm/extract/pnpm.ts",
|
|
||||||
"lib/modules/manager/npm/extract/utils.ts",
|
|
||||||
"lib/modules/manager/npm/extract/yarn.ts",
|
|
||||||
"lib/modules/manager/npm/index.ts",
|
|
||||||
"lib/modules/manager/npm/post-update/index.ts",
|
|
||||||
"lib/modules/manager/npm/post-update/lerna.ts",
|
|
||||||
"lib/modules/manager/npm/post-update/node-version.ts",
|
|
||||||
"lib/modules/manager/npm/post-update/npm.ts",
|
|
||||||
"lib/modules/manager/npm/post-update/pnpm.ts",
|
|
||||||
"lib/modules/manager/npm/post-update/yarn.ts",
|
|
||||||
"lib/modules/manager/npm/range.ts",
|
|
||||||
"lib/modules/manager/npm/update/dependency/index.ts",
|
|
||||||
"lib/modules/manager/npm/update/index.ts",
|
|
||||||
"lib/modules/manager/npm/update/locked-dependency/common/parent-version.ts",
|
|
||||||
"lib/modules/manager/npm/update/locked-dependency/index.ts",
|
|
||||||
"lib/modules/manager/npm/update/locked-dependency/package-lock/dep-constraints.ts",
|
|
||||||
"lib/modules/manager/npm/update/locked-dependency/package-lock/get-locked.ts",
|
|
||||||
"lib/modules/manager/npm/update/locked-dependency/package-lock/index.ts",
|
|
||||||
"lib/modules/manager/npm/update/locked-dependency/yarn-lock/get-locked.ts",
|
|
||||||
"lib/modules/manager/npm/update/locked-dependency/yarn-lock/index.ts",
|
|
||||||
"lib/modules/manager/npm/update/locked-dependency/yarn-lock/replace.ts",
|
|
||||||
"lib/modules/manager/npm/update/package-version/index.ts",
|
|
||||||
"lib/modules/platform/api.ts",
|
"lib/modules/platform/api.ts",
|
||||||
"lib/modules/platform/azure/azure-got-wrapper.ts",
|
"lib/modules/platform/azure/azure-got-wrapper.ts",
|
||||||
"lib/modules/platform/azure/azure-helper.ts",
|
"lib/modules/platform/azure/azure-helper.ts",
|
||||||
|
|
Loading…
Reference in a new issue