Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ clap = { version = "4", features = ["derive", "wrap_help", "string"] }
clap-cargo = "0.19.0"
clap_complete = { version = "4", features = ["unstable-dynamic"] }
console = "0.16"
dotenvy = "0.15.7"
effective-limits = "0.5.5"
enum-map = "3.0.0"
env_proxy = { version = "0.4.1", optional = true }
Expand Down
8 changes: 8 additions & 0 deletions doc/user-guide/src/environment-variables.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Environment variables

Rustup loads environment variables from an optional `${RUSTUP_HOME}/.env`.
For example:

```dotenv
RUSTUP_DIST_SERVER=https://example.com/rust-static
RUSTUP_TOOLCHAIN=stable
```

- `RUSTUP_LOG` (default: none). Enables Rustup's "custom logging mode". In this mode,
the verbosity of Rustup's log lines can be specified with `tracing_subscriber`'s
[directive syntax]. For example, set `RUSTUP_LOG=rustup=DEBUG` to receive log lines
Expand Down
1 change: 1 addition & 0 deletions src/bin/rustup-init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ fn main() -> Result<ExitCode> {
pre_rustup_main_init();

let process = Process::os();
process.load_dotenv()?;
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.worker_threads(process.io_thread_count()?.into())
Expand Down
10 changes: 10 additions & 0 deletions src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ impl Process {
Self::OsProcess(OsProcess::new())
}

pub fn load_dotenv(&self) -> Result<()> {
let path = self.rustup_home()?.join(".env");
match dotenvy::from_path(&path) {
Ok(()) => Ok(()),
Err(error) if error.not_found() => Ok(()),
Err(error) => Err(error)
.with_context(|| format!("failed to load environment file '{}'", path.display())),
}
}

pub fn name(&self) -> Option<String> {
let arg0 = match self.var("RUSTUP_FORCE_ARG0") {
Ok(v) => Some(v),
Expand Down
63 changes: 61 additions & 2 deletions tests/suite/cli_rustup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ use std::{
env::consts::EXE_SUFFIX,
fs,
path::{Path, PathBuf},
process::Command,
};

use rustup::{
env_var::RUST_RECURSION_COUNT_MAX,
for_host,
test::{
CROSS_ARCH1, CROSS_ARCH2, CliTestContext, MULTI_ARCH1, Scenario, this_host_tuple,
topical_doc_data,
Assert, CROSS_ARCH1, CROSS_ARCH2, CliTestContext, MULTI_ARCH1, SanitizedOutput, Scenario,
this_host_tuple, topical_doc_data,
},
utils::raw,
};
Expand Down Expand Up @@ -1416,6 +1417,64 @@ installed targets:
"#]]);
}

fn run_active_toolchain_subprocess(mut command: Command) -> Assert {
let output = command.output().unwrap();
Assert::new(SanitizedOutput {
status: output.status.code(),
stdout: String::from_utf8(output.stdout).unwrap(),
stderr: String::from_utf8(output.stderr).unwrap(),
})
}

#[tokio::test]
async fn show_toolchain_env_from_dotenv() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "toolchain", "install", "nightly", "beta"])
.await
.is_ok();
fs::write(
cx.config.rustupdir.join(".env"),
"RUSTUP_TOOLCHAIN=nightly\n",
)
.unwrap();

let mut command = cx.config.cmd("rustup", ["show", "active-toolchain"]);
command.env_remove("RUSTUP_TOOLCHAIN");
run_active_toolchain_subprocess(command)
.is_ok()
.with_stdout(snapbox::str![[r#"
nightly-[HOST_TUPLE] (overridden by environment variable RUSTUP_TOOLCHAIN)

"#]])
.with_stderr(snapbox::str![[""]]);

let mut command = cx.config.cmd("rustup", ["show", "active-toolchain"]);
command.env("RUSTUP_TOOLCHAIN", "beta");
run_active_toolchain_subprocess(command)
.is_ok()
.with_stdout(snapbox::str![[r#"
beta-[HOST_TUPLE] (overridden by environment variable RUSTUP_TOOLCHAIN)

"#]])
.with_stderr(snapbox::str![[""]]);
}

#[tokio::test]
async fn invalid_dotenv_is_reported() {
let cx = CliTestContext::new(Scenario::None).await;
fs::write(cx.config.rustupdir.join(".env"), "invalid line\n").unwrap();

let mut command = cx.config.cmd("rustup", ["--version"]);
command.env_remove("RUSTUP_TOOLCHAIN");
let output = command.output().unwrap();
let stderr = String::from_utf8(output.stderr).unwrap();

assert!(!output.status.success());
assert!(stderr.contains("failed to load environment file"));
assert!(stderr.contains(".env"));
}

#[tokio::test]
async fn show_toolchain_env_not_installed() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
Expand Down
Loading