mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-09 13:36:26 +00:00
Merge d93a22e64b
into f97189c600
This commit is contained in:
commit
893d1cf40d
11 changed files with 416 additions and 84 deletions
|
@ -27,13 +27,19 @@ defmodule MyProject.MixProject do
|
|||
{:secret, "~> 1.0", organization: "acme"},
|
||||
{:also_secret, "~> 1.0", only: [:dev, :test], organization: "acme", runtime: false},
|
||||
{:metrics, ">0.2.0 and <=1.0.0"},
|
||||
{:jason, ">= 1.0.0"},
|
||||
{:jason, ">= 1.0.0", only: :prod},
|
||||
{:hackney, "~> 1.0",
|
||||
optional: true},
|
||||
{:hammer_backend_redis, "~> 6.1"},
|
||||
{:hammer_backend_redis, "~> 6.1", only: [:dev, :prod, :test]},
|
||||
{:castore, "== 1.0.10"},
|
||||
{:gun, "~> 2.0.0", hex: "grpc_gun"},
|
||||
{:another_gun, "~> 0.4.0", hex: :raygun},
|
||||
{:credo, "~> 1.7", only:
|
||||
[:test,
|
||||
# prod,
|
||||
:dev],
|
||||
runtime: false},
|
||||
{:floki, "== 0.37.0", only: :test},
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
%{
|
||||
"another_gun": {:hex, :raygun, "0.4.0", "7744e99dd695f61e78ad5e047cce0affb3edfc6f93a92278598ab553b9c5091f", [:mix], [{:httpoison, "~> 0.8 or ~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.1", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "eee4b891e6e65c6a4b15386dc7b7a72b717f3c123cc0012cfd19e8f2ab21116d"},
|
||||
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
||||
"castore": {:hex, :castore, "1.0.10", "43bbeeac820f16c89f79721af1b3e092399b3a1ecc8df1a472738fd853574911", [:mix], [], "hexpm", "1b0b7ea14d889d9ea21202c43a4fa015eb913021cb535e8ed91946f4b77a8848"},
|
||||
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
||||
"cowboy": {:git, "https://github.com/ninenines/cowboy.git", "0c2e2224e372f01e6cf51a8e12d4856edb4cb8ac", [tag: "0.6.0"]},
|
||||
"cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"},
|
||||
"credo": {:hex, :credo, "1.7.10", "6e64fe59be8da5e30a1b96273b247b5cf1cc9e336b5fd66302a64b25749ad44d", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "71fbc9a6b8be21d993deca85bf151df023a3097b01e09a2809d460348561d8cd"},
|
||||
"decimal": {:hex, :decimal, "1.9.0", "83e8daf59631d632b171faabafb4a9f4242c514b0a06ba3df493951c08f64d07", [:mix], [], "hexpm", "b1f2343568eed6928f3e751cf2dffde95bfaa19dd95d09e8a9ea92ccfd6f7d85"},
|
||||
"ecto": {:git, "https://github.com/elixir-ecto/ecto.git", "795036d997c7503b21fb64d6bf1a89b83c44f2b5", [ref: "795036d997c7503b21fb64d6bf1a89b83c44f2b5"]},
|
||||
"secret": {:hex, :secret, "1.5.0", "344dbbf6610d205760ec37e2848bff2aab5a2de182bb5cdaa72cc2fd19d74535", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "19c205c8de0e2e5817f2250100281c58e717cb11ff1bb410bf661ee78c24e79b"},
|
||||
"also_secret": {:hex, :also_secret, "1.3.4", "344dbbf6610d205760ec37e2848bff2aab5a2de182bb5cdaa72cc2fd19d74535", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "19c205c8de0e2e5817f2250100281c58e717cb11ff1bb410bf661ee78c24e79b"},
|
||||
"file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},
|
||||
"floki": {:hex, :floki, "0.37.0", "b83e0280bbc6372f2a403b2848013650b16640cd2470aea6701f0632223d719e", [:mix], [], "hexpm", "516a0c15a69f78c47dc8e0b9b3724b29608aa6619379f91b1ffa47109b5d0dd3"},
|
||||
"gun": {:hex, :grpc_gun, "2.0.1", "221b792df3a93e8fead96f697cbaf920120deacced85c6cd3329d2e67f0871f8", [:rebar3], [{:cowlib, "~> 2.11", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "795a65eb9d0ba16697e6b0e1886009ce024799e43bb42753f0c59b029f592831"},
|
||||
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
|
||||
"hammer": {:hex, :hammer, "6.2.1", "5ae9c33e3dceaeb42de0db46bf505bd9c35f259c8defb03390cd7556fea67ee2", [:mix], [{:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm", "b9476d0c13883d2dc0cc72e786bac6ac28911fba7cc2e04b70ce6a6d9c4b2bdc"},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
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 in
|
|||
]
|
||||
`;
|
||||
|
||||
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",
|
||||
|
|
|
@ -74,9 +74,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,6 +89,33 @@ describe('modules/manager/mix/artifacts', () => {
|
|||
expect(execSnapshots).toMatchSnapshot();
|
||||
});
|
||||
|
||||
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, updateType: 'lockFileMaintenance' },
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('returns updated mix.lock', async () => {
|
||||
jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce();
|
||||
GlobalConfig.set({
|
||||
|
@ -96,28 +124,16 @@ describe('modules/manager/mix/artifacts', () => {
|
|||
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');
|
||||
|
||||
// erlang
|
||||
getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [
|
||||
{ version: '22.3.4.26' },
|
||||
{ version: '23.1.1.0' },
|
||||
{ version: '24.3.4.1' },
|
||||
{ version: '24.3.4.2' },
|
||||
{ version: '25.0.0.0' },
|
||||
],
|
||||
releases: [{ version: '25.0.0.0' }],
|
||||
});
|
||||
// elixir
|
||||
getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [
|
||||
{ version: '1.8.2' },
|
||||
{ version: '1.13.3' },
|
||||
{ version: '1.13.4' },
|
||||
],
|
||||
});
|
||||
getPkgReleases.mockResolvedValueOnce({ releases: [{ version: '1.13.4' }] });
|
||||
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
|
@ -137,7 +153,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,7 +179,7 @@ describe('modules/manager/mix/artifacts', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('authenticates to private repositories in updated dependecies', async () => {
|
||||
it('authenticates to private repositories in updated dependencies', async () => {
|
||||
jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce();
|
||||
GlobalConfig.set({
|
||||
...adminConfig,
|
||||
|
@ -171,7 +187,7 @@ describe('modules/manager/mix/artifacts', () => {
|
|||
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' });
|
||||
|
@ -179,21 +195,11 @@ describe('modules/manager/mix/artifacts', () => {
|
|||
|
||||
// erlang
|
||||
getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [
|
||||
{ version: '22.3.4.26' },
|
||||
{ version: '23.1.1.0' },
|
||||
{ version: '24.3.4.1' },
|
||||
{ version: '24.3.4.2' },
|
||||
{ version: '25.0.0.0' },
|
||||
],
|
||||
releases: [{ version: '25.0.0.0' }],
|
||||
});
|
||||
// elixir
|
||||
getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [
|
||||
{ version: 'v1.8.2' },
|
||||
{ version: 'v1.13.3' },
|
||||
{ version: 'v1.13.4' },
|
||||
],
|
||||
releases: [{ version: 'v1.13.4' }],
|
||||
});
|
||||
|
||||
const result = await updateArtifacts({
|
||||
|
@ -231,7 +237,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([
|
||||
|
@ -249,22 +255,10 @@ describe('modules/manager/mix/artifacts', () => {
|
|||
|
||||
// erlang
|
||||
getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [
|
||||
{ version: '22.3.4.26' },
|
||||
{ version: '23.1.1.0' },
|
||||
{ version: '24.3.4.1' },
|
||||
{ version: '24.3.4.2' },
|
||||
{ version: '25.0.0.0' },
|
||||
],
|
||||
releases: [{ version: '25.0.0.0' }],
|
||||
});
|
||||
// elixir
|
||||
getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [
|
||||
{ version: 'v1.8.2' },
|
||||
{ version: 'v1.13.3' },
|
||||
{ version: 'v1.13.4' },
|
||||
],
|
||||
});
|
||||
getPkgReleases.mockResolvedValueOnce({ releases: [{ version: '1.13.4' }] });
|
||||
|
||||
const result = await updateArtifacts({
|
||||
packageFileName: 'mix.exs',
|
||||
|
@ -280,7 +274,7 @@ describe('modules/manager/mix/artifacts', () => {
|
|||
]);
|
||||
expect(execSnapshots).toMatchObject([
|
||||
{ cmd: 'install-tool erlang 25.0.0.0' },
|
||||
{ cmd: 'install-tool elixir v1.13.4' },
|
||||
{ cmd: 'install-tool elixir 1.13.4' },
|
||||
{
|
||||
cmd: 'mix hex.organization auth an_organization --key an_organization_token',
|
||||
},
|
||||
|
@ -292,13 +286,24 @@ describe('modules/manager/mix/artifacts', () => {
|
|||
});
|
||||
|
||||
it('returns updated mix.lock in subdir', async () => {
|
||||
jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce();
|
||||
GlobalConfig.set({
|
||||
...adminConfig,
|
||||
binarySource: 'docker',
|
||||
dockerSidecarImage: 'ghcr.io/containerbase/sidecar',
|
||||
});
|
||||
fs.findLocalSiblingOrParent.mockResolvedValueOnce('subdir/mix.lock');
|
||||
mockExecAll();
|
||||
fs.getSiblingFileName.mockReturnValueOnce('subdir/mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('Old mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('New mix.lock');
|
||||
|
||||
// erlang
|
||||
getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [{ version: '25.0.0.0' }],
|
||||
});
|
||||
// elixir
|
||||
getPkgReleases.mockResolvedValueOnce({ releases: [{ version: '1.13.4' }] });
|
||||
|
||||
const execSnapshots = mockExecAll();
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'subdir/mix.exs',
|
||||
|
@ -306,13 +311,120 @@ describe('modules/manager/mix/artifacts', () => {
|
|||
newPackageFileContent: '{}',
|
||||
config,
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
file: {
|
||||
path: 'subdir/mix.lock',
|
||||
type: 'addition',
|
||||
contents: 'New mix.lock',
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(execSnapshots[1]?.cmd).toMatch('mix deps.update plug');
|
||||
expect(execSnapshots[1]?.options?.cwd).toBe('/tmp/github/some/repo/subdir');
|
||||
});
|
||||
|
||||
it('returns updated mix.lock in umbrella project', async () => {
|
||||
jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce();
|
||||
GlobalConfig.set({
|
||||
...adminConfig,
|
||||
binarySource: 'docker',
|
||||
dockerSidecarImage: 'ghcr.io/containerbase/sidecar',
|
||||
});
|
||||
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');
|
||||
|
||||
// erlang
|
||||
getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [{ version: '25.0.0.0' }],
|
||||
});
|
||||
// elixir
|
||||
getPkgReleases.mockResolvedValueOnce({ releases: [{ version: '1.13.4' }] });
|
||||
|
||||
const execSnapshots = mockExecAll();
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'apps/foo/mix.exs',
|
||||
updatedDeps: [{ depName: 'plug' }],
|
||||
newPackageFileContent: '{}',
|
||||
config,
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
file: {
|
||||
path: 'mix.lock',
|
||||
type: 'addition',
|
||||
contents: 'New mix.lock',
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(execSnapshots[1]?.cmd).toMatch('mix deps.update plug');
|
||||
expect(execSnapshots[1]?.options?.cwd).toBe(
|
||||
'/tmp/github/some/repo/apps/foo',
|
||||
);
|
||||
});
|
||||
|
||||
it('supports lockFileMaintenance', async () => {
|
||||
jest.spyOn(docker, 'removeDanglingContainers').mockResolvedValueOnce();
|
||||
GlobalConfig.set({
|
||||
...adminConfig,
|
||||
binarySource: 'docker',
|
||||
dockerSidecarImage: 'ghcr.io/containerbase/sidecar',
|
||||
});
|
||||
fs.getSiblingFileName.mockReturnValueOnce('mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('Old mix.lock');
|
||||
fs.readLocalFile.mockResolvedValueOnce('New mix.lock');
|
||||
|
||||
// erlang
|
||||
getPkgReleases.mockResolvedValueOnce({
|
||||
releases: [{ version: '25.0.0.0' }],
|
||||
});
|
||||
// elixir
|
||||
getPkgReleases.mockResolvedValueOnce({ releases: [{ version: '1.13.4' }] });
|
||||
|
||||
const execSnapshots = mockExecAll();
|
||||
expect(
|
||||
await updateArtifacts({
|
||||
packageFileName: 'mix.exs',
|
||||
updatedDeps: [],
|
||||
newPackageFileContent: '{}',
|
||||
config: { ...config, updateType: 'lockFileMaintenance' },
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
file: {
|
||||
path: 'mix.lock',
|
||||
type: 'addition',
|
||||
contents: 'New mix.lock',
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(execSnapshots[1]?.cmd).toMatch('mix deps.get');
|
||||
expect(execSnapshots[1]?.options?.cwd).toBe('/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, updateType: 'lockFileMaintenance' },
|
||||
}),
|
||||
).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 +442,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({
|
||||
|
|
|
@ -5,7 +5,9 @@ import { logger } from '../../../logger';
|
|||
import { exec } from '../../../util/exec';
|
||||
import type { ExecOptions } from '../../../util/exec/types';
|
||||
import {
|
||||
deleteLocalFile,
|
||||
findLocalSiblingOrParent,
|
||||
getSiblingFileName,
|
||||
readLocalFile,
|
||||
writeLocalFile,
|
||||
} from '../../../util/fs';
|
||||
|
@ -26,15 +28,35 @@ 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.updateType === 'lockFileMaintenance';
|
||||
|
||||
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) {
|
||||
lockFileName =
|
||||
(await findLocalSiblingOrParent(packageFileName, 'mix.lock')) ??
|
||||
'mix.lock';
|
||||
existingLockFileContent = await readLocalFile(lockFileName, 'utf8');
|
||||
isUmbrella = !!existingLockFileContent;
|
||||
}
|
||||
|
||||
if (isLockFileMaintenance && isUmbrella) {
|
||||
logger.debug('Cannot use lockFileMaintenance in an umbrella project');
|
||||
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 +69,6 @@ export async function updateArtifacts({
|
|||
];
|
||||
}
|
||||
|
||||
const existingLockFileContent = await readLocalFile(lockFileName, 'utf8');
|
||||
if (!existingLockFileContent) {
|
||||
logger.debug('No mix.lock found');
|
||||
return null;
|
||||
|
@ -113,14 +134,20 @@ 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;
|
||||
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);
|
||||
|
|
|
@ -20,12 +20,14 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '~> 0.8.1',
|
||||
datasource: 'hex',
|
||||
depName: 'postgrex',
|
||||
depType: 'prod',
|
||||
packageName: 'postgrex',
|
||||
},
|
||||
{
|
||||
currentValue: '<1.7.0 or ~>1.7.1',
|
||||
datasource: 'hex',
|
||||
depName: 'ranch',
|
||||
depType: 'prod',
|
||||
packageName: 'ranch',
|
||||
},
|
||||
{
|
||||
|
@ -33,6 +35,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '0.6.0',
|
||||
datasource: 'github-tags',
|
||||
depName: 'cowboy',
|
||||
depType: 'prod',
|
||||
packageName: 'ninenines/cowboy',
|
||||
},
|
||||
{
|
||||
|
@ -40,6 +43,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: 'main',
|
||||
datasource: 'git-tags',
|
||||
depName: 'phoenix',
|
||||
depType: 'prod',
|
||||
packageName: 'https://github.com/phoenixframework/phoenix.git',
|
||||
},
|
||||
{
|
||||
|
@ -47,42 +51,49 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: undefined,
|
||||
datasource: 'github-tags',
|
||||
depName: 'ecto',
|
||||
depType: 'prod',
|
||||
packageName: 'elixir-ecto/ecto',
|
||||
},
|
||||
{
|
||||
currentValue: '~> 1.0',
|
||||
datasource: 'hex',
|
||||
depName: 'secret',
|
||||
depType: 'prod',
|
||||
packageName: 'secret:acme',
|
||||
},
|
||||
{
|
||||
currentValue: '~> 1.0',
|
||||
datasource: 'hex',
|
||||
depName: 'also_secret',
|
||||
depType: 'dev',
|
||||
packageName: 'also_secret:acme',
|
||||
},
|
||||
{
|
||||
currentValue: '>0.2.0 and <=1.0.0',
|
||||
datasource: 'hex',
|
||||
depName: 'metrics',
|
||||
depType: 'prod',
|
||||
packageName: 'metrics',
|
||||
},
|
||||
{
|
||||
currentValue: '>= 1.0.0',
|
||||
datasource: 'hex',
|
||||
depName: 'jason',
|
||||
depType: 'prod',
|
||||
packageName: 'jason',
|
||||
},
|
||||
{
|
||||
currentValue: '~> 1.0',
|
||||
datasource: 'hex',
|
||||
depName: 'hackney',
|
||||
depType: 'prod',
|
||||
packageName: 'hackney',
|
||||
},
|
||||
{
|
||||
currentValue: '~> 6.1',
|
||||
datasource: 'hex',
|
||||
depName: 'hammer_backend_redis',
|
||||
depType: 'prod',
|
||||
packageName: 'hammer_backend_redis',
|
||||
},
|
||||
{
|
||||
|
@ -90,20 +101,38 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentVersion: '1.0.10',
|
||||
datasource: 'hex',
|
||||
depName: 'castore',
|
||||
depType: 'prod',
|
||||
packageName: 'castore',
|
||||
},
|
||||
{
|
||||
currentValue: '~> 2.0.0',
|
||||
datasource: 'hex',
|
||||
depName: 'gun',
|
||||
depType: 'prod',
|
||||
packageName: 'grpc_gun',
|
||||
},
|
||||
{
|
||||
currentValue: '~> 0.4.0',
|
||||
datasource: 'hex',
|
||||
depName: 'another_gun',
|
||||
depType: 'prod',
|
||||
packageName: 'raygun',
|
||||
},
|
||||
{
|
||||
currentValue: '~> 1.7',
|
||||
datasource: 'hex',
|
||||
depName: 'credo',
|
||||
depType: 'dev',
|
||||
packageName: 'credo',
|
||||
},
|
||||
{
|
||||
currentValue: '== 0.37.0',
|
||||
currentVersion: '0.37.0',
|
||||
datasource: 'hex',
|
||||
depName: 'floki',
|
||||
depType: 'dev',
|
||||
packageName: 'floki',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -116,6 +145,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '~> 0.8.1',
|
||||
datasource: 'hex',
|
||||
depName: 'postgrex',
|
||||
depType: 'prod',
|
||||
packageName: 'postgrex',
|
||||
lockedVersion: '0.8.4',
|
||||
},
|
||||
|
@ -123,6 +153,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '<1.7.0 or ~>1.7.1',
|
||||
datasource: 'hex',
|
||||
depName: 'ranch',
|
||||
depType: 'prod',
|
||||
packageName: 'ranch',
|
||||
lockedVersion: '1.7.1',
|
||||
},
|
||||
|
@ -131,6 +162,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '0.6.0',
|
||||
datasource: 'github-tags',
|
||||
depName: 'cowboy',
|
||||
depType: 'prod',
|
||||
packageName: 'ninenines/cowboy',
|
||||
lockedVersion: '0.6.0',
|
||||
},
|
||||
|
@ -139,6 +171,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: 'main',
|
||||
datasource: 'git-tags',
|
||||
depName: 'phoenix',
|
||||
depType: 'prod',
|
||||
packageName: 'https://github.com/phoenixframework/phoenix.git',
|
||||
lockedVersion: undefined,
|
||||
},
|
||||
|
@ -147,6 +180,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: undefined,
|
||||
datasource: 'github-tags',
|
||||
depName: 'ecto',
|
||||
depType: 'prod',
|
||||
packageName: 'elixir-ecto/ecto',
|
||||
lockedVersion: undefined,
|
||||
},
|
||||
|
@ -154,6 +188,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '~> 1.0',
|
||||
datasource: 'hex',
|
||||
depName: 'secret',
|
||||
depType: 'prod',
|
||||
packageName: 'secret:acme',
|
||||
lockedVersion: '1.5.0',
|
||||
},
|
||||
|
@ -161,6 +196,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '~> 1.0',
|
||||
datasource: 'hex',
|
||||
depName: 'also_secret',
|
||||
depType: 'dev',
|
||||
packageName: 'also_secret:acme',
|
||||
lockedVersion: '1.3.4',
|
||||
},
|
||||
|
@ -168,6 +204,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '>0.2.0 and <=1.0.0',
|
||||
datasource: 'hex',
|
||||
depName: 'metrics',
|
||||
depType: 'prod',
|
||||
packageName: 'metrics',
|
||||
lockedVersion: '1.0.0',
|
||||
},
|
||||
|
@ -175,6 +212,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '>= 1.0.0',
|
||||
datasource: 'hex',
|
||||
depName: 'jason',
|
||||
depType: 'prod',
|
||||
packageName: 'jason',
|
||||
lockedVersion: '1.4.4',
|
||||
},
|
||||
|
@ -182,6 +220,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '~> 1.0',
|
||||
datasource: 'hex',
|
||||
depName: 'hackney',
|
||||
depType: 'prod',
|
||||
packageName: 'hackney',
|
||||
lockedVersion: '1.20.1',
|
||||
},
|
||||
|
@ -189,6 +228,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '~> 6.1',
|
||||
datasource: 'hex',
|
||||
depName: 'hammer_backend_redis',
|
||||
depType: 'prod',
|
||||
packageName: 'hammer_backend_redis',
|
||||
lockedVersion: '6.2.0',
|
||||
},
|
||||
|
@ -197,6 +237,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentVersion: '1.0.10',
|
||||
datasource: 'hex',
|
||||
depName: 'castore',
|
||||
depType: 'prod',
|
||||
packageName: 'castore',
|
||||
lockedVersion: '1.0.10',
|
||||
},
|
||||
|
@ -204,6 +245,7 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '~> 2.0.0',
|
||||
datasource: 'hex',
|
||||
depName: 'gun',
|
||||
depType: 'prod',
|
||||
packageName: 'grpc_gun',
|
||||
lockedVersion: '2.0.1',
|
||||
},
|
||||
|
@ -211,9 +253,27 @@ describe('modules/manager/mix/extract', () => {
|
|||
currentValue: '~> 0.4.0',
|
||||
datasource: 'hex',
|
||||
depName: 'another_gun',
|
||||
depType: 'prod',
|
||||
packageName: 'raygun',
|
||||
lockedVersion: '0.4.0',
|
||||
},
|
||||
{
|
||||
currentValue: '~> 1.7',
|
||||
datasource: 'hex',
|
||||
depName: 'credo',
|
||||
depType: 'dev',
|
||||
packageName: 'credo',
|
||||
lockedVersion: '1.7.10',
|
||||
},
|
||||
{
|
||||
currentValue: '== 0.37.0',
|
||||
currentVersion: '0.37.0',
|
||||
datasource: 'hex',
|
||||
depName: 'floki',
|
||||
depType: 'dev',
|
||||
lockedVersion: '0.37.0',
|
||||
packageName: 'floki',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,6 +20,8 @@ const lockedVersionRegExp = regEx(
|
|||
/^\s+"(?<app>\w+)".*?"(?<lockedVersion>\d+\.\d+\.\d+)"/,
|
||||
);
|
||||
const hexRegexp = regEx(/hex:\s*(?:"(?<strValue>[^"]+)"|:(?<atomValue>\w+))/);
|
||||
const onlyValueRegexp = /only:\s*(?<only>\[[^\]]*\]|:\w+)/;
|
||||
const onlyEnvironmentsRegexp = /:(\w+)/gm;
|
||||
|
||||
export async function extractPackageFile(
|
||||
content: string,
|
||||
|
@ -48,22 +50,28 @@ export async function extractPackageFile(
|
|||
const hexGroups = hexRegexp.exec(opts)?.groups;
|
||||
const hex = hexGroups?.strValue ?? hexGroups?.atomValue;
|
||||
|
||||
let dep: PackageDependency;
|
||||
const onlyValue = onlyValueRegexp.exec(opts)?.groups?.only;
|
||||
const onlyEnvironments = [];
|
||||
let match;
|
||||
if (onlyValue) {
|
||||
while ((match = onlyEnvironmentsRegexp.exec(onlyValue)) !== null) {
|
||||
onlyEnvironments.push(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
const dep: PackageDependency = {
|
||||
depName: app,
|
||||
depType: 'prod',
|
||||
};
|
||||
|
||||
if (git ?? github) {
|
||||
dep = {
|
||||
depName: app,
|
||||
currentDigest: ref,
|
||||
currentValue: branchOrTag,
|
||||
datasource: git ? GitTagsDatasource.id : GithubTagsDatasource.id,
|
||||
packageName: git ?? github,
|
||||
};
|
||||
dep.currentDigest = ref;
|
||||
dep.currentValue = branchOrTag;
|
||||
dep.datasource = git ? GitTagsDatasource.id : GithubTagsDatasource.id;
|
||||
dep.packageName = git ?? github;
|
||||
} else {
|
||||
dep = {
|
||||
depName: app,
|
||||
currentValue: requirement,
|
||||
datasource: HexDatasource.id,
|
||||
};
|
||||
dep.currentValue = requirement;
|
||||
dep.datasource = HexDatasource.id;
|
||||
if (organization) {
|
||||
dep.packageName = `${app}:${organization}`;
|
||||
} else if (hex) {
|
||||
|
@ -71,11 +79,16 @@ export async function extractPackageFile(
|
|||
} else {
|
||||
dep.packageName = app;
|
||||
}
|
||||
|
||||
if (requirement?.startsWith('==')) {
|
||||
dep.currentVersion = requirement.replace(regEx(/^==\s*/), '');
|
||||
}
|
||||
}
|
||||
|
||||
if (onlyValue !== undefined && !onlyEnvironments.includes('prod')) {
|
||||
dep.depType = 'dev';
|
||||
}
|
||||
|
||||
deps.set(app, dep);
|
||||
logger.trace({ dep }, `setting ${app}`);
|
||||
depMatchGroups = depMatchRegExp.exec(depBuffer)?.groups;
|
||||
|
|
|
@ -5,6 +5,7 @@ import { HexDatasource } from '../../datasource/hex';
|
|||
|
||||
export { extractPackageFile } from './extract';
|
||||
export { updateArtifacts } from './artifacts';
|
||||
export { getRangeStrategy } from './range';
|
||||
|
||||
export const url = 'https://hexdocs.pm/mix/Mix.html';
|
||||
export const categories: Category[] = ['elixir'];
|
||||
|
@ -13,6 +14,7 @@ export const defaultConfig = {
|
|||
fileMatch: ['(^|/)mix\\.exs$'],
|
||||
};
|
||||
|
||||
export const supportsLockFileMaintenance = true;
|
||||
export const supportedDatasources = [
|
||||
GithubTagsDatasource.id,
|
||||
GitTagsDatasource.id,
|
||||
|
|
47
lib/modules/manager/mix/range.spec.ts
Normal file
47
lib/modules/manager/mix/range.spec.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import type { RangeConfig } from '../types';
|
||||
import { getRangeStrategy } from '.';
|
||||
|
||||
describe('modules/manager/mix/range', () => {
|
||||
it('returns same if not auto', () => {
|
||||
const config: RangeConfig = { rangeStrategy: 'pin' };
|
||||
expect(getRangeStrategy(config)).toBe('pin');
|
||||
|
||||
config.rangeStrategy = 'widen';
|
||||
expect(getRangeStrategy(config)).toBe('widen');
|
||||
});
|
||||
|
||||
it('widens complex bump', () => {
|
||||
const config: RangeConfig = {
|
||||
rangeStrategy: 'bump',
|
||||
depType: 'prod',
|
||||
currentValue: '>= 1.6.0 and < 2.0.0',
|
||||
};
|
||||
expect(getRangeStrategy(config)).toBe('widen');
|
||||
});
|
||||
|
||||
it('bumps non-complex bump', () => {
|
||||
const config: RangeConfig = {
|
||||
rangeStrategy: 'bump',
|
||||
depType: 'prod',
|
||||
currentValue: '~>1.0.0',
|
||||
};
|
||||
expect(getRangeStrategy(config)).toBe('bump');
|
||||
});
|
||||
|
||||
it('widens complex auto', () => {
|
||||
const config: RangeConfig = {
|
||||
rangeStrategy: 'auto',
|
||||
depType: 'prod',
|
||||
currentValue: '<1.7.0 or ~>1.7.1',
|
||||
};
|
||||
expect(getRangeStrategy(config)).toBe('widen');
|
||||
});
|
||||
|
||||
it('defaults to update-lockfile', () => {
|
||||
const config: RangeConfig = {
|
||||
rangeStrategy: 'auto',
|
||||
depType: 'prod',
|
||||
};
|
||||
expect(getRangeStrategy(config)).toBe('update-lockfile');
|
||||
});
|
||||
});
|
26
lib/modules/manager/mix/range.ts
Normal file
26
lib/modules/manager/mix/range.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { parseRange } from 'semver-utils';
|
||||
import { logger } from '../../../logger';
|
||||
import type { RangeStrategy } from '../../../types';
|
||||
import type { RangeConfig } from '../types';
|
||||
|
||||
export function getRangeStrategy(config: RangeConfig): RangeStrategy {
|
||||
const { currentValue, rangeStrategy } = config;
|
||||
const isComplexRange = currentValue
|
||||
? parseRange(currentValue).length > 1
|
||||
: false;
|
||||
|
||||
if (rangeStrategy === 'bump' && isComplexRange) {
|
||||
logger.debug(
|
||||
{ currentValue },
|
||||
'Replacing bump strategy for complex range with widen',
|
||||
);
|
||||
return 'widen';
|
||||
}
|
||||
if (rangeStrategy !== 'auto') {
|
||||
return rangeStrategy;
|
||||
}
|
||||
if (isComplexRange) {
|
||||
return 'widen';
|
||||
}
|
||||
return 'update-lockfile';
|
||||
}
|
|
@ -1,3 +1,38 @@
|
|||
The `mix` manager extracts dependencies for the `hex` datasource and uses Renovate's implementation of Hex SemVer to evaluate updates.
|
||||
The `mix` manager uses Renovate's implementation of [Elixir SemVer](https://hexdocs.pm/elixir/Version.html#module-requirements) to evaluate update ranges.
|
||||
|
||||
The `mix` package manager itself is also used to keep the lock file up-to-date.
|
||||
The `mix` package manager itself is used to keep the lock file up-to-date.
|
||||
|
||||
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.
|
||||
|
||||
At the moment, without this setting, you will not benefit from updates that fall within the `mix.exs` ranges.
|
||||
|
||||
`lockFileMaintenance=true` periodically refreshes your `mix.lock` files, ensuring all 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"`.
|
||||
Here's how `"auto"` works with the `mix` manager:
|
||||
|
||||
| Version type | New version | Old range | New range after update | What Renovate does |
|
||||
| :----------------------- | :---------- | :-------------------- | :--------------------- | :------------------------------------------------------------------------ |
|
||||
| Complex range | `1.7.2` | `< 1.7.0 or ~> 1.7.1` | `< 1.7.0 or ~> 1.7.2` | Widen range to include the new version. |
|
||||
| Simple range | `0.39.0` | `<= 0.38.0` | `<= 0.39.0` | If update outside current range: widens range to include the new version. |
|
||||
| Exact version constraint | `0.13.0` | `== 0.12.0` | `== 0.13.0` | Replace old version with new version. |
|
||||
|
||||
### Recommended `rangeStrategy` for apps and libraries
|
||||
|
||||
For applications, we recommend using `rangeStrategy=pin`.
|
||||
This pins your dependencies to exact versions, which is generally considered [best practice for apps](../../../dependency-pinning.md).
|
||||
|
||||
For libraries, use `rangeStrategy=widen` with version ranges in your `mix.exs`.
|
||||
This allows for greater compatibility with other projects that may use your library as a dependency.
|
||||
|
|
Loading…
Reference in a new issue