]> git.proxmox.com Git - rustc.git/blame - src/tools/cargo/src/cargo/ops/registry/mod.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / src / tools / cargo / src / cargo / ops / registry / mod.rs
CommitLineData
fe692bf9
FG
1//! Operations that interact with the [registry web API][1].
2//!
3//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html
4
5mod login;
6mod logout;
7mod owner;
8mod publish;
9mod search;
10mod yank;
11
12use std::collections::HashSet;
fe692bf9
FG
13use std::str;
14use std::task::Poll;
15
16use anyhow::{bail, format_err, Context as _};
add651ee 17use cargo_credential::{Operation, Secret};
fe692bf9 18use crates_io::{self, Registry};
781aab86 19use url::Url;
fe692bf9 20
fe692bf9 21use crate::core::SourceId;
781aab86 22use crate::sources::source::Source;
fe692bf9 23use crate::sources::{RegistrySource, SourceConfigMap};
add651ee
FG
24use crate::util::auth;
25use crate::util::config::{Config, PathAndArgs};
fe692bf9
FG
26use crate::util::errors::CargoResult;
27use crate::util::network::http::http_handle;
fe692bf9
FG
28
29pub use self::login::registry_login;
30pub use self::logout::registry_logout;
31pub use self::owner::modify_owners;
32pub use self::owner::OwnersOptions;
33pub use self::publish::publish;
34pub use self::publish::PublishOpts;
35pub use self::search::search;
36pub use self::yank::yank;
37
781aab86
FG
38/// Represents either `--registry` or `--index` argument, which is mutually exclusive.
39#[derive(Debug, Clone)]
40pub enum RegistryOrIndex {
41 Registry(String),
42 Index(Url),
43}
44
45impl RegistryOrIndex {
46 fn is_index(&self) -> bool {
47 matches!(self, RegistryOrIndex::Index(..))
48 }
49}
50
fe692bf9
FG
51/// Registry settings loaded from config files.
52///
53/// This is loaded based on the `--registry` flag and the config settings.
54#[derive(Debug, PartialEq)]
55pub enum RegistryCredentialConfig {
56 None,
57 /// The authentication token.
58 Token(Secret<String>),
59 /// Process used for fetching a token.
add651ee 60 Process(Vec<PathAndArgs>),
fe692bf9
FG
61 /// Secret Key and subject for Asymmetric tokens.
62 AsymmetricKey((Secret<String>, Option<String>)),
63}
64
65impl RegistryCredentialConfig {
66 /// Returns `true` if the credential is [`None`].
67 ///
68 /// [`None`]: Self::None
69 pub fn is_none(&self) -> bool {
70 matches!(self, Self::None)
71 }
72 /// Returns `true` if the credential is [`Token`].
73 ///
74 /// [`Token`]: Self::Token
75 pub fn is_token(&self) -> bool {
76 matches!(self, Self::Token(..))
77 }
78 /// Returns `true` if the credential is [`AsymmetricKey`].
79 ///
80 /// [`AsymmetricKey`]: RegistryCredentialConfig::AsymmetricKey
81 pub fn is_asymmetric_key(&self) -> bool {
82 matches!(self, Self::AsymmetricKey(..))
83 }
84 pub fn as_token(&self) -> Option<Secret<&str>> {
85 if let Self::Token(v) = self {
86 Some(v.as_deref())
87 } else {
88 None
89 }
90 }
add651ee 91 pub fn as_process(&self) -> Option<&Vec<PathAndArgs>> {
fe692bf9
FG
92 if let Self::Process(v) = self {
93 Some(v)
94 } else {
95 None
96 }
97 }
98 pub fn as_asymmetric_key(&self) -> Option<&(Secret<String>, Option<String>)> {
99 if let Self::AsymmetricKey(v) = self {
100 Some(v)
101 } else {
102 None
103 }
104 }
105}
106
107/// Returns the `Registry` and `Source` based on command-line and config settings.
108///
109/// * `token_from_cmdline`: The token from the command-line. If not set, uses the token
110/// from the config.
111/// * `index`: The index URL from the command-line.
112/// * `registry`: The registry name from the command-line. If neither
113/// `registry`, or `index` are set, then uses `crates-io`.
114/// * `force_update`: If `true`, forces the index to be updated.
115/// * `token_required`: If `true`, the token will be set.
116fn registry(
117 config: &Config,
118 token_from_cmdline: Option<Secret<&str>>,
781aab86 119 reg_or_index: Option<&RegistryOrIndex>,
fe692bf9 120 force_update: bool,
add651ee 121 token_required: Option<Operation<'_>>,
fe692bf9 122) -> CargoResult<(Registry, RegistrySourceIds)> {
781aab86 123 let source_ids = get_source_id(config, reg_or_index)?;
fe692bf9 124
781aab86
FG
125 let is_index = reg_or_index.map(|v| v.is_index()).unwrap_or_default();
126 if is_index && token_required.is_some() && token_from_cmdline.is_none() {
fe692bf9
FG
127 bail!("command-line argument --index requires --token to be specified");
128 }
129 if let Some(token) = token_from_cmdline {
add651ee 130 auth::cache_token_from_commandline(config, &source_ids.original, token);
fe692bf9
FG
131 }
132
133 let cfg = {
134 let _lock = config.acquire_package_cache_lock()?;
135 let mut src = RegistrySource::remote(source_ids.replacement, &HashSet::new(), config)?;
136 // Only update the index if `force_update` is set.
137 if force_update {
138 src.invalidate_cache()
139 }
140 let cfg = loop {
141 match src.config()? {
142 Poll::Pending => src
143 .block_until_ready()
144 .with_context(|| format!("failed to update {}", source_ids.replacement))?,
145 Poll::Ready(cfg) => break cfg,
146 }
147 };
148 cfg.expect("remote registries must have config")
149 };
150 let api_host = cfg
151 .api
152 .ok_or_else(|| format_err!("{} does not support API commands", source_ids.replacement))?;
153 let token = if token_required.is_some() || cfg.auth_required {
add651ee 154 let operation = token_required.unwrap_or(Operation::Read);
fe692bf9
FG
155 Some(auth::auth_token(
156 config,
157 &source_ids.original,
158 None,
add651ee
FG
159 operation,
160 vec![],
781aab86 161 false,
fe692bf9
FG
162 )?)
163 } else {
164 None
165 };
166 let handle = http_handle(config)?;
167 Ok((
168 Registry::new_handle(api_host, token, handle, cfg.auth_required),
169 source_ids,
170 ))
171}
172
173/// Gets the SourceId for an index or registry setting.
174///
175/// The `index` and `reg` values are from the command-line or config settings.
176/// If both are None, and no source-replacement is configured, returns the source for crates.io.
177/// If both are None, and source replacement is configured, returns an error.
178///
179/// The source for crates.io may be GitHub, index.crates.io, or a test-only registry depending
180/// on configuration.
181///
182/// If `reg` is set, source replacement is not followed.
183///
184/// The return value is a pair of `SourceId`s: The first may be a built-in replacement of
185/// crates.io (such as index.crates.io), while the second is always the original source.
186fn get_source_id(
187 config: &Config,
781aab86 188 reg_or_index: Option<&RegistryOrIndex>,
fe692bf9 189) -> CargoResult<RegistrySourceIds> {
781aab86
FG
190 let sid = match reg_or_index {
191 None => SourceId::crates_io(config)?,
192 Some(RegistryOrIndex::Index(url)) => SourceId::for_registry(url)?,
193 Some(RegistryOrIndex::Registry(r)) => SourceId::alt_registry(config, r)?,
fe692bf9
FG
194 };
195 // Load source replacements that are built-in to Cargo.
196 let builtin_replacement_sid = SourceConfigMap::empty(config)?
197 .load(sid, &HashSet::new())?
198 .replaced_source_id();
199 let replacement_sid = SourceConfigMap::new(config)?
200 .load(sid, &HashSet::new())?
201 .replaced_source_id();
781aab86 202 if reg_or_index.is_none() && replacement_sid != builtin_replacement_sid {
fe692bf9
FG
203 // Neither --registry nor --index was passed and the user has configured source-replacement.
204 if let Some(replacement_name) = replacement_sid.alt_registry_key() {
205 bail!("crates-io is replaced with remote registry {replacement_name};\ninclude `--registry {replacement_name}` or `--registry crates-io`");
206 } else {
207 bail!("crates-io is replaced with non-remote-registry source {replacement_sid};\ninclude `--registry crates-io` to use crates.io");
208 }
209 } else {
210 Ok(RegistrySourceIds {
211 original: sid,
212 replacement: builtin_replacement_sid,
213 })
214 }
215}
216
217struct RegistrySourceIds {
218 /// Use when looking up the auth token, or writing out `Cargo.lock`
219 original: SourceId,
220 /// Use when interacting with the source (querying / publishing , etc)
221 ///
222 /// The source for crates.io may be replaced by a built-in source for accessing crates.io with
223 /// the sparse protocol, or a source for the testing framework (when the replace_crates_io
224 /// function is used)
225 ///
226 /// User-defined source replacement is not applied.
227 replacement: SourceId,
228}