From 872bb4b39ffa986b077c5c491c5208b090d30775 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 11 Oct 2023 16:53:05 +0200 Subject: [PATCH 01/13] Fix requirements parsing with index URL option --- lockfile/src/parsers/pypi.rs | 5 +++++ tests/fixtures/requirements-locked.txt | 3 +++ 2 files changed, 8 insertions(+) diff --git a/lockfile/src/parsers/pypi.rs b/lockfile/src/parsers/pypi.rs index 86ed643f8..9fb2593fa 100644 --- a/lockfile/src/parsers/pypi.rs +++ b/lockfile/src/parsers/pypi.rs @@ -52,6 +52,11 @@ fn line(input: &str) -> IResult<&str, &str> { line = ""; } + // Ignore index config options. + if line.starts_with("--index-url") || line.starts_with("--extra-index-url") { + line = ""; + } + Ok((input, line)) } diff --git a/tests/fixtures/requirements-locked.txt b/tests/fixtures/requirements-locked.txt index 29142950b..d6c2e94e9 100644 --- a/tests/fixtures/requirements-locked.txt +++ b/tests/fixtures/requirements-locked.txt @@ -18,6 +18,9 @@ attrs==20.2.0 # This is an inline comment requests[security,tests]==2.28.1 +--index-url https://mirror1.phylum.io/simple/ +--extra-index-url https://mirror2.phylum.io/simple/ + werkzeug==2.9.2 ; python_version >= "3.7" and python_version < "3.12" attr @ file:///tmp/attr From 15552c59c12b5fe59928ec2d5b645a370b20e402 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 11 Oct 2023 17:09:00 +0200 Subject: [PATCH 02/13] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbe4d77a1..78ecdac19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Workspace lockfile generation for cargo, npm, yarn, and pnpm - Go lockfile generation - Ignored manifests with a different ecosystem's lockfile in a parent directory +- Pip requirements.txt parser failing with third-party registries ## [5.7.1] - 2023-09-08 From 59c9bd99ed02369a1e6c201a254306d099bc90f3 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 11 Oct 2023 17:09:12 +0200 Subject: [PATCH 03/13] Detect third-party registries --- lockfile/src/parsers/pypi.rs | 28 +++++++++++++++++++------- lockfile/src/python.rs | 10 ++++++++- tests/fixtures/requirements-locked.txt | 7 ++++--- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/lockfile/src/parsers/pypi.rs b/lockfile/src/parsers/pypi.rs index 9fb2593fa..8eb996a05 100644 --- a/lockfile/src/parsers/pypi.rs +++ b/lockfile/src/parsers/pypi.rs @@ -11,14 +11,15 @@ use nom::Err as NomErr; use phylum_types::types::package::PackageType; use crate::parsers::{self, IResult}; -use crate::{Package, PackageVersion}; +use crate::{Package, PackageVersion, ThirdPartyVersion}; pub fn parse(mut input: &str) -> IResult<&str, Vec> { let mut pkgs = Vec::new(); + let mut registry = None; while !input.is_empty() { // Get the next line. - let (new_input, line) = line(input)?; + let (new_input, line) = line(input, &mut registry)?; input = new_input; // Ignore empty lines. @@ -30,7 +31,7 @@ pub fn parse(mut input: &str) -> IResult<&str, Vec> { let (_, line) = alt((take_until(" #"), rest))(line)?; // Parse dependency. - let (_, pkg) = package(line)?; + let (_, pkg) = package(line, registry)?; pkgs.push(pkg); } @@ -38,7 +39,7 @@ pub fn parse(mut input: &str) -> IResult<&str, Vec> { } /// Parse one line in the lockfile. -fn line(input: &str) -> IResult<&str, &str> { +fn line<'a>(input: &'a str, registry: &mut Option<&'a str>) -> IResult<&'a str, &'a str> { // Take everything until the next newline. // // This takes line continuation characters into account. @@ -53,14 +54,21 @@ fn line(input: &str) -> IResult<&str, &str> { } // Ignore index config options. - if line.starts_with("--index-url") || line.starts_with("--extra-index-url") { + // + // Since `ThirdPartyVersion` only allows a single registry, we only record the + // primary one. + if let Some(index_url) = line.strip_prefix("--index-url") { + *registry = Some(index_url.trim()); + line = ""; + } + if line.starts_with("--extra-index-url") { line = ""; } Ok((input, line)) } -fn package(input: &str) -> IResult<&str, Package> { +fn package<'a>(input: &'a str, registry: Option<&str>) -> IResult<&'a str, Package> { // Ignore everything after `;`. let (_, input) = alt((take_until(";"), rest))(input)?; @@ -90,7 +98,13 @@ fn package(input: &str) -> IResult<&str, Package> { // Parse first-party dependencies. let (input, version) = package_version(input)?; - let version = PackageVersion::FirstParty(version.trim().into()); + let version = match registry { + Some(registry) => PackageVersion::ThirdParty(ThirdPartyVersion { + version: version.trim().into(), + registry: registry.into(), + }), + None => PackageVersion::FirstParty(version.trim().into()), + }; // Ensure line is empty after the dependency. line_done(input)?; diff --git a/lockfile/src/python.rs b/lockfile/src/python.rs index 7cee3eda4..f383c3b87 100644 --- a/lockfile/src/python.rs +++ b/lockfile/src/python.rs @@ -238,7 +238,7 @@ mod tests { let pkgs = PyRequirements .parse(include_str!("../../tests/fixtures/requirements-locked.txt")) .unwrap(); - assert_eq!(pkgs.len(), 12); + assert_eq!(pkgs.len(), 13); let expected_pkgs = [ Package { @@ -301,6 +301,14 @@ mod tests { version: PackageVersion::Path(Some("/tmp/editable".into())), package_type: PackageType::PyPi, }, + Package { + name: "other-registry".into(), + version: PackageVersion::ThirdParty(ThirdPartyVersion { + registry: "https://mirror1.phylum.io/simple/".into(), + version: "1.2.3".into(), + }), + package_type: PackageType::PyPi, + }, ]; for expected_pkg in expected_pkgs { diff --git a/tests/fixtures/requirements-locked.txt b/tests/fixtures/requirements-locked.txt index d6c2e94e9..c59235206 100644 --- a/tests/fixtures/requirements-locked.txt +++ b/tests/fixtures/requirements-locked.txt @@ -18,9 +18,6 @@ attrs==20.2.0 # This is an inline comment requests[security,tests]==2.28.1 ---index-url https://mirror1.phylum.io/simple/ ---extra-index-url https://mirror2.phylum.io/simple/ - werkzeug==2.9.2 ; python_version >= "3.7" and python_version < "3.12" attr @ file:///tmp/attr @@ -35,3 +32,7 @@ tomli @ https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a25 -e git+ssh://git@github.com/phylum-dev/phylum-ci.git@7d6d859ad368d1ab0a933f24679e3d3c08a40eac#egg=phylum -e /tmp/editable ; python_version >= "3.7" and python_version < "3.12" + +--index-url https://mirror1.phylum.io/simple/ +--extra-index-url https://mirror2.phylum.io/simple/ +other-registry==1.2.3 From 2f0660ae0948eab8359be82769a47253d8a49c87 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 11 Oct 2023 17:23:52 +0200 Subject: [PATCH 04/13] Add support for extra-only --- lockfile/src/parsers/pypi.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lockfile/src/parsers/pypi.rs b/lockfile/src/parsers/pypi.rs index 8eb996a05..d3cd1ac20 100644 --- a/lockfile/src/parsers/pypi.rs +++ b/lockfile/src/parsers/pypi.rs @@ -55,13 +55,17 @@ fn line<'a>(input: &'a str, registry: &mut Option<&'a str>) -> IResult<&'a str, // Ignore index config options. // - // Since `ThirdPartyVersion` only allows a single registry, we only record the - // primary one. + // Since `ThirdPartyVersion` only allows a single registry, we prefer recording + // only the primary one. if let Some(index_url) = line.strip_prefix("--index-url") { *registry = Some(index_url.trim()); line = ""; } - if line.starts_with("--extra-index-url") { + if let Some(extra_index_url) = line.strip_prefix("--extra-index-url") { + // Only use extra index URL if no other URL has been specified. + if registry.is_none() { + *registry = Some(extra_index_url.trim()); + } line = ""; } From 54fdbfa6483a33b64dbaf62a6346d5ff3d822365 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 11 Oct 2023 17:46:09 +0200 Subject: [PATCH 05/13] Revert "Add support for extra-only" This reverts commit 2f0660ae0948eab8359be82769a47253d8a49c87. --- lockfile/src/parsers/pypi.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lockfile/src/parsers/pypi.rs b/lockfile/src/parsers/pypi.rs index d3cd1ac20..8eb996a05 100644 --- a/lockfile/src/parsers/pypi.rs +++ b/lockfile/src/parsers/pypi.rs @@ -55,17 +55,13 @@ fn line<'a>(input: &'a str, registry: &mut Option<&'a str>) -> IResult<&'a str, // Ignore index config options. // - // Since `ThirdPartyVersion` only allows a single registry, we prefer recording - // only the primary one. + // Since `ThirdPartyVersion` only allows a single registry, we only record the + // primary one. if let Some(index_url) = line.strip_prefix("--index-url") { *registry = Some(index_url.trim()); line = ""; } - if let Some(extra_index_url) = line.strip_prefix("--extra-index-url") { - // Only use extra index URL if no other URL has been specified. - if registry.is_none() { - *registry = Some(extra_index_url.trim()); - } + if line.starts_with("--extra-index-url") { line = ""; } From 7e83fee3c1225ea48cf7e30d9d8ceaf99d3a9a98 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 11 Oct 2023 17:48:26 +0200 Subject: [PATCH 06/13] Add support for short `-i` flag --- lockfile/src/parsers/pypi.rs | 4 +++- lockfile/src/python.rs | 2 +- tests/fixtures/requirements-locked.txt | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lockfile/src/parsers/pypi.rs b/lockfile/src/parsers/pypi.rs index 8eb996a05..e2330f816 100644 --- a/lockfile/src/parsers/pypi.rs +++ b/lockfile/src/parsers/pypi.rs @@ -57,7 +57,9 @@ fn line<'a>(input: &'a str, registry: &mut Option<&'a str>) -> IResult<&'a str, // // Since `ThirdPartyVersion` only allows a single registry, we only record the // primary one. - if let Some(index_url) = line.strip_prefix("--index-url") { + if let Some(index_url) = + line.strip_prefix("--index-url ").or_else(|| line.strip_prefix("-i ")) + { *registry = Some(index_url.trim()); line = ""; } diff --git a/lockfile/src/python.rs b/lockfile/src/python.rs index f383c3b87..ea55da30f 100644 --- a/lockfile/src/python.rs +++ b/lockfile/src/python.rs @@ -304,7 +304,7 @@ mod tests { Package { name: "other-registry".into(), version: PackageVersion::ThirdParty(ThirdPartyVersion { - registry: "https://mirror1.phylum.io/simple/".into(), + registry: "https://mirror2.phylum.io/simple/".into(), version: "1.2.3".into(), }), package_type: PackageType::PyPi, diff --git a/tests/fixtures/requirements-locked.txt b/tests/fixtures/requirements-locked.txt index c59235206..c1515d4ee 100644 --- a/tests/fixtures/requirements-locked.txt +++ b/tests/fixtures/requirements-locked.txt @@ -34,5 +34,6 @@ tomli @ https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a25 -e /tmp/editable ; python_version >= "3.7" and python_version < "3.12" --index-url https://mirror1.phylum.io/simple/ ---extra-index-url https://mirror2.phylum.io/simple/ +-i https://mirror2.phylum.io/simple/ +--extra-index-url https://mirror3.phylum.io/simple/ other-registry==1.2.3 From d6f19a72d183a4eb237e7f78700e1ced578f3077 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 11 Oct 2023 17:52:14 +0200 Subject: [PATCH 07/13] Fix formatting --- lockfile/src/parsers/pypi.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lockfile/src/parsers/pypi.rs b/lockfile/src/parsers/pypi.rs index e2330f816..6949f5dc6 100644 --- a/lockfile/src/parsers/pypi.rs +++ b/lockfile/src/parsers/pypi.rs @@ -57,8 +57,7 @@ fn line<'a>(input: &'a str, registry: &mut Option<&'a str>) -> IResult<&'a str, // // Since `ThirdPartyVersion` only allows a single registry, we only record the // primary one. - if let Some(index_url) = - line.strip_prefix("--index-url ").or_else(|| line.strip_prefix("-i ")) + if let Some(index_url) = line.strip_prefix("--index-url ").or_else(|| line.strip_prefix("-i ")) { *registry = Some(index_url.trim()); line = ""; From c0a8a518da50b3af0ee943e2ee7f54749ec9b137 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 11 Oct 2023 17:54:02 +0200 Subject: [PATCH 08/13] Fix parsing inconsistency --- lockfile/src/parsers/pypi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lockfile/src/parsers/pypi.rs b/lockfile/src/parsers/pypi.rs index 6949f5dc6..c6b844a25 100644 --- a/lockfile/src/parsers/pypi.rs +++ b/lockfile/src/parsers/pypi.rs @@ -62,7 +62,7 @@ fn line<'a>(input: &'a str, registry: &mut Option<&'a str>) -> IResult<&'a str, *registry = Some(index_url.trim()); line = ""; } - if line.starts_with("--extra-index-url") { + if line.starts_with("--extra-index-url ") { line = ""; } From 6f1485cc1331e61b8fa92c7d5dafe44f5ede8ac0 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 11 Oct 2023 18:00:28 +0200 Subject: [PATCH 09/13] Allow for equals in param --- lockfile/src/parsers/pypi.rs | 8 ++++++-- lockfile/src/python.rs | 10 +++++++++- tests/fixtures/requirements-locked.txt | 4 +++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lockfile/src/parsers/pypi.rs b/lockfile/src/parsers/pypi.rs index c6b844a25..e80aabbaf 100644 --- a/lockfile/src/parsers/pypi.rs +++ b/lockfile/src/parsers/pypi.rs @@ -57,12 +57,16 @@ fn line<'a>(input: &'a str, registry: &mut Option<&'a str>) -> IResult<&'a str, // // Since `ThirdPartyVersion` only allows a single registry, we only record the // primary one. - if let Some(index_url) = line.strip_prefix("--index-url ").or_else(|| line.strip_prefix("-i ")) + if let Some(mut index_url) = + line.strip_prefix("--index-url").or_else(|| line.strip_prefix("-i")) { + // Strip optional `=` from `--index-url=https://...` + index_url = index_url.trim().strip_prefix('=').unwrap_or(index_url).trim(); + *registry = Some(index_url.trim()); line = ""; } - if line.starts_with("--extra-index-url ") { + if line.starts_with("--extra-index-url") { line = ""; } diff --git a/lockfile/src/python.rs b/lockfile/src/python.rs index ea55da30f..09b84b4f0 100644 --- a/lockfile/src/python.rs +++ b/lockfile/src/python.rs @@ -238,7 +238,7 @@ mod tests { let pkgs = PyRequirements .parse(include_str!("../../tests/fixtures/requirements-locked.txt")) .unwrap(); - assert_eq!(pkgs.len(), 13); + assert_eq!(pkgs.len(), 14); let expected_pkgs = [ Package { @@ -301,6 +301,14 @@ mod tests { version: PackageVersion::Path(Some("/tmp/editable".into())), package_type: PackageType::PyPi, }, + Package { + name: "other-registry-a".into(), + version: PackageVersion::ThirdParty(ThirdPartyVersion { + registry: "https://mirror1.phylum.io/simple/".into(), + version: "3.2.1".into(), + }), + package_type: PackageType::PyPi, + }, Package { name: "other-registry".into(), version: PackageVersion::ThirdParty(ThirdPartyVersion { diff --git a/tests/fixtures/requirements-locked.txt b/tests/fixtures/requirements-locked.txt index c1515d4ee..797df1d49 100644 --- a/tests/fixtures/requirements-locked.txt +++ b/tests/fixtures/requirements-locked.txt @@ -34,6 +34,8 @@ tomli @ https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a25 -e /tmp/editable ; python_version >= "3.7" and python_version < "3.12" --index-url https://mirror1.phylum.io/simple/ --i https://mirror2.phylum.io/simple/ +other-registry-a==3.2.1 +-i=https://mirror2.phylum.io/simple/ --extra-index-url https://mirror3.phylum.io/simple/ +--extra-index-url=https://mirror3.phylum.io/simple/ other-registry==1.2.3 From f0cf0692dc7098fbf3defc1ba2a73330001ddaae Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 11 Oct 2023 18:22:30 +0200 Subject: [PATCH 10/13] Remove invalid format support --- lockfile/src/parsers/pypi.rs | 3 --- tests/fixtures/requirements-locked.txt | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lockfile/src/parsers/pypi.rs b/lockfile/src/parsers/pypi.rs index e80aabbaf..d11e6dae1 100644 --- a/lockfile/src/parsers/pypi.rs +++ b/lockfile/src/parsers/pypi.rs @@ -60,9 +60,6 @@ fn line<'a>(input: &'a str, registry: &mut Option<&'a str>) -> IResult<&'a str, if let Some(mut index_url) = line.strip_prefix("--index-url").or_else(|| line.strip_prefix("-i")) { - // Strip optional `=` from `--index-url=https://...` - index_url = index_url.trim().strip_prefix('=').unwrap_or(index_url).trim(); - *registry = Some(index_url.trim()); line = ""; } diff --git a/tests/fixtures/requirements-locked.txt b/tests/fixtures/requirements-locked.txt index 797df1d49..20fe3429e 100644 --- a/tests/fixtures/requirements-locked.txt +++ b/tests/fixtures/requirements-locked.txt @@ -35,7 +35,6 @@ tomli @ https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a25 --index-url https://mirror1.phylum.io/simple/ other-registry-a==3.2.1 --i=https://mirror2.phylum.io/simple/ +-i https://mirror2.phylum.io/simple/ --extra-index-url https://mirror3.phylum.io/simple/ ---extra-index-url=https://mirror3.phylum.io/simple/ other-registry==1.2.3 From d1b90391f1144b2e1f4d124b3d9a99b29c8a3cbc Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 11 Oct 2023 18:25:42 +0200 Subject: [PATCH 11/13] Add test without space --- lockfile/src/parsers/pypi.rs | 4 +--- tests/fixtures/requirements-locked.txt | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lockfile/src/parsers/pypi.rs b/lockfile/src/parsers/pypi.rs index d11e6dae1..0f7425528 100644 --- a/lockfile/src/parsers/pypi.rs +++ b/lockfile/src/parsers/pypi.rs @@ -57,9 +57,7 @@ fn line<'a>(input: &'a str, registry: &mut Option<&'a str>) -> IResult<&'a str, // // Since `ThirdPartyVersion` only allows a single registry, we only record the // primary one. - if let Some(mut index_url) = - line.strip_prefix("--index-url").or_else(|| line.strip_prefix("-i")) - { + if let Some(index_url) = line.strip_prefix("--index-url").or_else(|| line.strip_prefix("-i")) { *registry = Some(index_url.trim()); line = ""; } diff --git a/tests/fixtures/requirements-locked.txt b/tests/fixtures/requirements-locked.txt index 20fe3429e..c27a7f35f 100644 --- a/tests/fixtures/requirements-locked.txt +++ b/tests/fixtures/requirements-locked.txt @@ -35,6 +35,6 @@ tomli @ https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a25 --index-url https://mirror1.phylum.io/simple/ other-registry-a==3.2.1 --i https://mirror2.phylum.io/simple/ +-ihttps://mirror2.phylum.io/simple/ --extra-index-url https://mirror3.phylum.io/simple/ other-registry==1.2.3 From e941e0894792d17b743dae00b8a6ec8219e3dd73 Mon Sep 17 00:00:00 2001 From: Kyle Willmon Date: Wed, 11 Oct 2023 13:26:29 -0500 Subject: [PATCH 12/13] Move CHANGELOG entry --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78ecdac19..ce1879f1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Fixed +- Pip requirements.txt parser failing with third-party registries + ## [5.7.2] - 2023-10-10 ### Fixed - Workspace lockfile generation for cargo, npm, yarn, and pnpm - Go lockfile generation - Ignored manifests with a different ecosystem's lockfile in a parent directory -- Pip requirements.txt parser failing with third-party registries ## [5.7.1] - 2023-09-08 From 3a93a4306e7b44daa69b6dbfd06ff94670e63bbc Mon Sep 17 00:00:00 2001 From: Kyle Willmon Date: Wed, 11 Oct 2023 13:28:31 -0500 Subject: [PATCH 13/13] Allow --index-url= form --- lockfile/src/parsers/pypi.rs | 6 +++++- tests/fixtures/requirements-locked.txt | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lockfile/src/parsers/pypi.rs b/lockfile/src/parsers/pypi.rs index 0f7425528..4064e3b5a 100644 --- a/lockfile/src/parsers/pypi.rs +++ b/lockfile/src/parsers/pypi.rs @@ -57,7 +57,11 @@ fn line<'a>(input: &'a str, registry: &mut Option<&'a str>) -> IResult<&'a str, // // Since `ThirdPartyVersion` only allows a single registry, we only record the // primary one. - if let Some(index_url) = line.strip_prefix("--index-url").or_else(|| line.strip_prefix("-i")) { + if let Some(index_url) = line + .strip_prefix("--index-url") + .and_then(|line| line.strip_prefix(['=', ' '])) + .or_else(|| line.strip_prefix("-i")) + { *registry = Some(index_url.trim()); line = ""; } diff --git a/tests/fixtures/requirements-locked.txt b/tests/fixtures/requirements-locked.txt index c27a7f35f..07297db1c 100644 --- a/tests/fixtures/requirements-locked.txt +++ b/tests/fixtures/requirements-locked.txt @@ -33,8 +33,9 @@ tomli @ https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a25 -e /tmp/editable ; python_version >= "3.7" and python_version < "3.12" ---index-url https://mirror1.phylum.io/simple/ +--index-url https://unused.phylum.io/simple/ +--index-url=https://mirror1.phylum.io/simple/ other-registry-a==3.2.1 -ihttps://mirror2.phylum.io/simple/ ---extra-index-url https://mirror3.phylum.io/simple/ +--extra-index-url=https://mirror3.phylum.io/simple/ other-registry==1.2.3