forked from lix-project/lix-installer
Compare commits
17 commits
main
...
replace-pr
Author | SHA1 | Date | |
---|---|---|---|
3063635b98 | |||
0ff7364c9f | |||
0e191eadd1 | |||
d3f54dfea2 | |||
3da67105ca | |||
b5a4d15fb8 | |||
b5a56d5020 | |||
dd8a3b9714 | |||
f072444d4d | |||
7663d83aaf | |||
3b2713d9e9 | |||
7907c62963 | |||
e914be9215 | |||
f8dbb4db05 | |||
848d223fc6 | |||
be02735121 | |||
a3231d4b02 |
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -444,6 +444,14 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "export"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/DeterminateSystems/export#cf859216f9b4b9e27ef0aa0bcb2f52ca8a4e1c02"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.8"
|
||||
|
@ -963,6 +971,7 @@ dependencies = [
|
|||
"color-eyre",
|
||||
"dirs",
|
||||
"dyn-clone",
|
||||
"export",
|
||||
"eyre",
|
||||
"glob",
|
||||
"indexmap 2.1.0",
|
||||
|
|
|
@ -61,6 +61,7 @@ which = "4.4.0"
|
|||
sysctl = "0.5.4"
|
||||
walkdir = "2.3.3"
|
||||
indexmap = { version = "2.0.2", features = ["serde"] }
|
||||
export = { git = "https://github.com/DeterminateSystems/export" }
|
||||
|
||||
[dev-dependencies]
|
||||
eyre = { version = "0.6.8", default-features = false, features = [ "track-caller" ] }
|
||||
|
|
|
@ -9,8 +9,8 @@ use std::path::{Path, PathBuf};
|
|||
use tokio::task::JoinSet;
|
||||
use tracing::{span, Instrument, Span};
|
||||
|
||||
const PROFILE_NIX_FILE_SHELL: &str = "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh";
|
||||
const PROFILE_NIX_FILE_FISH: &str = "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.fish";
|
||||
pub(crate) const PROFILE_NIX_FILE_SHELL: &str = "/nix/nix-installer.d/profile.sh";
|
||||
pub(crate) const PROFILE_NIX_FILE_FISH: &str = "/nix/nix-installer.d/profile.fish";
|
||||
|
||||
/**
|
||||
Configure any detected shell profiles to include Nix support
|
||||
|
@ -31,14 +31,47 @@ impl ConfigureShellProfile {
|
|||
let mut create_directories = Vec::default();
|
||||
|
||||
let shell_buf = format!(
|
||||
"\n\
|
||||
# Nix\n\
|
||||
if [ -e '{PROFILE_NIX_FILE_SHELL}' ]; then\n\
|
||||
{inde}. '{PROFILE_NIX_FILE_SHELL}'\n\
|
||||
fi\n\
|
||||
# End Nix\n
|
||||
\n",
|
||||
inde = " ", // indent
|
||||
r#"
|
||||
|
||||
# Begin Nix
|
||||
if [ -f '{PROFILE_NIX_FILE_SHELL}' ]; then
|
||||
. '{PROFILE_NIX_FILE_SHELL}'
|
||||
fi
|
||||
# End Nix
|
||||
|
||||
"#
|
||||
);
|
||||
|
||||
create_directories.push(
|
||||
CreateDirectory::plan("/nix/nix-installer.d", None, None, 0o0755, false)
|
||||
.await
|
||||
.map_err(Self::error)?,
|
||||
);
|
||||
|
||||
create_or_insert_files.push(
|
||||
CreateOrInsertIntoFile::plan(
|
||||
PROFILE_NIX_FILE_SHELL,
|
||||
None,
|
||||
None,
|
||||
0o644,
|
||||
include_str!("./profiles/profile.sh").to_string(),
|
||||
create_or_insert_into_file::Position::Beginning,
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?,
|
||||
);
|
||||
|
||||
create_or_insert_files.push(
|
||||
CreateOrInsertIntoFile::plan(
|
||||
PROFILE_NIX_FILE_FISH,
|
||||
None,
|
||||
None,
|
||||
0o644,
|
||||
include_str!("./profiles/profile.fish").to_string(),
|
||||
create_or_insert_into_file::Position::Beginning,
|
||||
)
|
||||
.await
|
||||
.map_err(Self::error)?,
|
||||
);
|
||||
|
||||
for profile_target in locations.bash.iter().chain(locations.zsh.iter()) {
|
||||
|
@ -67,14 +100,15 @@ impl ConfigureShellProfile {
|
|||
}
|
||||
|
||||
let fish_buf = format!(
|
||||
"\n\
|
||||
# Nix\n\
|
||||
if test -e '{PROFILE_NIX_FILE_FISH}'\n\
|
||||
{inde}. '{PROFILE_NIX_FILE_FISH}'\n\
|
||||
end\n\
|
||||
# End Nix\n\
|
||||
\n",
|
||||
inde = " ", // indent
|
||||
r#"
|
||||
|
||||
# Begin Nix
|
||||
if [ -f {PROFILE_NIX_FILE_FISH} ]; then
|
||||
. {PROFILE_NIX_FILE_FISH}
|
||||
fi
|
||||
# End Nix
|
||||
|
||||
"#
|
||||
);
|
||||
|
||||
for fish_prefix in &locations.fish.confd_prefixes {
|
||||
|
|
3
src/action/common/profiles/profile.fish
Normal file
3
src/action/common/profiles/profile.fish
Normal file
|
@ -0,0 +1,3 @@
|
|||
if [ -f /nix/nix-installer ] && [ -x /nix/nix-installer ] && not set -q __ETC_PROFILE_NIX_SOURCED;
|
||||
eval "$(/nix/nix-installer export --format fish)"
|
||||
end
|
5
src/action/common/profiles/profile.sh
Normal file
5
src/action/common/profiles/profile.sh
Normal file
|
@ -0,0 +1,5 @@
|
|||
# shellcheck shell=sh
|
||||
|
||||
if [ -f /nix/nix-installer ] && [ -x /nix/nix-installer ] && [ -z "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then
|
||||
eval "$(/nix/nix-installer export --format sh)"
|
||||
fi
|
|
@ -1,10 +1,10 @@
|
|||
use crate::action::base::{create_or_insert_into_file, CreateOrInsertIntoFile};
|
||||
use crate::action::{Action, ActionDescription, ActionError, ActionTag, StatefulAction};
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use tracing::{span, Instrument, Span};
|
||||
|
||||
const PROFILE_NIX_FILE_SHELL: &str = "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh";
|
||||
use crate::action::base::{create_or_insert_into_file, CreateOrInsertIntoFile};
|
||||
use crate::action::common::configure_shell_profile::PROFILE_NIX_FILE_SHELL;
|
||||
use crate::action::{Action, ActionDescription, ActionError, ActionTag, StatefulAction};
|
||||
|
||||
/**
|
||||
Configure macOS's zshenv to load the Nix environment when ForceCommand is used.
|
||||
|
@ -20,12 +20,14 @@ impl ConfigureRemoteBuilding {
|
|||
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
|
||||
let shell_buf = format!(
|
||||
r#"
|
||||
|
||||
# Set up Nix only on SSH connections
|
||||
# See: https://github.com/DeterminateSystems/nix-installer/pull/714
|
||||
if [ -e '{PROFILE_NIX_FILE_SHELL}' ] && [ -n "${{SSH_CONNECTION}}" ] && [ "${{SHLVL}}" -eq 1 ]; then
|
||||
if [ -n "${{SSH_CONNECTION}}" ] && [ "${{SHLVL}}" -eq 1 ] && [ -f '{PROFILE_NIX_FILE_SHELL}' ]; then
|
||||
. '{PROFILE_NIX_FILE_SHELL}'
|
||||
fi
|
||||
# End Nix
|
||||
|
||||
"#
|
||||
);
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ impl CommandExecute for NixInstallerCli {
|
|||
NixInstallerSubcommand::Install(install) => install.execute().await,
|
||||
NixInstallerSubcommand::Repair(restore_shell) => restore_shell.execute().await,
|
||||
NixInstallerSubcommand::Uninstall(revert) => revert.execute().await,
|
||||
NixInstallerSubcommand::Export(export) => export.execute().await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
270
src/cli/subcommand/export.rs
Normal file
270
src/cli/subcommand/export.rs
Normal file
|
@ -0,0 +1,270 @@
|
|||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::io::{stdout, Write};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::ExitCode;
|
||||
|
||||
use crate::cli::CommandExecute;
|
||||
use clap::Parser;
|
||||
|
||||
const LOCAL_STATE_DIR: &str = "/nix/var";
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("The HOME environment variable is not set.")]
|
||||
HomeNotSet,
|
||||
|
||||
#[error("__ETC_PROFILE_NIX_SOURCED is set, indicating the relevant environment variables have already been set.")]
|
||||
AlreadyRun,
|
||||
|
||||
#[error("Some of the paths from Nix for XDG_DATA_DIR are not valid, due to an illegal character, like a colon.")]
|
||||
InvalidXdgDataDirs(Vec<PathBuf>),
|
||||
|
||||
#[error("Some of the paths from Nix for PATH are not valid, due to an illegal character, like a colon.")]
|
||||
InvalidPathDirs(Vec<PathBuf>),
|
||||
|
||||
#[error("Some of the paths from Nix for MANPATH are not valid, due to an illegal character, like a colon.")]
|
||||
InvalidManPathDirs(Vec<PathBuf>),
|
||||
}
|
||||
|
||||
/**
|
||||
Emit all the environment variables that should be set to use Nix.
|
||||
|
||||
Safety note: environment variables and values can contain any bytes except
|
||||
for a null byte. This includes newlines and spaces, which requires careful
|
||||
handling.
|
||||
|
||||
In `space-newline-separated` mode, `nix-installer` guarantees it will:
|
||||
|
||||
* only emit keys that are alphanumeric with underscores,
|
||||
* only emit values without newlines
|
||||
|
||||
and will refuse to emit any output to stdout if the variables and values
|
||||
would violate these safety rules.
|
||||
|
||||
In `null-separated` mode, `nix-installer` emits data in this format:
|
||||
|
||||
KEYNAME\0VALUE\0KEYNAME\0VALUE\0
|
||||
|
||||
*/
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(args_conflicts_with_subcommands = true)]
|
||||
pub struct Export {
|
||||
#[clap(long)]
|
||||
format: ExportFormat,
|
||||
|
||||
#[clap(long)]
|
||||
sample_output: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, clap::ValueEnum)]
|
||||
enum ExportFormat {
|
||||
Fish,
|
||||
Sh,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl CommandExecute for Export {
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
async fn execute(self) -> eyre::Result<ExitCode> {
|
||||
let env: HashMap<String, OsString> = match self.sample_output {
|
||||
Some(filename) => {
|
||||
// Note: not tokio File b/c I don't think serde_json has fancy async support?
|
||||
let file = std::fs::File::open(filename)?;
|
||||
let intermediate: HashMap<String, String> = serde_json::from_reader(file)?;
|
||||
intermediate
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.into()))
|
||||
.collect()
|
||||
},
|
||||
None => {
|
||||
match calculate_environment() {
|
||||
e @ Err(Error::AlreadyRun) => {
|
||||
tracing::debug!("Ignored error: {:?}", e);
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::warn!("Error setting up the environment for Nix: {:?}", e);
|
||||
// Don't return an Err, because we don't want to suggest bug reports for predictable problems.
|
||||
return Ok(ExitCode::FAILURE);
|
||||
},
|
||||
Ok(env) => env,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let mut export_env: HashMap<export::VariableName, OsString> = HashMap::new();
|
||||
for (k, v) in env.into_iter() {
|
||||
export_env.insert(k.try_into()?, v);
|
||||
}
|
||||
|
||||
stdout().write_all(
|
||||
export::escape(
|
||||
match self.format {
|
||||
ExportFormat::Fish => export::Encoding::Fish,
|
||||
ExportFormat::Sh => export::Encoding::PosixShell,
|
||||
},
|
||||
export_env,
|
||||
)?
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
}
|
||||
|
||||
fn nonempty_var_os(key: &str) -> Option<OsString> {
|
||||
env::var_os(key).filter(|val| !val.is_empty())
|
||||
}
|
||||
|
||||
fn env_path(key: &str) -> Option<Vec<PathBuf>> {
|
||||
let path = env::var_os(key)?;
|
||||
|
||||
if path.is_empty() {
|
||||
return Some(vec![]);
|
||||
}
|
||||
|
||||
Some(env::split_paths(&path).collect())
|
||||
}
|
||||
|
||||
pub fn calculate_environment() -> Result<HashMap<String, OsString>, Error> {
|
||||
let mut envs: HashMap<String, OsString> = HashMap::new();
|
||||
|
||||
// Don't export variables twice.
|
||||
// @PORT-NOTE nix-profile-daemon.sh.in and nix-profile-daemon.fish.in implemented
|
||||
// this behavior, but it was not implemented in nix-profile.sh.in and nix-profile.fish.in
|
||||
// even though I believe it is desirable in both cases.
|
||||
if nonempty_var_os("__ETC_PROFILE_NIX_SOURCED") == Some("1".into()) {
|
||||
return Err(Error::AlreadyRun);
|
||||
}
|
||||
|
||||
// @PORT-NOTE nix-profile.sh.in and nix-profile.fish.in check HOME and USER are set,
|
||||
// but not nix-profile-daemon.sh.in and nix-profile-daemon.fish.in.
|
||||
// The -daemon variants appear to just assume the values are set, which is probably
|
||||
// not safe, so we check it in all cases.
|
||||
let home = if let Some(home) = nonempty_var_os("HOME") {
|
||||
PathBuf::from(home)
|
||||
} else {
|
||||
return Err(Error::HomeNotSet);
|
||||
};
|
||||
|
||||
envs.insert("__ETC_PROFILE_NIX_SOURCED".into(), "1".into());
|
||||
|
||||
let nix_link: PathBuf = {
|
||||
let legacy_location = home.join(".nix-profile");
|
||||
let xdg_location = nonempty_var_os("XDG_STATE_HOME")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| home.join(".local/state"))
|
||||
.join("nix/profile");
|
||||
|
||||
if xdg_location.is_symlink() {
|
||||
// In the future we'll prefer the legacy location, but
|
||||
// evidently this is the intended order preference:
|
||||
// https://github.com/NixOS/nix/commit/2b801d6e3c3a3be6feb6fa2d9a0b009fa9261b45
|
||||
xdg_location
|
||||
} else {
|
||||
legacy_location
|
||||
}
|
||||
};
|
||||
|
||||
let nix_profiles = &[
|
||||
PathBuf::from(LOCAL_STATE_DIR).join("nix/profiles/default"),
|
||||
nix_link.clone(),
|
||||
];
|
||||
envs.insert(
|
||||
"NIX_PROFILES".into(),
|
||||
nix_profiles
|
||||
.iter()
|
||||
.map(|path| path.as_os_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(OsStr::new(" ")),
|
||||
);
|
||||
|
||||
{
|
||||
let mut xdg_data_dirs: Vec<PathBuf> = env_path("XDG_DATA_DIRS").unwrap_or_else(|| {
|
||||
vec![
|
||||
PathBuf::from("/usr/local/share"),
|
||||
PathBuf::from("/usr/share"),
|
||||
]
|
||||
});
|
||||
|
||||
xdg_data_dirs.extend(vec![
|
||||
nix_link.join("share"),
|
||||
PathBuf::from(LOCAL_STATE_DIR).join("nix/profiles/default/share"),
|
||||
]);
|
||||
|
||||
if let Ok(dirs) = env::join_paths(&xdg_data_dirs) {
|
||||
envs.insert("XDG_DATA_DIRS".into(), dirs);
|
||||
} else {
|
||||
return Err(Error::InvalidXdgDataDirs(xdg_data_dirs));
|
||||
}
|
||||
}
|
||||
|
||||
if nonempty_var_os("NIX_SSL_CERT_FILE").is_none() {
|
||||
let mut candidate_locations = vec![
|
||||
PathBuf::from("/etc/ssl/certs/ca-certificates.crt"), // NixOS, Ubuntu, Debian, Gentoo, Arch
|
||||
PathBuf::from("/etc/ssl/ca-bundle.pem"), // openSUSE Tumbleweed
|
||||
PathBuf::from("/etc/ssl/certs/ca-bundle.crt"), // Old NixOS
|
||||
PathBuf::from("/etc/pki/tls/certs/ca-bundle.crt"), // Fedora, CentOS
|
||||
];
|
||||
|
||||
// Add the various profiles, preferring the last profile, ie: most global profile (matches upstream behavior)
|
||||
for profile in nix_profiles.iter().rev() {
|
||||
candidate_locations.extend([
|
||||
profile.join("etc/ssl/certs/ca-bundle.crt"), // fall back to cacert in Nix profile
|
||||
profile.join("etc/ca-bundle.crt"), // old cacert in Nix profile
|
||||
]);
|
||||
}
|
||||
|
||||
if let Some(cert) = candidate_locations.iter().find(|path| path.is_file()) {
|
||||
envs.insert("NIX_SSL_CERT_FILE".into(), cert.into());
|
||||
} else {
|
||||
tracing::warn!(
|
||||
"Could not identify any SSL certificates out of these candidates: {:?}",
|
||||
candidate_locations
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
let mut path = vec![
|
||||
nix_link.join("bin"),
|
||||
// Note: This is typically only used in single-user installs, but I chose to do it in both for simplicity.
|
||||
// If there is good reason, we can make it fancier.
|
||||
PathBuf::from(LOCAL_STATE_DIR).join("nix/profiles/default/bin"),
|
||||
];
|
||||
|
||||
if let Some(old_path) = env_path("PATH") {
|
||||
path.extend(old_path);
|
||||
}
|
||||
|
||||
if let Ok(dirs) = env::join_paths(&path) {
|
||||
envs.insert("PATH".into(), dirs);
|
||||
} else {
|
||||
return Err(Error::InvalidPathDirs(path));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(old_path) = env_path("MANPATH") {
|
||||
let mut path = vec![
|
||||
nix_link.join("share/man"),
|
||||
// Note: This is typically only used in single-user installs, but I chose to do it in both for simplicity.
|
||||
// If there is good reason, we can make it fancier.
|
||||
PathBuf::from(LOCAL_STATE_DIR).join("nix/profiles/default/share/man"),
|
||||
];
|
||||
|
||||
path.extend(old_path);
|
||||
|
||||
if let Ok(dirs) = env::join_paths(&path) {
|
||||
envs.insert("MANPATH".into(), dirs);
|
||||
} else {
|
||||
return Err(Error::InvalidManPathDirs(path));
|
||||
}
|
||||
}
|
||||
|
||||
tracing::debug!("Calculated environment: {:#?}", envs);
|
||||
|
||||
Ok(envs)
|
||||
}
|
|
@ -1,18 +1,20 @@
|
|||
use std::{
|
||||
os::unix::prelude::PermissionsExt,
|
||||
path::{Path, PathBuf},
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
action::ActionState,
|
||||
action::{
|
||||
common::configure_shell_profile::PROFILE_NIX_FILE_FISH,
|
||||
common::configure_shell_profile::PROFILE_NIX_FILE_SHELL, ActionState,
|
||||
},
|
||||
cli::{
|
||||
ensure_root,
|
||||
interaction::{self, PromptChoice},
|
||||
signal_channel, CommandExecute,
|
||||
},
|
||||
error::HasExpectedErrors,
|
||||
plan::RECEIPT_LOCATION,
|
||||
plan::{copy_self_to_nix_dir, RECEIPT_LOCATION},
|
||||
planner::Planner,
|
||||
settings::CommonSettings,
|
||||
BuiltinPlanner, InstallPlan, NixInstallerError,
|
||||
|
@ -313,9 +315,8 @@ impl CommandExecute for Install {
|
|||
}
|
||||
},
|
||||
Ok(_) => {
|
||||
copy_self_to_nix_dir()
|
||||
.await
|
||||
.wrap_err("Copying `nix-installer` to `/nix/nix-installer`")?;
|
||||
let load_fish = format!(". {}", PROFILE_NIX_FILE_FISH);
|
||||
let load_shell = format!(". {}", PROFILE_NIX_FILE_SHELL);
|
||||
println!(
|
||||
"\
|
||||
{success}\n\
|
||||
|
@ -323,10 +324,12 @@ impl CommandExecute for Install {
|
|||
",
|
||||
success = "Nix was installed successfully!".green().bold(),
|
||||
shell_reminder = match std::env::var("SHELL") {
|
||||
Ok(val) if val.contains("fish") =>
|
||||
". /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.fish".bold(),
|
||||
Ok(_) | Err(_) =>
|
||||
". /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh".bold(),
|
||||
Ok(val) if val.contains("fish") => {
|
||||
load_fish.bold()
|
||||
},
|
||||
Ok(_) | Err(_) => {
|
||||
load_shell.bold()
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -335,11 +338,3 @@ impl CommandExecute for Install {
|
|||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug")]
|
||||
async fn copy_self_to_nix_dir() -> Result<(), std::io::Error> {
|
||||
let path = std::env::current_exe()?;
|
||||
tokio::fs::copy(path, "/nix/nix-installer").await?;
|
||||
tokio::fs::set_permissions("/nix/nix-installer", PermissionsExt::from_mode(0o0755)).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ mod uninstall;
|
|||
use uninstall::Uninstall;
|
||||
mod self_test;
|
||||
use self_test::SelfTest;
|
||||
mod export;
|
||||
pub use export::Export;
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
|
@ -17,4 +19,5 @@ pub enum NixInstallerSubcommand {
|
|||
Uninstall(Uninstall),
|
||||
SelfTest(SelfTest),
|
||||
Plan(Plan),
|
||||
Export(Export),
|
||||
}
|
||||
|
|
17
src/plan.rs
17
src/plan.rs
|
@ -1,13 +1,15 @@
|
|||
use std::os::unix::prelude::PermissionsExt;
|
||||
use std::{path::PathBuf, str::FromStr};
|
||||
|
||||
use owo_colors::OwoColorize;
|
||||
use semver::{Version, VersionReq};
|
||||
use tokio::sync::broadcast::Receiver;
|
||||
|
||||
use crate::{
|
||||
action::{Action, ActionDescription, StatefulAction},
|
||||
planner::{BuiltinPlanner, Planner},
|
||||
NixInstallerError,
|
||||
};
|
||||
use owo_colors::OwoColorize;
|
||||
use semver::{Version, VersionReq};
|
||||
use tokio::sync::broadcast::Receiver;
|
||||
|
||||
pub const RECEIPT_LOCATION: &str = "/nix/receipt.json";
|
||||
|
||||
|
@ -211,6 +213,7 @@ impl InstallPlan {
|
|||
}
|
||||
|
||||
write_receipt(self.clone()).await?;
|
||||
copy_self_to_nix_dir().await.ok();
|
||||
|
||||
if let Err(err) = crate::self_test::self_test()
|
||||
.await
|
||||
|
@ -425,6 +428,14 @@ async fn write_receipt(plan: InstallPlan) -> Result<(), NixInstallerError> {
|
|||
Result::<(), NixInstallerError>::Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug")]
|
||||
pub(crate) async fn copy_self_to_nix_dir() -> Result<(), std::io::Error> {
|
||||
let path = std::env::current_exe()?;
|
||||
tokio::fs::copy(path, "/nix/nix-installer").await?;
|
||||
tokio::fs::set_permissions("/nix/nix-installer", PermissionsExt::from_mode(0o0755)).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn current_version() -> Result<Version, NixInstallerError> {
|
||||
let nix_installer_version_str = env!("CARGO_PKG_VERSION");
|
||||
Version::from_str(nix_installer_version_str).map_err(|e| {
|
||||
|
|
Loading…
Reference in a new issue