From 16337594973b9375b179239de4a031db1140a35d Mon Sep 17 00:00:00 2001 From: Michael Kriese <michael.kriese@visualon.de> Date: Wed, 23 Nov 2022 10:35:38 +0100 Subject: [PATCH] feat(manager/nuget): support install mode (#19049) --- docs/usage/self-hosted-configuration.md | 1 + .../__snapshots__/artifacts.spec.ts.snap | 221 ----------------- lib/modules/manager/nuget/artifacts.spec.ts | 232 ++++++++++++++++-- lib/modules/manager/nuget/artifacts.ts | 5 +- lib/util/exec/containerbase.ts | 5 + 5 files changed, 224 insertions(+), 240 deletions(-) delete mode 100644 lib/modules/manager/nuget/__snapshots__/artifacts.spec.ts.snap diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md index 43acd75998..df9bf7ba4c 100644 --- a/docs/usage/self-hosted-configuration.md +++ b/docs/usage/self-hosted-configuration.md @@ -159,6 +159,7 @@ Supported tools for dynamic install are: - `bundler` - `cargo` - `composer` +- `dotnet` - `flux` - `gradle-wrapper` - `jb` diff --git a/lib/modules/manager/nuget/__snapshots__/artifacts.spec.ts.snap b/lib/modules/manager/nuget/__snapshots__/artifacts.spec.ts.snap deleted file mode 100644 index 9a15a57672..0000000000 --- a/lib/modules/manager/nuget/__snapshots__/artifacts.spec.ts.snap +++ /dev/null @@ -1,221 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`modules/manager/nuget/artifacts aborts if lock file is unchanged 1`] = ` -[ - { - "cmd": "dotnet restore 'path/with space/project.csproj' --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config", - "options": { - "cwd": "/tmp/github/some/repo", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "NUGET_PACKAGES": "/tmp/renovate/cache/__renovate-private-cache/nuget/packages", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; - -exports[`modules/manager/nuget/artifacts authenticates at registries 1`] = ` -[ - { - "cmd": "dotnet nuget add source https://my-registry.example.org/ --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config --name myRegistry --username some-username --password some-password --store-password-in-clear-text", - "options": { - "cwd": "/tmp/github/some/repo", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "NUGET_PACKAGES": "/tmp/renovate/cache/__renovate-private-cache/nuget/packages", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, - { - "cmd": "dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config", - "options": { - "cwd": "/tmp/github/some/repo", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "NUGET_PACKAGES": "/tmp/renovate/cache/__renovate-private-cache/nuget/packages", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; - -exports[`modules/manager/nuget/artifacts performs lock file maintenance 1`] = ` -[ - { - "cmd": "dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config", - "options": { - "cwd": "/tmp/github/some/repo", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "NUGET_PACKAGES": "/tmp/renovate/cache/__renovate-private-cache/nuget/packages", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; - -exports[`modules/manager/nuget/artifacts strips protocol version from feed url 1`] = ` -[ - { - "cmd": "dotnet nuget add source https://my-registry.example.org/ --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config --name myRegistry", - "options": { - "cwd": "/tmp/github/some/repo", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "NUGET_PACKAGES": "/tmp/renovate/cache/__renovate-private-cache/nuget/packages", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, - { - "cmd": "dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config", - "options": { - "cwd": "/tmp/github/some/repo", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "NUGET_PACKAGES": "/tmp/renovate/cache/__renovate-private-cache/nuget/packages", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; - -exports[`modules/manager/nuget/artifacts supports docker mode 1`] = ` -[ - { - "cmd": "docker pull renovate/dotnet", - "options": { - "encoding": "utf-8", - }, - }, - { - "cmd": "docker ps --filter name=renovate_dotnet -aq", - "options": { - "encoding": "utf-8", - }, - }, - { - "cmd": "docker run --rm --name=renovate_dotnet --label=renovate_child -v "/tmp/github/some/repo":"/tmp/github/some/repo" -v "/tmp/renovate/cache":"/tmp/renovate/cache" -e NUGET_PACKAGES -e BUILDPACK_CACHE_DIR -e CONTAINERBASE_CACHE_DIR -w "/tmp/github/some/repo" renovate/dotnet bash -l -c "dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config"", - "options": { - "cwd": "/tmp/github/some/repo", - "encoding": "utf-8", - "env": { - "BUILDPACK_CACHE_DIR": "/tmp/renovate/cache/containerbase", - "CONTAINERBASE_CACHE_DIR": "/tmp/renovate/cache/containerbase", - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "NUGET_PACKAGES": "/tmp/renovate/cache/__renovate-private-cache/nuget/packages", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; - -exports[`modules/manager/nuget/artifacts supports global mode 1`] = ` -[ - { - "cmd": "dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config", - "options": { - "cwd": "/tmp/github/some/repo", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "NUGET_PACKAGES": "/tmp/renovate/cache/__renovate-private-cache/nuget/packages", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; - -exports[`modules/manager/nuget/artifacts updates lock file 1`] = ` -[ - { - "cmd": "dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config", - "options": { - "cwd": "/tmp/github/some/repo", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "NUGET_PACKAGES": "/tmp/renovate/cache/__renovate-private-cache/nuget/packages", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; diff --git a/lib/modules/manager/nuget/artifacts.spec.ts b/lib/modules/manager/nuget/artifacts.spec.ts index b4f844ef4c..639b3572e0 100644 --- a/lib/modules/manager/nuget/artifacts.spec.ts +++ b/lib/modules/manager/nuget/artifacts.spec.ts @@ -18,10 +18,10 @@ jest.mock('./util'); const { getConfiguredRegistries, getDefaultRegistries } = mocked(util); const hostRules = mocked(_hostRules); -// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -const realFs = jest.requireActual( - '../../../util/fs' -) as typeof import('../../../util/fs'); +const realFs = + jest.requireActual<typeof import('../../../util/fs')>('../../../util/fs'); + +process.env.CONTAINERBASE = 'true'; const adminConfig: RepoGlobalConfig = { // `join` fixes Windows CI @@ -83,7 +83,18 @@ describe('modules/manager/nuget/artifacts', () => { config, }) ).toBeNull(); - expect(execSnapshots).toMatchSnapshot(); + expect(execSnapshots).toMatchObject([ + { + cmd: "dotnet restore 'path/with space/project.csproj' --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config", + options: { + cwd: '/tmp/github/some/repo', + env: { + NUGET_PACKAGES: + '/tmp/renovate/cache/__renovate-private-cache/nuget/packages', + }, + }, + }, + ]); }); it('updates lock file', async () => { @@ -103,8 +114,27 @@ describe('modules/manager/nuget/artifacts', () => { newPackageFileContent: '{}', config, }) - ).not.toBeNull(); - expect(execSnapshots).toMatchSnapshot(); + ).toEqual([ + { + file: { + contents: 'New packages.lock.json', + path: 'packages.lock.json', + type: 'addition', + }, + }, + ]); + expect(execSnapshots).toMatchObject([ + { + cmd: 'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config', + options: { + cwd: '/tmp/github/some/repo', + env: { + NUGET_PACKAGES: + '/tmp/renovate/cache/__renovate-private-cache/nuget/packages', + }, + }, + }, + ]); }); it('does not update lock file when non-proj file is changed', async () => { @@ -169,8 +199,27 @@ describe('modules/manager/nuget/artifacts', () => { isLockFileMaintenance: true, }, }) - ).not.toBeNull(); - expect(execSnapshots).toMatchSnapshot(); + ).toEqual([ + { + file: { + contents: 'New packages.lock.json', + path: 'packages.lock.json', + type: 'addition', + }, + }, + ]); + expect(execSnapshots).toMatchObject([ + { + cmd: 'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config', + options: { + cwd: '/tmp/github/some/repo', + env: { + NUGET_PACKAGES: + '/tmp/renovate/cache/__renovate-private-cache/nuget/packages', + }, + }, + }, + ]); }); it('supports docker mode', async () => { @@ -189,10 +238,104 @@ describe('modules/manager/nuget/artifacts', () => { packageFileName: 'project.csproj', updatedDeps: [{ depName: 'dep' }], newPackageFileContent: '{}', - config, + config: { ...config, constraints: { dotnet: '7.0.100' } }, }) - ).not.toBeNull(); - expect(execSnapshots).toMatchSnapshot(); + ).toEqual([ + { + file: { + contents: 'New packages.lock.json', + path: 'packages.lock.json', + type: 'addition', + }, + }, + ]); + expect(execSnapshots).toMatchObject([ + { + cmd: 'docker pull renovate/sidecar', + }, + { + cmd: 'docker ps --filter name=renovate_sidecar -aq', + }, + { + cmd: + 'docker run --rm --name=renovate_sidecar --label=renovate_child ' + + '-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' + + '-v "/tmp/renovate/cache":"/tmp/renovate/cache" ' + + '-e NUGET_PACKAGES ' + + '-e BUILDPACK_CACHE_DIR ' + + '-e CONTAINERBASE_CACHE_DIR ' + + '-w "/tmp/github/some/repo" ' + + 'renovate/sidecar ' + + 'bash -l -c "' + + 'install-tool dotnet 7.0.100' + + ' && ' + + 'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config' + + '"', + options: { + env: { + BUILDPACK_CACHE_DIR: '/tmp/renovate/cache/containerbase', + CONTAINERBASE_CACHE_DIR: '/tmp/renovate/cache/containerbase', + NUGET_PACKAGES: + '/tmp/renovate/cache/__renovate-private-cache/nuget/packages', + }, + }, + }, + ]); + }); + + it('supports install mode', async () => { + GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); + const execSnapshots = mockExecAll(); + fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json'); + fs.getFileContentMap + .mockResolvedValueOnce({ + 'packages.lock.json': 'Current packages.lock.json', + }) + .mockResolvedValueOnce({ + 'packages.lock.json': 'New packages.lock.json', + }); + expect( + await nuget.updateArtifacts({ + packageFileName: 'project.csproj', + updatedDeps: [{ depName: 'dep' }], + newPackageFileContent: '{}', + config: { ...config, constraints: { dotnet: '7.0.100' } }, + }) + ).toEqual([ + { + file: { + contents: 'New packages.lock.json', + path: 'packages.lock.json', + type: 'addition', + }, + }, + ]); + expect(execSnapshots).toMatchObject([ + { + cmd: 'install-tool dotnet 7.0.100', + options: { + cwd: '/tmp/github/some/repo', + env: { + BUILDPACK_CACHE_DIR: '/tmp/renovate/cache/containerbase', + CONTAINERBASE_CACHE_DIR: '/tmp/renovate/cache/containerbase', + NUGET_PACKAGES: + '/tmp/renovate/cache/__renovate-private-cache/nuget/packages', + }, + }, + }, + { + cmd: 'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config', + options: { + cwd: '/tmp/github/some/repo', + env: { + BUILDPACK_CACHE_DIR: '/tmp/renovate/cache/containerbase', + CONTAINERBASE_CACHE_DIR: '/tmp/renovate/cache/containerbase', + NUGET_PACKAGES: + '/tmp/renovate/cache/__renovate-private-cache/nuget/packages', + }, + }, + }, + ]); }); it('supports global mode', async () => { @@ -213,11 +356,31 @@ describe('modules/manager/nuget/artifacts', () => { newPackageFileContent: '{}', config, }) - ).not.toBeNull(); - expect(execSnapshots).toMatchSnapshot(); + ).toEqual([ + { + file: { + contents: 'New packages.lock.json', + path: 'packages.lock.json', + type: 'addition', + }, + }, + ]); + expect(execSnapshots).toMatchObject([ + { + cmd: 'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config', + options: { + cwd: '/tmp/github/some/repo', + env: { + NUGET_PACKAGES: + '/tmp/renovate/cache/__renovate-private-cache/nuget/packages', + }, + }, + }, + ]); }); it('catches errors', async () => { + const execSnapshots = mockExecAll(); fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json'); fs.getFileContentMap.mockResolvedValueOnce({ 'packages.lock.json': 'Current packages.lock.json', @@ -240,6 +403,7 @@ describe('modules/manager/nuget/artifacts', () => { }, }, ]); + expect(execSnapshots).toBeEmptyArray(); }); it('authenticates at registries', async () => { @@ -277,8 +441,25 @@ describe('modules/manager/nuget/artifacts', () => { newPackageFileContent: '{}', config, }) - ).not.toBeNull(); - expect(execSnapshots).toMatchSnapshot(); + ).toEqual([ + { + file: { + contents: 'New packages.lock.json', + path: 'packages.lock.json', + type: 'addition', + }, + }, + ]); + expect(execSnapshots).toMatchObject([ + { + cmd: + 'dotnet nuget add source https://my-registry.example.org/ --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config ' + + '--name myRegistry --username some-username --password some-password --store-password-in-clear-text', + }, + { + cmd: 'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config', + }, + ]); }); it('strips protocol version from feed url', async () => { @@ -305,7 +486,22 @@ describe('modules/manager/nuget/artifacts', () => { newPackageFileContent: '{}', config, }) - ).not.toBeNull(); - expect(execSnapshots).toMatchSnapshot(); + ).toEqual([ + { + file: { + contents: 'New packages.lock.json', + path: 'packages.lock.json', + type: 'addition', + }, + }, + ]); + expect(execSnapshots).toMatchObject([ + { + cmd: 'dotnet nuget add source https://my-registry.example.org/ --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config --name myRegistry', + }, + { + cmd: 'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config', + }, + ]); }); }); diff --git a/lib/modules/manager/nuget/artifacts.ts b/lib/modules/manager/nuget/artifacts.ts index a88dfdb95b..af5f31c718 100644 --- a/lib/modules/manager/nuget/artifacts.ts +++ b/lib/modules/manager/nuget/artifacts.ts @@ -69,9 +69,12 @@ async function runDotnetRestore( const execOptions: ExecOptions = { docker: { - image: 'dotnet', + image: 'sidecar', }, extraEnv: { NUGET_PACKAGES: join(nugetCacheDir, 'packages') }, + toolConstraints: [ + { toolName: 'dotnet', constraint: config.constraints?.dotnet }, + ], }; const nugetConfigFile = join(nugetCacheDir, `nuget.config`); diff --git a/lib/util/exec/containerbase.ts b/lib/util/exec/containerbase.ts index 4c9ef8d3e2..8c37a6f591 100644 --- a/lib/util/exec/containerbase.ts +++ b/lib/util/exec/containerbase.ts @@ -35,6 +35,11 @@ const allToolConfig: Record<string, ToolConfig> = { depName: 'corepack', versioning: npmVersioningId, }, + dotnet: { + datasource: 'dotnet', + depName: 'dotnet-sdk', + versioning: semverVersioningId, + }, erlang: { datasource: 'github-releases', depName: 'containerbase/erlang-prebuild', -- GitLab