Skip to content

Commit

Permalink
feat(cargo): support sparse registry indices
Browse files Browse the repository at this point in the history
  • Loading branch information
tofay committed Feb 13, 2023
1 parent 09402a3 commit 83edcee
Show file tree
Hide file tree
Showing 6 changed files with 400 additions and 12 deletions.
11 changes: 9 additions & 2 deletions docs/usage/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,18 @@ Renovate supports upgrading dependencies in `Cargo.toml` files and their accompa

Renovate updates Rust crates by default.

## Private crate registries and private Git dependencies
## Private Git crate registries and private Git dependencies

If any dependencies are hosted in private Git repositories, [Git Authentication for cargo](https://doc.rust-lang.org/cargo/appendix/git-authentication.html) must be set up.

If any dependencies are hosted on private crate registries (i.e., not on `crates.io`), then credentials should be set up in such a way that the Git command-line is able to clone the registry index.
If any dependencies are hosted on private Git crate registries (i.e., not on `crates.io`), then credentials should be set up in such a way that the Git command-line is able to clone the registry index.
Third-party crate registries usually provide instructions to achieve this.

Both of these are currently only possible when running Renovate self-hosted.

## Private sparse crate registries

Renovate can update dependencies hosted on a private sparse crate registry (<https://doc.rust-lang.org/beta/cargo/reference/registry-index.html#sparse-protocol>).
Since sparse registries are HTTP based, authentication for Renovate can be configured via additional hostRules.

Renovate can only update Cargo lockfiles for projects using dependencies from sparse registries if the local Rust toolchain has the sparse-registry feature enabled (i.e beta/nightly/or 1.68 or later - 1.68 is due for release on March 9th 2023).
4 changes: 4 additions & 0 deletions lib/modules/datasource/crate/__fixtures__/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"dl": "https://crates.io/api/v1/crates",
"api": "https://crates.io"
}
273 changes: 273 additions & 0 deletions lib/modules/datasource/crate/__snapshots__/index.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,279 @@ exports[`modules/datasource/crate/index getReleases clones other private registr
}
`;

exports[`modules/datasource/crate/index getReleases processes real data for sparse registry: amethyst 1`] = `
{
"dependencyUrl": "https://crates.io/api/v1/crates/amethyst",
"registryUrl": "sparse+https://index.crates.io",
"releases": [
{
"version": "0.1.0",
},
{
"version": "0.1.1",
},
{
"version": "0.1.3",
},
{
"version": "0.1.4",
},
{
"version": "0.2.1",
},
{
"version": "0.3.0",
},
{
"version": "0.3.1",
},
{
"version": "0.4.0",
},
{
"version": "0.4.1",
},
{
"version": "0.4.2",
},
{
"version": "0.4.3",
},
{
"version": "0.5.0",
},
{
"version": "0.5.1",
},
{
"version": "0.6.0",
},
{
"version": "0.7.0",
},
{
"version": "0.8.0",
},
{
"version": "0.9.0",
},
{
"version": "0.10.0",
},
{
"isDeprecated": true,
"version": "0.10.1",
},
],
}
`;

exports[`modules/datasource/crate/index getReleases processes real data for sparse registry: libc 1`] = `
{
"dependencyUrl": "https://crates.io/api/v1/crates/libc",
"registryUrl": "sparse+https://index.crates.io",
"releases": [
{
"version": "0.1.0",
},
{
"version": "0.1.1",
},
{
"version": "0.1.2",
},
{
"version": "0.1.3",
},
{
"version": "0.1.4",
},
{
"version": "0.1.5",
},
{
"version": "0.1.6",
},
{
"version": "0.1.7",
},
{
"version": "0.1.8",
},
{
"isDeprecated": true,
"version": "0.1.9",
},
{
"version": "0.1.10",
},
{
"isDeprecated": true,
"version": "0.1.11",
},
{
"version": "0.1.12",
},
{
"version": "0.2.0",
},
{
"version": "0.2.1",
},
{
"version": "0.2.2",
},
{
"version": "0.2.3",
},
{
"version": "0.2.4",
},
{
"version": "0.2.5",
},
{
"version": "0.2.6",
},
{
"version": "0.2.7",
},
{
"version": "0.2.8",
},
{
"version": "0.2.9",
},
{
"version": "0.2.10",
},
{
"version": "0.2.11",
},
{
"version": "0.2.12",
},
{
"version": "0.2.13",
},
{
"version": "0.2.14",
},
{
"version": "0.2.15",
},
{
"version": "0.2.16",
},
{
"version": "0.2.17",
},
{
"version": "0.2.18",
},
{
"version": "0.2.19",
},
{
"version": "0.2.20",
},
{
"version": "0.2.21",
},
{
"version": "0.2.22",
},
{
"version": "0.2.23",
},
{
"version": "0.2.24",
},
{
"version": "0.2.25",
},
{
"version": "0.2.26",
},
{
"version": "0.2.27",
},
{
"version": "0.2.28",
},
{
"version": "0.2.29",
},
{
"version": "0.2.30",
},
{
"version": "0.2.31",
},
{
"version": "0.2.32",
},
{
"version": "0.2.33",
},
{
"version": "0.2.34",
},
{
"version": "0.2.35",
},
{
"version": "0.2.36",
},
{
"version": "0.2.37",
},
{
"version": "0.2.38",
},
{
"version": "0.2.39",
},
{
"version": "0.2.40",
},
{
"version": "0.2.41",
},
{
"version": "0.2.42",
},
{
"version": "0.2.43",
},
{
"version": "0.2.44",
},
{
"version": "0.2.45",
},
{
"version": "0.2.46",
},
{
"version": "0.2.47",
},
{
"version": "0.2.48",
},
{
"version": "0.2.49",
},
{
"version": "0.2.50",
},
{
"version": "0.2.51",
},
],
}
`;

exports[`modules/datasource/crate/index getReleases processes real data: amethyst 1`] = `
{
"dependencyUrl": "https://crates.io/crates/amethyst",
Expand Down
58 changes: 57 additions & 1 deletion lib/modules/datasource/crate/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const API_BASE_URL = CrateDatasource.CRATES_IO_API_BASE_URL;
const baseUrl =
'https://raw.githubusercontent.com/rust-lang/crates.io-index/master/';

const sparseBaseUrl = 'https://index.crates.io';

const datasource = CrateDatasource.id;

function setupGitMocks(delayMs?: number): { mockClone: jest.Mock<any, any> } {
Expand Down Expand Up @@ -68,6 +70,13 @@ function mockCratesApiCallFor(crateName: string, response?: httpMock.Body) {
.reply(response ? 200 : 404, response);
}

function mockSparseConfig() {
httpMock
.scope(sparseBaseUrl)
.get('/config.json')
.reply(200, Fixtures.get('config.json'));
}

describe('modules/datasource/crate/index', () => {
describe('getIndexSuffix', () => {
it('returns correct suffixes', () => {
Expand Down Expand Up @@ -248,6 +257,40 @@ describe('modules/datasource/crate/index', () => {
expect(res).toBeDefined();
});

it('processes real data for sparse registry: libc', async () => {
mockSparseConfig();

httpMock
.scope(sparseBaseUrl)
.get('/li/bc/libc')
.reply(200, Fixtures.get('libc'));
const res = await getPkgReleases({
datasource,
depName: 'libc',
registryUrls: ['sparse+https://index.crates.io'],
});
expect(res).toMatchSnapshot();
expect(res).not.toBeNull();
expect(res).toBeDefined();
});

it('processes real data for sparse registry: amethyst', async () => {
mockSparseConfig();

httpMock
.scope(sparseBaseUrl)
.get('/am/et/amethyst')
.reply(200, Fixtures.get('amethyst'));
const res = await getPkgReleases({
datasource,
depName: 'amethyst',
registryUrls: ['sparse+https://index.crates.io'],
});
expect(res).toMatchSnapshot();
expect(res).not.toBeNull();
expect(res).toBeDefined();
});

it('refuses to clone if allowCustomCrateRegistries is not true', async () => {
const { mockClone } = setupGitMocks();

Expand Down Expand Up @@ -354,10 +397,23 @@ describe('modules/datasource/crate/index', () => {
expect(result).toBeNull();
expect(result2).toBeNull();
});

it('does not clone for sparse registries', async () => {
const { mockClone } = setupGitMocks();
mockSparseConfig();

const res = await getPkgReleases({
datasource,
depName: 'mypkg',
registryUrls: ['sparse+https://index.crates.io'],
});
expect(mockClone).toHaveBeenCalledTimes(0);
expect(res).toBeNull();
});
});

describe('fetchCrateRecordsPayload', () => {
it('rejects if it has neither clonePath nor crates.io flavor', async () => {
it('rejects if it has neither clonePath nor crates.io/sparse flavor', async () => {
const info: RegistryInfo = {
rawUrl: 'https://example.com',
url: new URL('https://example.com'),
Expand Down
Loading

0 comments on commit 83edcee

Please sign in to comment.