mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-31 09:41:46 +00:00
cca6227dfa
* fix(git): preserve approved runtime config with authentication * test(git): remove redundant authentication cases * test(git): update post-upgrade environment assertions * test(hermit): cover uninstall setup failure * refactor(git): clarify authentication host types * refactor(git): coerce exec options * refactor(git): coerce docker env vars * refactor(exec): coerce docker env vars
691 lines
17 KiB
TypeScript
691 lines
17 KiB
TypeScript
import { exec, mockExecAll } from '~test/exec-util.ts';
|
|
import { hostRules, partial } from '~test/util.ts';
|
|
import { GlobalConfig } from '../../../config/global.ts';
|
|
import { ExecError } from '../../../util/exec/exec-error.ts';
|
|
import {
|
|
localPathIsSymbolicLink,
|
|
readLocalSymlink,
|
|
} from '../../../util/fs/index.ts';
|
|
import { getRepoStatus } from '../../../util/git/index.ts';
|
|
import type { StatusResult } from '../../../util/git/types.ts';
|
|
import type { UpdateArtifact } from '../types.ts';
|
|
import { updateArtifacts } from './index.ts';
|
|
|
|
vi.mock('../../../util/fs/index.ts');
|
|
|
|
const getRepoStatusMock = vi.mocked(getRepoStatus);
|
|
|
|
const lstatsMock = vi.mocked(localPathIsSymbolicLink);
|
|
const readlinkMock = vi.mocked(readLocalSymlink);
|
|
|
|
describe('modules/manager/hermit/artifacts', () => {
|
|
describe('updateArtifacts', () => {
|
|
it('should run hermit install for packages and return updated files', async () => {
|
|
lstatsMock.mockResolvedValue(true);
|
|
|
|
readlinkMock.mockResolvedValue('hermit');
|
|
GlobalConfig.set({ localDir: '', binarySource: 'global' });
|
|
|
|
const execSnapshots = mockExecAll();
|
|
getRepoStatusMock.mockResolvedValue(
|
|
partial<StatusResult>({
|
|
not_added: ['bin/go-1.17.1'],
|
|
deleted: ['bin/go-1.17'],
|
|
modified: ['bin/go', 'bin/jq'],
|
|
created: ['bin/jq-extra'],
|
|
renamed: [
|
|
{
|
|
from: 'bin/jq-1.5',
|
|
to: 'bin/jq-1.6',
|
|
},
|
|
],
|
|
}),
|
|
);
|
|
|
|
const res = await updateArtifacts(
|
|
partial<UpdateArtifact>({
|
|
updatedDeps: [
|
|
{
|
|
depName: 'go',
|
|
currentVersion: '1.17',
|
|
newValue: '1.17.1',
|
|
},
|
|
{
|
|
depName: 'jq',
|
|
currentVersion: '1.5',
|
|
newValue: '1.6',
|
|
},
|
|
],
|
|
packageFileName: 'go/bin/hermit',
|
|
}),
|
|
);
|
|
|
|
expect(execSnapshots).toMatchObject([
|
|
{ cmd: './hermit install go-1.17.1 jq-1.6' },
|
|
]);
|
|
|
|
expect(res).toStrictEqual([
|
|
{
|
|
file: {
|
|
path: 'bin/jq-1.5',
|
|
type: 'deletion',
|
|
},
|
|
},
|
|
{
|
|
file: {
|
|
contents: 'hermit',
|
|
isSymlink: true,
|
|
isExecutable: undefined,
|
|
path: 'bin/jq-1.6',
|
|
type: 'addition',
|
|
},
|
|
},
|
|
{
|
|
file: {
|
|
path: 'bin/go',
|
|
type: 'deletion',
|
|
},
|
|
},
|
|
{
|
|
file: {
|
|
contents: 'hermit',
|
|
isSymlink: true,
|
|
isExecutable: undefined,
|
|
path: 'bin/go',
|
|
type: 'addition',
|
|
},
|
|
},
|
|
{
|
|
file: {
|
|
path: 'bin/jq',
|
|
type: 'deletion',
|
|
},
|
|
},
|
|
{
|
|
file: {
|
|
contents: 'hermit',
|
|
isSymlink: true,
|
|
isExecutable: undefined,
|
|
path: 'bin/jq',
|
|
type: 'addition',
|
|
},
|
|
},
|
|
{
|
|
file: {
|
|
contents: 'hermit',
|
|
isSymlink: true,
|
|
isExecutable: undefined,
|
|
path: 'bin/jq-extra',
|
|
type: 'addition',
|
|
},
|
|
},
|
|
{
|
|
file: {
|
|
contents: 'hermit',
|
|
isSymlink: true,
|
|
isExecutable: undefined,
|
|
path: 'bin/go-1.17.1',
|
|
type: 'addition',
|
|
},
|
|
},
|
|
{
|
|
file: {
|
|
path: 'bin/go-1.17',
|
|
type: 'deletion',
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('should uninstall old package for name replacement', async () => {
|
|
lstatsMock.mockResolvedValue(true);
|
|
|
|
readlinkMock.mockResolvedValue('hermit');
|
|
GlobalConfig.set({ localDir: '', binarySource: 'global' });
|
|
|
|
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: '', binarySource: 'global' });
|
|
|
|
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.each([
|
|
{
|
|
name: 'returns an update error when uninstallation execution fails',
|
|
error: new ExecError('', {
|
|
stdout: '',
|
|
stderr: 'error executing hermit uninstall',
|
|
cmd: '',
|
|
options: {},
|
|
}),
|
|
artifactError: {
|
|
fileName: 'from: openjdk-17.0.3, to: openjdk',
|
|
stderr: 'error executing hermit uninstall',
|
|
},
|
|
},
|
|
{
|
|
name: 'returns a generic error when uninstallation setup fails',
|
|
error: new Error('unexpected environment preparation failure'),
|
|
artifactError: {
|
|
stderr: 'unexpected environment preparation failure',
|
|
},
|
|
},
|
|
])('$name', async ({ error, artifactError }) => {
|
|
lstatsMock.mockResolvedValue(true);
|
|
|
|
readlinkMock.mockResolvedValue(null);
|
|
GlobalConfig.set({ localDir: '', binarySource: 'global' });
|
|
|
|
mockExecAll(error);
|
|
|
|
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,
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('should fail on error getting link content', async () => {
|
|
lstatsMock.mockResolvedValue(true);
|
|
|
|
readlinkMock.mockResolvedValue(null);
|
|
GlobalConfig.set({ localDir: '', binarySource: 'global' });
|
|
mockExecAll();
|
|
|
|
getRepoStatusMock.mockResolvedValue(
|
|
partial<StatusResult>({
|
|
not_added: [],
|
|
deleted: [],
|
|
modified: [],
|
|
created: [],
|
|
renamed: [
|
|
{
|
|
from: 'bin/jq-1.5',
|
|
to: 'bin/jq-1.6',
|
|
},
|
|
],
|
|
}),
|
|
);
|
|
|
|
const res = await updateArtifacts(
|
|
partial<UpdateArtifact>({
|
|
updatedDeps: [
|
|
{
|
|
depName: 'go',
|
|
currentVersion: '1.17',
|
|
newValue: '1.17.1',
|
|
},
|
|
{
|
|
depName: 'jq',
|
|
currentVersion: '1.5',
|
|
newValue: '1.6',
|
|
},
|
|
],
|
|
packageFileName: 'go/bin/hermit',
|
|
}),
|
|
);
|
|
|
|
expect(res).toEqual([
|
|
{
|
|
artifactError: {
|
|
stderr: 'error getting content for bin/jq-1.6',
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('should return error on installation error', async () => {
|
|
mockExecAll(
|
|
new ExecError('', {
|
|
stdout: '',
|
|
stderr: 'error executing hermit install',
|
|
cmd: '',
|
|
options: {},
|
|
}),
|
|
);
|
|
|
|
const res = await updateArtifacts(
|
|
partial<UpdateArtifact>({
|
|
updatedDeps: [
|
|
{
|
|
depName: 'go',
|
|
currentVersion: '1.17',
|
|
newValue: '1.17.1',
|
|
},
|
|
{
|
|
depName: 'jq',
|
|
currentVersion: '1.5',
|
|
newValue: '1.6',
|
|
},
|
|
],
|
|
packageFileName: 'go/bin/hermit',
|
|
}),
|
|
);
|
|
|
|
expect(res).toStrictEqual([
|
|
{
|
|
artifactError: {
|
|
fileName: 'from: go-1.17 jq-1.5, to: go-1.17.1 jq-1.6',
|
|
stderr: 'error executing hermit install',
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('should return error on invalid update information', async () => {
|
|
let res = await updateArtifacts(
|
|
partial<UpdateArtifact>({
|
|
updatedDeps: [
|
|
{
|
|
currentVersion: '1.17',
|
|
newValue: '1.17.1',
|
|
},
|
|
],
|
|
packageFileName: 'go/bin/hermit',
|
|
}),
|
|
);
|
|
|
|
expect(res).toStrictEqual([
|
|
{
|
|
artifactError: {
|
|
fileName: 'from: -1.17, to: -1.17.1',
|
|
stderr: `invalid package to update`,
|
|
},
|
|
},
|
|
]);
|
|
|
|
res = await updateArtifacts(
|
|
partial<UpdateArtifact>({
|
|
updatedDeps: [
|
|
{
|
|
depName: 'go',
|
|
newValue: '1.17.1',
|
|
},
|
|
],
|
|
packageFileName: 'go/bin/hermit',
|
|
}),
|
|
);
|
|
|
|
expect(res).toStrictEqual([
|
|
{
|
|
artifactError: {
|
|
fileName: 'from: go-, to: go-1.17.1',
|
|
stderr: `invalid package to update`,
|
|
},
|
|
},
|
|
]);
|
|
|
|
res = await updateArtifacts(
|
|
partial<UpdateArtifact>({
|
|
updatedDeps: [
|
|
{
|
|
depName: 'go',
|
|
currentVersion: '1.17',
|
|
},
|
|
],
|
|
packageFileName: 'go/bin/hermit',
|
|
}),
|
|
);
|
|
|
|
expect(res).toStrictEqual([
|
|
{
|
|
artifactError: {
|
|
fileName: 'from: go-1.17, to: go-',
|
|
stderr: `invalid package to update`,
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('prevents injections', async () => {
|
|
lstatsMock.mockResolvedValue(true);
|
|
|
|
readlinkMock.mockResolvedValue('hermit');
|
|
GlobalConfig.set({ localDir: '', binarySource: 'global' });
|
|
const execSnapshots = mockExecAll();
|
|
await updateArtifacts({
|
|
updatedDeps: [
|
|
{
|
|
depName: '|| date; echo ',
|
|
currentVersion: '1.0.0',
|
|
newValue: '1.0.1',
|
|
},
|
|
{
|
|
depName: '|| whoami',
|
|
currentVersion: '1.18.0',
|
|
newName: 'go',
|
|
newValue: '1.24.1',
|
|
updateType: 'replacement',
|
|
},
|
|
],
|
|
packageFileName: './bin/hermit',
|
|
newPackageFileContent: '',
|
|
config: {},
|
|
});
|
|
expect(execSnapshots).toMatchObject([
|
|
{
|
|
cmd: "./hermit uninstall '|| whoami'",
|
|
},
|
|
{
|
|
cmd: "./hermit install '|| date; echo -1.0.1' go-1.24.1",
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('propagates git credentials from hostRules to hermit install', async () => {
|
|
lstatsMock.mockResolvedValue(true);
|
|
readlinkMock.mockResolvedValue('hermit');
|
|
GlobalConfig.set({ localDir: '', binarySource: 'global' });
|
|
|
|
hostRules.add({
|
|
hostType: 'github',
|
|
matchHost: 'github.com',
|
|
token: 'abc123',
|
|
});
|
|
hostRules.add({
|
|
hostType: 'hermit',
|
|
matchHost: 'hermit.example.com',
|
|
username: 'hermit-user',
|
|
password: 'hermit-pass',
|
|
});
|
|
|
|
const execSnapshots = mockExecAll();
|
|
getRepoStatusMock.mockResolvedValue(
|
|
partial<StatusResult>({
|
|
not_added: [],
|
|
deleted: [],
|
|
modified: [],
|
|
created: [],
|
|
renamed: [],
|
|
}),
|
|
);
|
|
|
|
await updateArtifacts(
|
|
partial<UpdateArtifact>({
|
|
updatedDeps: [
|
|
{
|
|
depName: 'go',
|
|
currentVersion: '1.17',
|
|
newValue: '1.17.1',
|
|
},
|
|
],
|
|
packageFileName: 'go/bin/hermit',
|
|
}),
|
|
);
|
|
|
|
expect(execSnapshots).toMatchObject([
|
|
{
|
|
cmd: './hermit install go-1.17.1',
|
|
options: {
|
|
env: {
|
|
GIT_CONFIG_COUNT: '6',
|
|
GIT_CONFIG_KEY_0: 'url.https://ssh:abc123@github.com/.insteadOf',
|
|
GIT_CONFIG_KEY_1: 'url.https://git:abc123@github.com/.insteadOf',
|
|
GIT_CONFIG_KEY_2: 'url.https://abc123@github.com/.insteadOf',
|
|
GIT_CONFIG_KEY_3:
|
|
'url.https://hermit-user:hermit-pass@hermit.example.com/.insteadOf',
|
|
GIT_CONFIG_KEY_4:
|
|
'url.https://hermit-user:hermit-pass@hermit.example.com/.insteadOf',
|
|
GIT_CONFIG_KEY_5:
|
|
'url.https://hermit-user:hermit-pass@hermit.example.com/.insteadOf',
|
|
GIT_CONFIG_VALUE_0: 'ssh://git@github.com/',
|
|
GIT_CONFIG_VALUE_1: 'git@github.com:',
|
|
GIT_CONFIG_VALUE_2: 'https://github.com/',
|
|
GIT_CONFIG_VALUE_3: 'ssh://git@hermit.example.com/',
|
|
GIT_CONFIG_VALUE_4: 'git@hermit.example.com:',
|
|
GIT_CONFIG_VALUE_5: 'https://hermit.example.com/',
|
|
},
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('returns generic error when a non-UpdateHermitError propagates from updateHermitPackage', async () => {
|
|
mockExecAll(new Error('unexpected execution failure'));
|
|
|
|
const res = await updateArtifacts(
|
|
partial<UpdateArtifact>({
|
|
updatedDeps: [
|
|
{
|
|
depName: 'go',
|
|
currentVersion: '1.17',
|
|
newValue: '1.17.1',
|
|
},
|
|
],
|
|
packageFileName: 'go/bin/hermit',
|
|
}),
|
|
);
|
|
|
|
expect(res).toStrictEqual([
|
|
{
|
|
artifactError: {
|
|
stderr: 'unexpected execution failure',
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('stringifies non-Error thrown values in the generic fallback', async () => {
|
|
exec.mockImplementationOnce(() => {
|
|
// eslint-disable-next-line @typescript-eslint/only-throw-error -- deliberately testing non-Error throw path
|
|
throw 'raw string failure';
|
|
});
|
|
|
|
const res = await updateArtifacts(
|
|
partial<UpdateArtifact>({
|
|
updatedDeps: [
|
|
{
|
|
depName: 'go',
|
|
currentVersion: '1.17',
|
|
newValue: '1.17.1',
|
|
},
|
|
],
|
|
packageFileName: 'go/bin/hermit',
|
|
}),
|
|
);
|
|
|
|
expect(res).toStrictEqual([
|
|
{
|
|
artifactError: {
|
|
stderr: 'raw string failure',
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
});
|