diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 4fb6864350da60ebccef566c8c1c3c8d32d71cdc..9cb987176cc5fa1d46001b0943b663d0b6b906d3 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -2995,7 +2995,6 @@ Managers which do not support replacement: - `git-submodules` - `gomod` - `gradle` -- `hermit` - `homebrew` - `maven` - `regex` diff --git a/lib/modules/manager/hermit/artifacts.spec.ts b/lib/modules/manager/hermit/artifacts.spec.ts index f76e605367cffea74fc2e76c1698ba8219e8d9bd..510be6989e67f6d50c5fc93ba82b5b3fed4706f4 100644 --- a/lib/modules/manager/hermit/artifacts.spec.ts +++ b/lib/modules/manager/hermit/artifacts.spec.ts @@ -135,12 +135,244 @@ describe('modules/manager/hermit/artifacts', () => { ]); }); - it('should fail on error getting link content', async () => { + it('should uninstall old package for name replacement', async () => { + lstatsMock.mockResolvedValue(true); + + readlinkMock.mockResolvedValue('hermit'); + GlobalConfig.set({ localDir: '' }); + + const execSnapshots = mockExecAll(); + getRepoStatusMock.mockResolvedValue( + partial<StatusResult>({ + not_added: [], + deleted: [], + modified: ['bin/java', 'bin/javac', 'bin/jar'], + renamed: [ + { + from: 'bin/.openjdk-17.0.3', + to: 'bin/.openjdk-17.0.4.1_1', + }, + ], + created: [], + }), + ); + + const res = await updateArtifacts( + partial<UpdateArtifact>({ + updatedDeps: [ + { + depName: 'openjdk', + newName: 'openjre', + currentVersion: '17.0.3', + newValue: '17.0.4.1_1', + updateType: 'replacement', + }, + ], + packageFileName: 'go/bin/hermit', + }), + ); + + expect(execSnapshots).toMatchObject([ + { cmd: './hermit uninstall openjdk' }, + { cmd: './hermit install openjre-17.0.4.1_1' }, + ]); + + expect(res).toStrictEqual([ + { + file: { + path: 'bin/.openjdk-17.0.3', + type: 'deletion', + }, + }, + { + file: { + contents: 'hermit', + isSymlink: true, + isExecutable: undefined, + path: 'bin/.openjdk-17.0.4.1_1', + type: 'addition', + }, + }, + { + file: { + path: 'bin/java', + type: 'deletion', + }, + }, + { + file: { + contents: 'hermit', + isSymlink: true, + isExecutable: undefined, + path: 'bin/java', + type: 'addition', + }, + }, + { + file: { + path: 'bin/javac', + type: 'deletion', + }, + }, + { + file: { + contents: 'hermit', + isSymlink: true, + isExecutable: undefined, + path: 'bin/javac', + type: 'addition', + }, + }, + { + file: { + path: 'bin/jar', + type: 'deletion', + }, + }, + { + file: { + contents: 'hermit', + isSymlink: true, + isExecutable: undefined, + path: 'bin/jar', + type: 'addition', + }, + }, + ]); + }); + + it('should not uninstall package for version only replcaement', async () => { + lstatsMock.mockResolvedValue(true); + + readlinkMock.mockResolvedValue('hermit'); + GlobalConfig.set({ localDir: '' }); + + const execSnapshots = mockExecAll(); + getRepoStatusMock.mockResolvedValue( + partial<StatusResult>({ + not_added: [], + deleted: [], + modified: ['bin/go'], + renamed: [ + { + from: 'bin/.go-1.19', + to: 'bin/.go-1.18', + }, + ], + created: [], + }), + ); + + const res = await updateArtifacts( + partial<UpdateArtifact>({ + updatedDeps: [ + { + depName: 'go', + newName: 'go', + currentVersion: '1.19', + newValue: '1.18', + updateType: 'replacement', + }, + ], + packageFileName: 'go/bin/hermit', + }), + ); + + expect(execSnapshots).toMatchObject([ + { cmd: './hermit install go-1.18' }, + ]); + + expect(res).toStrictEqual([ + { + file: { + path: 'bin/.go-1.19', + type: 'deletion', + }, + }, + { + file: { + contents: 'hermit', + isSymlink: true, + isExecutable: undefined, + path: 'bin/.go-1.18', + type: 'addition', + }, + }, + { + file: { + path: 'bin/go', + type: 'deletion', + }, + }, + { + file: { + contents: 'hermit', + isSymlink: true, + isExecutable: undefined, + path: 'bin/go', + type: 'addition', + }, + }, + ]); + }); + + it('should fail if uninstallation fails', async () => { lstatsMock.mockResolvedValue(true); readlinkMock.mockResolvedValue(null); GlobalConfig.set({ localDir: '' }); + mockExecAll( + new ExecError('', { + stdout: '', + stderr: 'error executing hermit uninstall', + cmd: '', + options: { + encoding: 'utf-8', + }, + }), + ); + + getRepoStatusMock.mockResolvedValue( + partial<StatusResult>({ + not_added: [], + deleted: [], + modified: [], + created: [], + renamed: [], + }), + ); + + const res = await updateArtifacts( + partial<UpdateArtifact>({ + updatedDeps: [ + { + depName: 'openjdk', + newName: 'openjre', + currentVersion: '17.0.3', + newValue: '17.0.4.1_1', + updateType: 'replacement', + }, + ], + packageFileName: 'go/bin/hermit', + }), + ); + + expect(res).toEqual([ + { + artifactError: { + lockFile: 'from: openjdk-17.0.3, to: openjdk', + stderr: 'error executing hermit uninstall', + }, + }, + ]); + }); + + it('should fail on error getting link content', async () => { + lstatsMock.mockResolvedValue(true); + + readlinkMock.mockResolvedValue(null); + GlobalConfig.set({ localDir: '' }); mockExecAll(); getRepoStatusMock.mockResolvedValue( diff --git a/lib/modules/manager/hermit/artifacts.ts b/lib/modules/manager/hermit/artifacts.ts index c860347ff26341cfddba79da0e2842807e684f89..2d53df9a0902499527dfbb8487f12941c671fa40 100644 --- a/lib/modules/manager/hermit/artifacts.ts +++ b/lib/modules/manager/hermit/artifacts.ts @@ -177,6 +177,8 @@ async function updateHermitPackage(update: UpdateArtifact): Promise<void> { const toInstall = []; const from = []; + // storing the old package for replacement + const toUninstall = []; for (const pkg of update.updatedDeps) { if (!pkg.depName || !pkg.currentVersion || !pkg.newValue) { @@ -197,12 +199,18 @@ async function updateHermitPackage(update: UpdateArtifact): Promise<void> { } const depName = pkg.depName; + const newName = pkg.newName; const currentVersion = pkg.currentVersion; const newValue = pkg.newValue; const fromPackage = getHermitPackage(depName, currentVersion); - const toPackage = getHermitPackage(depName, newValue); + // newName will be available for replacement + const toPackage = getHermitPackage(newName ?? depName, newValue); toInstall.push(toPackage); from.push(fromPackage); + // skips uninstall for version only replacement + if (pkg.updateType === 'replacement' && newName !== depName) { + toUninstall.push(depName); + } } const execOptions: ExecOptions = { @@ -211,9 +219,32 @@ async function updateHermitPackage(update: UpdateArtifact): Promise<void> { cwdFile: update.packageFileName, }; - const packagesToInstall = toInstall.join(' '); const fromPackages = from.map(quote).join(' '); + // when a name replacement happens, need to uninstall the old package + if (toUninstall.length > 0) { + const packagesToUninstall = toUninstall.join(' '); + const uninstallCommands = `./hermit uninstall ${packagesToUninstall}`; + + try { + const result = await exec(uninstallCommands, execOptions); + logger.trace( + { stdout: result.stdout }, + `hermit uninstall command stdout`, + ); + } catch (e) { + logger.warn({ err: e }, `error uninstall hermit package for replacement`); + throw new UpdateHermitError( + fromPackages, + packagesToUninstall, + e.stderr, + e.stdout, + ); + } + } + + const packagesToInstall = toInstall.join(' '); + const execCommands = `./hermit install ${packagesToInstall}`; logger.debug( {