diff --git a/lib/manager/nuget/artifacts.ts b/lib/manager/nuget/artifacts.ts index ede2e3b0081cd30ed673f522fde4cdd4499ef0d1..549947424bcd12d5c8401a2c200f25dd933a69a1 100644 --- a/lib/manager/nuget/artifacts.ts +++ b/lib/manager/nuget/artifacts.ts @@ -1,6 +1,5 @@ import { join } from 'path'; import { quote } from 'shlex'; -import { GlobalConfig } from '../../config/global'; import { TEMPORARY_ERROR } from '../../constants/error-messages'; import { NugetDatasource } from '../../datasource/nuget'; import { parseRegistryUrl } from '../../datasource/nuget/common'; @@ -33,10 +32,8 @@ async function addSourceCmds( config: UpdateArtifactsConfig, nugetConfigFile: string ): Promise<string[]> { - const { localDir } = GlobalConfig.get(); const registries = - (await getConfiguredRegistries(packageFileName, localDir)) || - getDefaultRegistries(); + (await getConfiguredRegistries(packageFileName)) || getDefaultRegistries(); const result = []; for (const registry of registries) { const { username, password } = hostRules.find({ diff --git a/lib/manager/nuget/extract.ts b/lib/manager/nuget/extract.ts index df4cd40ee5faf8df061dcb9acfd3bb9aabf11998..403b8a8a00a8a0a1ef632fd14dbdda1ff40b5834 100644 --- a/lib/manager/nuget/extract.ts +++ b/lib/manager/nuget/extract.ts @@ -1,5 +1,4 @@ import { XmlDocument, XmlElement, XmlNode } from 'xmldoc'; -import { GlobalConfig } from '../../config/global'; import { NugetDatasource } from '../../datasource/nuget'; import { logger } from '../../logger'; import { getSiblingFileName, localPathExists } from '../../util/fs'; @@ -74,8 +73,7 @@ export async function extractPackageFile( ): Promise<PackageFile | null> { logger.trace({ packageFile }, 'nuget.extractPackageFile()'); - const { localDir } = GlobalConfig.get(); - const registries = await getConfiguredRegistries(packageFile, localDir); + const registries = await getConfiguredRegistries(packageFile); const registryUrls = registries ? registries.map((registry) => registry.url) : undefined; diff --git a/lib/manager/nuget/util.ts b/lib/manager/nuget/util.ts index d33f255dbad8d647063d249f5b3a67395c79bd07..9174813e7c11c5cb3398f55115bac33db20fa8fd 100644 --- a/lib/manager/nuget/util.ts +++ b/lib/manager/nuget/util.ts @@ -1,16 +1,15 @@ import cryptoRandomString from 'crypto-random-string'; -import findUp from 'find-up'; import upath from 'upath'; import { XmlDocument } from 'xmldoc'; import { defaultRegistryUrls } from '../../datasource/nuget'; import { logger } from '../../logger'; -import { readFile } from '../../util/fs'; +import { findUpLocal, readLocalFile } from '../../util/fs'; import { regEx } from '../../util/regex'; import type { Registry } from './types'; async function readFileAsXmlDocument(file: string): Promise<XmlDocument> { try { - return new XmlDocument(await readFile(file, 'utf8')); + return new XmlDocument(await readLocalFile(file, 'utf8')); } catch (err) { logger.debug({ err }, `failed to parse '${file}' as XML document`); return undefined; @@ -31,22 +30,16 @@ export function getDefaultRegistries(): Registry[] { } export async function getConfiguredRegistries( - packageFile: string, - localDir: string + packageFile: string ): Promise<Registry[] | undefined> { // Valid file names taken from https://github.com/NuGet/NuGet.Client/blob/f64621487c0b454eda4b98af853bf4a528bef72a/src/NuGet.Core/NuGet.Configuration/Settings/Settings.cs#L34 const nuGetConfigFileNames = ['nuget.config', 'NuGet.config', 'NuGet.Config']; // normalize paths, otherwise startsWith can fail because of path delimitter mismatch - const normalizedLocalDir = upath.normalizeSafe(localDir); - const nuGetConfigPath = await findUp(nuGetConfigFileNames, { - cwd: upath.dirname(upath.join(normalizedLocalDir, packageFile)), - type: 'file', - }); - - if ( - !nuGetConfigPath || - upath.normalizeSafe(nuGetConfigPath).startsWith(normalizedLocalDir) !== true - ) { + const nuGetConfigPath = await findUpLocal( + nuGetConfigFileNames, + upath.dirname(packageFile) + ); + if (!nuGetConfigPath) { return undefined; } diff --git a/lib/util/fs/index.spec.ts b/lib/util/fs/index.spec.ts index ce9b0b6e40d5fde9d17de4fa6fa5c9d4e12f5bac..517c8b839b33407cc9195329c8448be87b4ce146 100644 --- a/lib/util/fs/index.spec.ts +++ b/lib/util/fs/index.spec.ts @@ -1,14 +1,15 @@ +import _findUp from 'find-up'; import { withDir } from 'tmp-promise'; import { join } from 'upath'; import { envMock } from '../../../test/exec-util'; -import { mocked } from '../../../test/util'; +import { env, mockedFunction } from '../../../test/util'; import { GlobalConfig } from '../../config/global'; -import * as _env from '../exec/env'; import { ensureCacheDir, ensureLocalDir, exists, findLocalSiblingOrParent, + findUpLocal, getSubDirectory, localPathExists, localPathIsFile, @@ -18,7 +19,9 @@ import { } from '.'; jest.mock('../../util/exec/env'); -const env = mocked(_env); +jest.mock('find-up'); + +const findUp = mockedFunction(_findUp); describe('util/fs/index', () => { describe('readLocalFile', () => { @@ -210,4 +213,28 @@ describe('util/fs/index', () => { ).toBeFalse(); }); }); + + describe('findUpLocal', () => { + beforeEach(() => { + GlobalConfig.set({ localDir: '/abs/path/to/local/dir' }); + }); + + it('returns relative path for file', async () => { + findUp.mockResolvedValueOnce('/abs/path/to/local/dir/subdir/file.json'); + const res = await findUpLocal('file.json', 'subdir/subdir2'); + expect(res).toBe('subdir/file.json'); + }); + + it('returns null if nothing found', async () => { + findUp.mockResolvedValueOnce(undefined); + const res = await findUpLocal('file.json', 'subdir/subdir2'); + expect(res).toBeNull(); + }); + + it('returns undefined if found a file outside of localDir', async () => { + findUp.mockResolvedValueOnce('/abs/path/to/file.json'); + const res = await findUpLocal('file.json', 'subdir/subdir2'); + expect(res).toBeNull(); + }); + }); }); diff --git a/lib/util/fs/index.ts b/lib/util/fs/index.ts index 41151486849459ef79d4cb580196cc1fb2dfa536..2b4ff40098722642d924b4cd8c621aa4027bd00e 100644 --- a/lib/util/fs/index.ts +++ b/lib/util/fs/index.ts @@ -1,6 +1,7 @@ import stream from 'stream'; import util from 'util'; import is from '@sindresorhus/is'; +import findUp from 'find-up'; import fs from 'fs-extra'; import upath from 'upath'; import { GlobalConfig } from '../../config/global'; @@ -162,3 +163,34 @@ export function localPathIsFile(pathName: string): Promise<boolean> { .then((s) => s.isFile()) .catch(() => false); } + +/** + * Find a file or directory by walking up parent directories within localDir + */ + +export async function findUpLocal( + fileName: string | string[], + cwd: string +): Promise<string | null> { + const { localDir } = GlobalConfig.get(); + const absoluteCwd = upath.join(localDir, cwd); + const normalizedAbsoluteCwd = upath.normalizeSafe(absoluteCwd); + const res = await findUp(fileName, { + cwd: normalizedAbsoluteCwd, + type: 'file', + }); + // Return null if nothing found + if (!is.nonEmptyString(res) || !is.nonEmptyString(localDir)) { + return null; + } + // Return relative path if file is inside of local dir + if (res.startsWith(localDir)) { + let relativePath = res.replace(localDir, ''); + if (relativePath.startsWith('/')) { + relativePath = relativePath.substr(1); + } + return relativePath; + } + // Return null if found file is outside of localDir + return null; +} diff --git a/test/util.ts b/test/util.ts index 7b9cded36af79bda83075a4fc40228eaea57f519..694da0e0cec5a847fcf2cd3dc71fff889e30bef2 100644 --- a/test/util.ts +++ b/test/util.ts @@ -16,7 +16,17 @@ import * as _hostRules from '../lib/util/host-rules'; * @param module module which is mocked by `jest.mock` */ export function mocked<T>(module: T): jest.Mocked<T> { - return module as never; + return module as jest.Mocked<T>; +} + +/** + * Simple wrapper for getting mocked version of a function + * @param func function which is mocked by `jest.mock` + */ +export function mockedFunction<T extends (...args: any[]) => any>( + func: T +): jest.MockedFunction<T> { + return func as jest.MockedFunction<T>; } /**