mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-25 15:01:49 +00:00
feat(mix): implement lockFileMaintenance (#33326)
Co-authored-by: Rhys Arkins <rhys@arkins.net> Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`modules/manager/mix/artifacts > authenticates to private repositories in updated dependecies 1`] = `
|
||||
exports[`modules/manager/mix/artifacts > authenticates to private repositories in updated dependencies 1`] = `
|
||||
[
|
||||
{
|
||||
"file": {
|
||||
@@ -12,7 +12,7 @@ exports[`modules/manager/mix/artifacts > authenticates to private repositories i
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`modules/manager/mix/artifacts > authenticates to private repositories in updated dependecies 2`] = `
|
||||
exports[`modules/manager/mix/artifacts > authenticates to private repositories in updated dependencies 2`] = `
|
||||
[
|
||||
{
|
||||
"cmd": "docker ps --filter name=renovate_sidecar -aq",
|
||||
@@ -80,7 +80,7 @@ exports[`modules/manager/mix/artifacts > returns updated mix.lock 1`] = `
|
||||
},
|
||||
},
|
||||
{
|
||||
"cmd": "docker run --rm --name=renovate_sidecar --label=renovate_child -v "/tmp/github/some/repo":"/tmp/github/some/repo" -v "/tmp/cache":"/tmp/cache" -e CONTAINERBASE_CACHE_DIR -w "/tmp/github/some/repo" ghcr.io/containerbase/sidecar bash -l -c "install-tool erlang 25.0.0.0 && install-tool elixir 1.13.4 && mix deps.update plug"",
|
||||
"cmd": "docker run --rm --name=renovate_sidecar --label=renovate_child -v "/tmp/github/some/repo":"/tmp/github/some/repo" -v "/tmp/cache":"/tmp/cache" -e CONTAINERBASE_CACHE_DIR -w "/tmp/github/some/repo" ghcr.io/containerbase/sidecar bash -l -c "install-tool erlang 25.0.0.0 && install-tool elixir v1.13.4 && mix deps.update plug"",
|
||||
"options": {
|
||||
"cwd": "/tmp/github/some/repo",
|
||||
"encoding": "utf-8",
|
||||
|
||||
@@ -2,7 +2,6 @@ import { join } from 'upath';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import { GlobalConfig } from '../../../config/global';
|
||||
import type { RepoGlobalConfig } from '../../../config/types';
|
||||
import * as docker from '../../../util/exec/docker';
|
||||
import { getPkgReleases as _getPkgReleases } from '../../datasource';
|
||||
import type { UpdateArtifactsConfig } from '../types';
|
||||
import { updateArtifacts } from '.';
|
||||
@@ -27,6 +26,10 @@ const adminConfig: RepoGlobalConfig = {
|
||||
process.env.CONTAINERBASE = 'true';
|
||||
|
||||
const config: UpdateArtifactsConfig = {};
|
||||
const constraints: Record<string, string> = {
|
||||
erlang: '25.0.0.0',
|
||||
elixir: 'v1.13.4',
|
||||
};
|
||||
|
||||
describe('modules/manager/mix/artifacts', () => {
|
||||
beforeEach(() => {
|
||||
@@ -74,9 +77,10 @@ describe('modules/manager/mix/artifacts', () => {
|
||||
});
|
||||
|
||||
it('returns null if unchanged', async () => {
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('Current mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('Current mix.lock');
|
||||
const execSnapshots = mockExecAll();
|
||||
fs.readLocalFile.mockResolvedValueOnce('Current mix.lock');
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'mix.exs',
|
||||
@@ -88,15 +92,61 @@ describe('modules/manager/mix/artifacts', () => {
|
||||
expect(execSnapshots).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('returns null when trying to use lockFileMaintenance with no mix.lock file', async () => {
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce(null);
|
||||
fs.localPathExists.mockResolvedValueOnce(false);
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce(null);
|
||||
|
||||
fs.writeLocalFile.mockClear(); // Clear the mock to ensure we can check if it's called
|
||||
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'mix.exs',
|
||||
updatedDeps: [],
|
||||
newPackageFileContent: '{}',
|
||||
config: { ...config, isLockFileMaintenance: true },
|
||||
}),
|
||||
).toBeNull();
|
||||
|
||||
expect(fs.writeLocalFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns null if no updatedDeps and no lockFileMaintenance', async () => {
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'mix.exs',
|
||||
updatedDeps: [],
|
||||
newPackageFileContent: '{}',
|
||||
config,
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null if using lockFileMaintenance in umbrella project', async () => {
|
||||
fs.getSiblingFileName.mockReturnValueOnce('apps/foo/mix.lock');
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce('mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce(null);
|
||||
fs.readLocalFile.mockResolvedValueOnce('Old mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('New mix.lock');
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'apps/foo/mix.exs',
|
||||
updatedDeps: [],
|
||||
newPackageFileContent: '{}',
|
||||
config: { ...config, isLockFileMaintenance: true },
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('returns updated mix.lock', async () => {
|
||||
vi.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce();
|
||||
GlobalConfig.set({
|
||||
...adminConfig,
|
||||
binarySource: 'docker',
|
||||
dockerSidecarImage: 'ghcr.io/containerbase/sidecar',
|
||||
});
|
||||
fs.readLocalFile.mockResolvedValueOnce('Old mix.lock');
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce('mix.lock');
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
const execSnapshots = mockExecAll();
|
||||
fs.readLocalFile.mockResolvedValueOnce('New mix.lock');
|
||||
|
||||
@@ -113,9 +163,9 @@ describe('modules/manager/mix/artifacts', () => {
|
||||
// elixir
|
||||
getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [
|
||||
{ version: '1.8.2' },
|
||||
{ version: '1.13.3' },
|
||||
{ version: '1.13.4' },
|
||||
{ version: 'v1.8.2' },
|
||||
{ version: 'v1.13.3' },
|
||||
{ version: 'v1.13.4' },
|
||||
],
|
||||
});
|
||||
|
||||
@@ -137,7 +187,7 @@ describe('modules/manager/mix/artifacts', () => {
|
||||
it('uses constraints on install mode', async () => {
|
||||
GlobalConfig.set({ ...adminConfig, binarySource: 'install' });
|
||||
fs.readLocalFile.mockResolvedValueOnce('Old mix.lock');
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce('mix.lock');
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
const execSnapshots = mockExecAll();
|
||||
fs.readLocalFile.mockResolvedValueOnce('New mix.lock');
|
||||
|
||||
@@ -163,15 +213,14 @@ describe('modules/manager/mix/artifacts', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('authenticates to private repositories in updated dependecies', async () => {
|
||||
vi.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce();
|
||||
it('authenticates to private repositories in updated dependencies', async () => {
|
||||
GlobalConfig.set({
|
||||
...adminConfig,
|
||||
binarySource: 'docker',
|
||||
dockerSidecarImage: 'ghcr.io/containerbase/sidecar',
|
||||
});
|
||||
fs.readLocalFile.mockResolvedValueOnce('Old mix.lock');
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce('mix.lock');
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
const execSnapshots = mockExecAll();
|
||||
fs.readLocalFile.mockResolvedValueOnce('New mix.lock');
|
||||
hostRules.find.mockReturnValueOnce({ token: 'valid_test_token' });
|
||||
@@ -231,7 +280,7 @@ describe('modules/manager/mix/artifacts', () => {
|
||||
it('authenticates to private repositories configured in hostRules', async () => {
|
||||
GlobalConfig.set({ ...adminConfig, binarySource: 'install' });
|
||||
fs.readLocalFile.mockResolvedValueOnce('Old mix.lock');
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce('mix.lock');
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
const execSnapshots = mockExecAll();
|
||||
fs.readLocalFile.mockResolvedValueOnce('New mix.lock');
|
||||
hostRules.getAll.mockReturnValueOnce([
|
||||
@@ -292,27 +341,132 @@ describe('modules/manager/mix/artifacts', () => {
|
||||
});
|
||||
|
||||
it('returns updated mix.lock in subdir', async () => {
|
||||
GlobalConfig.set({
|
||||
...adminConfig,
|
||||
binarySource: 'docker',
|
||||
dockerSidecarImage: 'ghcr.io/containerbase/sidecar',
|
||||
});
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce('subdir/mix.lock');
|
||||
mockExecAll();
|
||||
GlobalConfig.set({ ...adminConfig, binarySource: 'install' });
|
||||
fs.getSiblingFileName.mockReturnValueOnce('subdir/mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('Old mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('New mix.lock');
|
||||
|
||||
const execSnapshots = mockExecAll();
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'subdir/mix.exs',
|
||||
updatedDeps: [{ depName: 'plug' }],
|
||||
newPackageFileContent: '{}',
|
||||
config,
|
||||
config: { ...config, constraints },
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
file: {
|
||||
path: 'subdir/mix.lock',
|
||||
type: 'addition',
|
||||
contents: 'New mix.lock',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(execSnapshots).toMatchObject([
|
||||
{ cmd: 'install-tool erlang 25.0.0.0' },
|
||||
{ cmd: 'install-tool elixir v1.13.4' },
|
||||
{
|
||||
cmd: 'mix deps.update plug',
|
||||
options: { cwd: '/tmp/github/some/repo/subdir' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns updated mix.lock in umbrella project', async () => {
|
||||
GlobalConfig.set({ ...adminConfig, binarySource: 'install' });
|
||||
fs.getSiblingFileName.mockReturnValueOnce('apps/foo/mix.lock');
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce('mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce(null);
|
||||
fs.readLocalFile.mockResolvedValueOnce('Old mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('New mix.lock');
|
||||
|
||||
const execSnapshots = mockExecAll();
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'apps/foo/mix.exs',
|
||||
updatedDeps: [{ depName: 'plug' }],
|
||||
newPackageFileContent: '{}',
|
||||
config: { ...config, constraints },
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
file: {
|
||||
path: 'mix.lock',
|
||||
type: 'addition',
|
||||
contents: 'New mix.lock',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(execSnapshots).toMatchObject([
|
||||
{ cmd: 'install-tool erlang 25.0.0.0' },
|
||||
{ cmd: 'install-tool elixir v1.13.4' },
|
||||
{
|
||||
cmd: 'mix deps.update plug',
|
||||
options: { cwd: '/tmp/github/some/repo/apps/foo' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('supports lockFileMaintenance', async () => {
|
||||
GlobalConfig.set({ ...adminConfig, binarySource: 'install' });
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('Old mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('New mix.lock');
|
||||
|
||||
const execSnapshots = mockExecAll();
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'mix.exs',
|
||||
updatedDeps: [],
|
||||
newPackageFileContent: '{}',
|
||||
config: {
|
||||
...config,
|
||||
constraints,
|
||||
isLockFileMaintenance: true,
|
||||
},
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
file: {
|
||||
path: 'mix.lock',
|
||||
type: 'addition',
|
||||
contents: 'New mix.lock',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(execSnapshots).toMatchObject([
|
||||
{ cmd: 'install-tool erlang 25.0.0.0' },
|
||||
{ cmd: 'install-tool elixir v1.13.4' },
|
||||
{
|
||||
cmd: 'mix deps.get',
|
||||
options: { cwd: '/tmp/github/some/repo' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('lockFileMaintenance returns null if unchanged', async () => {
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('Old mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('Old mix.lock');
|
||||
|
||||
mockExecAll();
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'mix.exs',
|
||||
updatedDeps: [],
|
||||
newPackageFileContent: '{}',
|
||||
config: { ...config, isLockFileMaintenance: true },
|
||||
}),
|
||||
).toBeNull();
|
||||
expect(fs.readLocalFile).toHaveBeenCalledWith('subdir/mix.lock', 'utf8');
|
||||
});
|
||||
|
||||
it('catches write errors', async () => {
|
||||
fs.readLocalFile.mockResolvedValueOnce('Current mix.lock');
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce('mix.lock');
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
fs.writeLocalFile.mockImplementationOnce(() => {
|
||||
throw new Error('not found');
|
||||
});
|
||||
@@ -330,7 +484,7 @@ describe('modules/manager/mix/artifacts', () => {
|
||||
|
||||
it('catches exec errors', async () => {
|
||||
fs.readLocalFile.mockResolvedValueOnce('Current mix.lock');
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce('mix.lock');
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
mockExecAll(new Error('exec-error'));
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
@@ -343,4 +497,70 @@ describe('modules/manager/mix/artifacts', () => {
|
||||
{ artifactError: { lockFile: 'mix.lock', stderr: 'exec-error' } },
|
||||
]);
|
||||
});
|
||||
|
||||
it('detects read errors', async () => {
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce(null);
|
||||
fs.localPathExists.mockResolvedValueOnce(true);
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'mix.exs',
|
||||
updatedDeps: [{ depName: 'plug' }],
|
||||
newPackageFileContent: '{}',
|
||||
config,
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
artifactError: {
|
||||
lockFile: 'mix.lock',
|
||||
stderr: 'Error reading mix.lock',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('detects read errors (umbrella)', async () => {
|
||||
fs.getSiblingFileName.mockReturnValueOnce('apps/foo/mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce(null);
|
||||
fs.localPathExists.mockResolvedValueOnce(false);
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce('mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce(null);
|
||||
fs.localPathExists.mockResolvedValueOnce(true);
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'apps/foo/mix.exs',
|
||||
updatedDeps: [{ depName: 'plug' }],
|
||||
newPackageFileContent: '{}',
|
||||
config,
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
artifactError: {
|
||||
lockFile: 'mix.lock',
|
||||
stderr: 'Error reading mix.lock',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("handles updates and doesn't try to create mix.lock file if it doesn't exist", async () => {
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce(null);
|
||||
fs.localPathExists.mockResolvedValueOnce(false);
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce(null);
|
||||
|
||||
const execSnapshots = mockExecAll();
|
||||
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'mix.exs',
|
||||
updatedDeps: [{ depName: 'plug' }],
|
||||
newPackageFileContent: 'New mix.exs',
|
||||
config,
|
||||
}),
|
||||
).toBeNull();
|
||||
|
||||
expect(execSnapshots).toHaveLength(0);
|
||||
expect(fs.writeLocalFile).toHaveBeenCalledWith('mix.exs', 'New mix.exs');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,10 @@ import { logger } from '../../../logger';
|
||||
import { exec } from '../../../util/exec';
|
||||
import type { ExecOptions } from '../../../util/exec/types';
|
||||
import {
|
||||
deleteLocalFile,
|
||||
findLocalSiblingOrParent,
|
||||
getSiblingFileName,
|
||||
localPathExists,
|
||||
readLocalFile,
|
||||
writeLocalFile,
|
||||
} from '../../../util/fs';
|
||||
@@ -26,15 +29,60 @@ export async function updateArtifacts({
|
||||
config,
|
||||
}: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> {
|
||||
logger.debug(`mix.getArtifacts(${packageFileName})`);
|
||||
if (updatedDeps.length < 1) {
|
||||
logger.debug('No updated mix deps - returning null');
|
||||
const { isLockFileMaintenance } = config;
|
||||
|
||||
if (is.emptyArray(updatedDeps) && !isLockFileMaintenance) {
|
||||
logger.debug('No updated mix deps');
|
||||
return null;
|
||||
}
|
||||
|
||||
let lockFileName = getSiblingFileName(packageFileName, 'mix.lock');
|
||||
let isUmbrella = false;
|
||||
|
||||
let existingLockFileContent = await readLocalFile(lockFileName, 'utf8');
|
||||
if (!existingLockFileContent) {
|
||||
const lockFileError = await checkLockFileReadError(lockFileName);
|
||||
if (lockFileError) {
|
||||
return lockFileError;
|
||||
}
|
||||
|
||||
const parentLockFileName = await findLocalSiblingOrParent(
|
||||
packageFileName,
|
||||
'mix.lock',
|
||||
);
|
||||
existingLockFileContent =
|
||||
parentLockFileName && (await readLocalFile(parentLockFileName, 'utf8'));
|
||||
|
||||
if (parentLockFileName && existingLockFileContent) {
|
||||
lockFileName = parentLockFileName;
|
||||
isUmbrella = true;
|
||||
} else if (parentLockFileName) {
|
||||
const lockFileError = await checkLockFileReadError(parentLockFileName);
|
||||
if (lockFileError) {
|
||||
return lockFileError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isLockFileMaintenance && isUmbrella) {
|
||||
logger.debug(
|
||||
'Cannot use lockFileMaintenance in an umbrella project, see https://docs.renovatebot.com/modules/manager/mix/#lockFileMaintenance',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isLockFileMaintenance && !existingLockFileContent) {
|
||||
logger.debug(
|
||||
'Cannot use lockFileMaintenance when no mix.lock file is present',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const lockFileName =
|
||||
(await findLocalSiblingOrParent(packageFileName, 'mix.lock')) ?? 'mix.lock';
|
||||
try {
|
||||
await writeLocalFile(packageFileName, newPackageFileContent);
|
||||
if (isLockFileMaintenance) {
|
||||
await deleteLocalFile(lockFileName);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn({ err }, 'mix.exs could not be written');
|
||||
return [
|
||||
@@ -47,7 +95,6 @@ export async function updateArtifacts({
|
||||
];
|
||||
}
|
||||
|
||||
const existingLockFileContent = await readLocalFile(lockFileName, 'utf8');
|
||||
if (!existingLockFileContent) {
|
||||
logger.debug('No mix.lock found');
|
||||
return null;
|
||||
@@ -113,19 +160,25 @@ export async function updateArtifacts({
|
||||
],
|
||||
preCommands,
|
||||
};
|
||||
const command = [
|
||||
'mix',
|
||||
'deps.update',
|
||||
...updatedDeps
|
||||
.map((dep) => dep.depName)
|
||||
.filter(is.string)
|
||||
.map((dep) => quote(dep)),
|
||||
].join(' ');
|
||||
|
||||
let command: string;
|
||||
if (isLockFileMaintenance) {
|
||||
command = 'mix deps.get';
|
||||
} else {
|
||||
command = [
|
||||
'mix',
|
||||
'deps.update',
|
||||
...updatedDeps
|
||||
.map((dep) => dep.depName)
|
||||
.filter(is.string)
|
||||
.map((dep) => quote(dep)),
|
||||
].join(' ');
|
||||
}
|
||||
|
||||
try {
|
||||
await exec(command, execOptions);
|
||||
} catch (err) {
|
||||
// istanbul ignore if
|
||||
/* v8 ignore next 3 */
|
||||
if (err.message === TEMPORARY_ERROR) {
|
||||
throw err;
|
||||
}
|
||||
@@ -161,3 +214,19 @@ export async function updateArtifacts({
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
async function checkLockFileReadError(
|
||||
lockFileName: string,
|
||||
): Promise<UpdateArtifactsResult[] | null> {
|
||||
if (await localPathExists(lockFileName)) {
|
||||
return [
|
||||
{
|
||||
artifactError: {
|
||||
lockFile: lockFileName,
|
||||
stderr: `Error reading ${lockFileName}`,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ export const defaultConfig = {
|
||||
fileMatch: ['(^|/)mix\\.exs$'],
|
||||
};
|
||||
|
||||
export const supportsLockFileMaintenance = true;
|
||||
export const supportedDatasources = [
|
||||
GithubTagsDatasource.id,
|
||||
GitTagsDatasource.id,
|
||||
|
||||
@@ -7,6 +7,15 @@ The following `depTypes` are currently supported by the `mix` manager :
|
||||
- `prod`: all dependencies by default
|
||||
- `dev`: dependencies with [`:only` option](https://hexdocs.pm/mix/Mix.Tasks.Deps.html#module-dependency-definition-options) not containing `:prod`
|
||||
|
||||
### `lockFileMaintenance`
|
||||
|
||||
We recommend you use [`lockFileMaintenance`](../../../configuration-options.md#lockfilemaintenance) for the `mix` manager.
|
||||
|
||||
`lockFileMaintenance=true` periodically refreshes your `mix.lock` files, ensuring all indirect dependencies are updated to their latest allowed versions.
|
||||
|
||||
This option will be skipped in [umbrella projects](https://hexdocs.pm/elixir/dependencies-and-umbrella-projects.html#umbrella-projects), as they share a single
|
||||
`mix.lock` file for all applications `mix.exs` files.
|
||||
|
||||
### Default `rangeStrategy=auto` behavior
|
||||
|
||||
Renovate's default [`rangeStrategy`](../../../configuration-options.md#rangestrategy) is `"auto"`.
|
||||
|
||||
Reference in New Issue
Block a user