From d0cfc042eba92eb206611c9e8784d41a2c053bab Mon Sep 17 00:00:00 2001 From: Derek Mahar Date: Fri, 8 Sep 2023 03:34:30 -0400 Subject: [PATCH 1/4] Update README.md (#227) Replace "eachother" with "each other". --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index af11f787..aff46f97 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ There is also an `activate` binary though this should be ignored, it is only use ### Multi-profile -This type of design (as opposed to more traditional tools like NixOps or morph) allows for lesser-privileged deployments, and the ability to update different things independently of eachother. You can deploy any type of profile to any user, not just a NixOS profile to `root`. +This type of design (as opposed to more traditional tools like NixOps or morph) allows for lesser-privileged deployments, and the ability to update different things independently of each other. You can deploy any type of profile to any user, not just a NixOS profile to `root`. ### Magic Rollback From f26e888c41d28107de9dbc5b4e1553c1dfcf83db Mon Sep 17 00:00:00 2001 From: Roman Melnikov Date: Wed, 6 Sep 2023 14:54:22 +0200 Subject: [PATCH 2/4] [#201] Deduce profile directory during activation Problem: Since https://github.com/NixOS/nix/pull/5226 nix profiles for users are stored in 'XDG_STATE_HOME' or 'HOME' directory. However, 'deploy-rs' still expects profiles to be present in '/nix/var/nix/profiles/per-user'. As a result, an attempt to deploy a profile with newer nix may fail with an error about non-existing files. Solution: Instead of deducing the profile path prior to ssh'ing and actual activation, deduce the path to the profile during as a part of 'activate-rs' invocation. Now if the profile path is not specified explicitly as an attribute in profile within the deploy flake, the path to the profile is determined based on the user to which the profile belongs and on the values of 'XDG_STATE_HOME' and 'HOME' variables. Additionally, if the old profile directory (in '/nix/var/nix/profiles/per-user') for a given user already exists, it is used instead for the sake of backward compatibility. --- Cargo.lock | 163 ++++++++++++++++++++++++++++++++++++++------ Cargo.toml | 1 + README.md | 7 +- src/bin/activate.rs | 110 ++++++++++++++++++++++++++---- src/deploy.rs | 65 ++++++++++++++---- src/lib.rs | 38 +++++------ 6 files changed, 316 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 75044275..55e55643 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -131,6 +131,7 @@ name = "deploy-rs" version = "0.1.0" dependencies = [ "clap", + "dirs", "flexi_logger", "fork", "futures-util", @@ -149,6 +150,27 @@ dependencies = [ "yn", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "filetime" version = "0.2.13" @@ -157,7 +179,7 @@ checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.1.57", "winapi", ] @@ -238,6 +260,17 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "glob" version = "0.3.0" @@ -332,9 +365,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "log" @@ -470,6 +503,12 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "os_str_bytes" version = "2.4.0" @@ -568,6 +607,26 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + [[package]] name = "regex" version = "1.7.3" @@ -698,7 +757,7 @@ checksum = "2c29947abdee2a218277abeca306f25789c938e500ea5a9d4b12a5a504466902" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.1.57", "winapi", ] @@ -925,13 +984,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -940,7 +999,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -949,13 +1017,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -964,42 +1047,84 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "yansi" version = "0.5.0" diff --git a/Cargo.toml b/Cargo.toml index 7b93c3d2..638e8a27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ edition = "2018" [dependencies] clap = { version = "3.0.0-beta.2", features = [ "wrap_help" ] } +dirs = "5.0.1" flexi_logger = "0.16" fork = "0.1" futures-util = "0.3.6" diff --git a/README.md b/README.md index af11f787..41201b73 100644 --- a/README.md +++ b/README.md @@ -125,8 +125,11 @@ This is the core of how `deploy-rs` was designed, any number of these can run on path = deploy-rs.lib.x86_64-linux.activate.custom pkgs.hello "./bin/hello"; # An optional path to where your profile should be installed to, this is useful if you want to use a common profile name across multiple users, but would have conflicts in your node's profile list. - # This will default to `"/nix/var/nix/profiles/$PROFILE_NAME` if `user` is root (see: generic options), and `/nix/var/nix/profiles/per-user/$USER/$PROFILE_NAME` if it is not. - profilePath = "/nix/var/nix/profiles/per-user/someuser/someprofile"; + # This will default to `"/nix/var/nix/profiles/system` if `user` is `root` and profile name is `system`, + # `/nix/var/nix/profiles/per-user/root/$PROFILE_NAME` if profile name is different. + # For non-root profiles will default to /nix/var/nix/profiles/per-user/$USER/$PROFILE_NAME if `/nix/var/nix/profiles/per-user/$USER` already exists, + # and `${XDG_STATE_HOME:-$HOME/.local/state}/nix/profiles/$PROFILE_NAME` otherwise. + profilePath = "/home/someuser/.local/state/nix/profiles/someprofile"; # ...generic options... (see lower section) } diff --git a/src/bin/activate.rs b/src/bin/activate.rs index bf035389..4a2760b6 100644 --- a/src/bin/activate.rs +++ b/src/bin/activate.rs @@ -15,9 +15,10 @@ use tokio::time::timeout; use std::time::Duration; -use std::path::PathBuf; +use std::env; +use std::path::{Path, PathBuf}; -use notify::{RecommendedWatcher, RecursiveMode, Watcher, recommended_watcher}; +use notify::{recommended_watcher, RecommendedWatcher, RecursiveMode, Watcher}; use thiserror::Error; @@ -47,11 +48,24 @@ enum SubCommand { /// Activate a profile #[derive(Clap, Debug)] +#[clap(group( + clap::ArgGroup::new("profile") + .required(true) + .multiple(false) + .args(&["profile-path","profile-user"]) +))] struct ActivateOpts { /// The closure to activate closure: String, /// The profile path to install into - profile_path: String, + #[clap(long)] + profile_path: Option, + /// The profile user if explicit profile path is not specified + #[clap(long, requires = "profile-name")] + profile_user: Option, + /// The profile name + #[clap(long, requires = "profile-user")] + profile_name: Option, /// Maximum time to wait for confirmation after activation #[clap(long)] @@ -78,7 +92,7 @@ struct ActivateOpts { temp_path: PathBuf, } -/// Activate a profile +/// Wait for profile activation #[derive(Clap, Debug)] struct WaitOpts { /// The closure to wait for @@ -89,11 +103,18 @@ struct WaitOpts { temp_path: PathBuf, } -/// Activate a profile +/// Revoke profile activation #[derive(Clap, Debug)] struct RevokeOpts { - /// The profile path to revoke - profile_path: String, + /// The profile path to install into + #[clap(long)] + profile_path: Option, + /// The profile user if explicit profile path is not specified + #[clap(long, requires = "profile-name")] + profile_user: Option, + /// The profile name + #[clap(long, requires = "profile-user")] + profile_name: Option, } #[derive(Error, Debug)] @@ -315,8 +336,8 @@ pub async fn wait(temp_path: PathBuf, closure: String) -> Result<(), WaitError> // 'lock_path' may not exist yet when some other files are created in 'temp_path' // x is already supposed to be canonical path Ok(lock_path) if x == &lock_path => created.try_send(Ok(())), - _ => Ok (()) - } + _ => Ok(()), + }, _ => Ok(()), } } @@ -459,6 +480,61 @@ async fn revoke(profile_path: String) -> Result<(), DeactivateError> { Ok(()) } +#[derive(Error, Debug)] +pub enum GetProfilePathError { + #[error("Failed to deduce HOME directory for user {0}")] + NoUserHome(String), +} + +fn get_profile_path( + profile_path: Option, + profile_user: Option, + profile_name: Option, +) -> Result { + match (profile_path, profile_user, profile_name) { + (Some(profile_path), None, None) => Ok(profile_path), + (None, Some(profile_user), Some(profile_name)) => { + let nix_state_dir = env::var("NIX_STATE_DIR").unwrap_or("/nix/var/nix".to_string()); + // As per https://nixos.org/manual/nix/stable/command-ref/files/profiles#profiles + match &profile_user[..] { + "root" => { + match &profile_name[..] { + // NixOS system profile belongs to the root user, but isn't stored in the 'per-user/root' + "system" => Ok(format!("{}/profiles/system", nix_state_dir)), + _ => Ok(format!( + "{}/profiles/per-user/root/{}", + nix_state_dir, profile_name + )), + } + } + _ => { + let old_user_profiles_dir = + format!("{}/profiles/per-user/{}", nix_state_dir, profile_user); + // To stay backward compatible + if Path::new(&old_user_profiles_dir).exists() { + Ok(format!("{}/{}", old_user_profiles_dir, profile_name)) + } else { + // https://github.com/NixOS/nix/blob/2.17.0/src/libstore/profiles.cc#L308 + // This is basically the equivalent of calling 'dirs::state_dir()'. + // However, this function returns 'None' on macOS, while nix will actually + // check env variables, so we imitate nix implementation below instead of + // using 'dirs::state_dir()' directly. + let state_dir = env::var("XDG_STATE_HOME").or_else(|_| { + dirs::home_dir() + .map(|h| { + format!("{}/.local/state", h.as_path().display().to_string()) + }) + .ok_or(GetProfilePathError::NoUserHome(profile_user)) + })?; + Ok(format!("{}/nix/profiles/{}", state_dir, profile_name)) + } + } + } + } + _ => panic!("impossible"), + } +} + #[tokio::main] async fn main() -> Result<(), Box> { // Ensure that this process stays alive after the SSH connection dies @@ -483,7 +559,11 @@ async fn main() -> Result<(), Box> { let r = match opts.subcmd { SubCommand::Activate(activate_opts) => activate( - activate_opts.profile_path, + get_profile_path( + activate_opts.profile_path, + activate_opts.profile_user, + activate_opts.profile_name, + )?, activate_opts.closure, activate_opts.auto_rollback, activate_opts.temp_path, @@ -499,9 +579,13 @@ async fn main() -> Result<(), Box> { .await .map_err(|x| Box::new(x) as Box), - SubCommand::Revoke(revoke_opts) => revoke(revoke_opts.profile_path) - .await - .map_err(|x| Box::new(x) as Box), + SubCommand::Revoke(revoke_opts) => revoke(get_profile_path( + revoke_opts.profile_path, + revoke_opts.profile_user, + revoke_opts.profile_name, + )?) + .await + .map_err(|x| Box::new(x) as Box), }; match r { diff --git a/src/deploy.rs b/src/deploy.rs index 574e9b2e..41cd58ba 100644 --- a/src/deploy.rs +++ b/src/deploy.rs @@ -9,11 +9,11 @@ use std::path::Path; use thiserror::Error; use tokio::process::Command; -use crate::DeployDataDefsError; +use crate::{DeployDataDefsError, ProfileInfo}; struct ActivateCommandData<'a> { sudo: &'a Option, - profile_path: &'a str, + profile_info: &'a ProfileInfo, closure: &'a str, auto_rollback: bool, temp_path: &'a Path, @@ -37,8 +37,21 @@ fn build_activate_command(data: &ActivateCommandData) -> String { } self_activate_command = format!( - "{} activate '{}' '{}' --temp-path '{}'", - self_activate_command, data.closure, data.profile_path, data.temp_path.display() + "{} activate '{}' {} --temp-path '{}'", + self_activate_command, + data.closure, + match data.profile_info { + ProfileInfo::ProfilePath { profile_path } => + format!("--profile-path '{}'", profile_path), + ProfileInfo::ProfileUserAndName { + profile_user, + profile_name, + } => format!( + "--profile-user {} --profile-name {}", + profile_user, profile_name + ), + }, + data.temp_path.display() ); self_activate_command = format!( @@ -72,7 +85,9 @@ fn build_activate_command(data: &ActivateCommandData) -> String { #[test] fn test_activation_command_builder() { let sudo = Some("sudo -u test".to_string()); - let profile_path = "/blah/profiles/test"; + let profile_info = &ProfileInfo::ProfilePath { + profile_path: "/blah/profiles/test".to_string(), + }; let closure = "/nix/store/blah/etc"; let auto_rollback = true; let dry_activate = false; @@ -86,7 +101,7 @@ fn test_activation_command_builder() { assert_eq!( build_activate_command(&ActivateCommandData { sudo: &sudo, - profile_path, + profile_info, closure, auto_rollback, temp_path, @@ -97,7 +112,7 @@ fn test_activation_command_builder() { dry_activate, boot, }), - "sudo -u test /nix/store/blah/etc/activate-rs --debug-logs --log-dir /tmp/something.txt activate '/nix/store/blah/etc' '/blah/profiles/test' --temp-path '/tmp' --confirm-timeout 30 --magic-rollback --auto-rollback" + "sudo -u test /nix/store/blah/etc/activate-rs --debug-logs --log-dir /tmp/something.txt activate '/nix/store/blah/etc' --profile-path '/blah/profiles/test' --temp-path '/tmp' --confirm-timeout 30 --magic-rollback --auto-rollback" .to_string(), ); } @@ -123,7 +138,9 @@ fn build_wait_command(data: &WaitCommandData) -> String { self_activate_command = format!( "{} wait '{}' --temp-path '{}'", - self_activate_command, data.closure, data.temp_path.display(), + self_activate_command, + data.closure, + data.temp_path.display(), ); if let Some(sudo_cmd) = &data.sudo { @@ -157,7 +174,7 @@ fn test_wait_command_builder() { struct RevokeCommandData<'a> { sudo: &'a Option, closure: &'a str, - profile_path: &'a str, + profile_info: ProfileInfo, debug_logs: bool, log_dir: Option<&'a str>, } @@ -173,7 +190,21 @@ fn build_revoke_command(data: &RevokeCommandData) -> String { self_activate_command = format!("{} --log-dir {}", self_activate_command, log_dir); } - self_activate_command = format!("{} revoke '{}'", self_activate_command, data.profile_path); + self_activate_command = format!( + "{} revoke {}", + self_activate_command, + match &data.profile_info { + ProfileInfo::ProfilePath { profile_path } => + format!("--profile-path '{}'", profile_path), + ProfileInfo::ProfileUserAndName { + profile_user, + profile_name, + } => format!( + "--profile-user {} --profile-name {}", + profile_user, profile_name + ), + } + ); if let Some(sudo_cmd) = &data.sudo { self_activate_command = format!("{} {}", sudo_cmd, self_activate_command); @@ -186,7 +217,9 @@ fn build_revoke_command(data: &RevokeCommandData) -> String { fn test_revoke_command_builder() { let sudo = Some("sudo -u test".to_string()); let closure = "/nix/store/blah/etc"; - let profile_path = "/nix/var/nix/per-user/user/profile"; + let profile_info = ProfileInfo::ProfilePath { + profile_path: "/nix/var/nix/per-user/user/profile".to_string(), + }; let debug_logs = true; let log_dir = Some("/tmp/something.txt"); @@ -194,11 +227,11 @@ fn test_revoke_command_builder() { build_revoke_command(&RevokeCommandData { sudo: &sudo, closure, - profile_path, + profile_info, debug_logs, log_dir }), - "sudo -u test /nix/store/blah/etc/activate-rs --debug-logs --log-dir /tmp/something.txt revoke '/nix/var/nix/per-user/user/profile'" + "sudo -u test /nix/store/blah/etc/activate-rs --debug-logs --log-dir /tmp/something.txt revoke --profile-path '/nix/var/nix/per-user/user/profile'" .to_string(), ); } @@ -271,6 +304,8 @@ pub enum DeployProfileError { #[error("Error confirming deployment: {0}")] Confirm(#[from] ConfirmProfileError), + #[error("Deployment data invalid: {0}")] + InvalidDeployDataDefs(#[from] DeployDataDefsError), } pub async fn deploy_profile( @@ -299,7 +334,7 @@ pub async fn deploy_profile( let self_activate_command = build_activate_command(&ActivateCommandData { sudo: &deploy_defs.sudo, - profile_path: &deploy_defs.profile_path, + profile_info: &deploy_data.get_profile_info()?, closure: &deploy_data.profile.profile_settings.path, auto_rollback, temp_path: temp_path, @@ -439,7 +474,7 @@ pub async fn revoke( let self_revoke_command = build_revoke_command(&RevokeCommandData { sudo: &deploy_defs.sudo, closure: &deploy_data.profile.profile_settings.path, - profile_path: &deploy_data.get_profile_path()?, + profile_info: deploy_data.get_profile_info()?, debug_logs: deploy_data.debug_logs, log_dir: deploy_data.log_dir, }); diff --git a/src/lib.rs b/src/lib.rs index c6f8e030..0e5d817b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -332,9 +332,17 @@ pub struct DeployData<'a> { pub struct DeployDefs { pub ssh_user: String, pub profile_user: String, - pub profile_path: String, pub sudo: Option, } +enum ProfileInfo { + ProfilePath { + profile_path: String, + }, + ProfileUserAndName { + profile_user: String, + profile_name: String, + }, +} #[derive(Error, Debug)] pub enum DeployDataDefsError { @@ -351,8 +359,6 @@ impl<'a> DeployData<'a> { let profile_user = self.get_profile_user()?; - let profile_path = self.get_profile_path()?; - let sudo: Option = match self.merged_settings.user { Some(ref user) if user != &ssh_user => Some(format!("{} {}", self.get_sudo(), user)), _ => None, @@ -361,26 +367,10 @@ impl<'a> DeployData<'a> { Ok(DeployDefs { ssh_user, profile_user, - profile_path, sudo, }) } - fn get_profile_path(&'a self) -> Result { - let profile_user = self.get_profile_user()?; - let profile_path = match self.profile.profile_settings.profile_path { - None => match &profile_user[..] { - "root" => format!("/nix/var/nix/profiles/{}", self.profile_name), - _ => format!( - "/nix/var/nix/profiles/per-user/{}/{}", - profile_user, self.profile_name - ), - }, - Some(ref x) => x.clone(), - }; - Ok(profile_path) - } - fn get_profile_user(&'a self) -> Result { let profile_user = match self.merged_settings.user { Some(ref x) => x.clone(), @@ -403,6 +393,16 @@ impl<'a> DeployData<'a> { None => "sudo -u".to_string(), } } + + fn get_profile_info(&'a self) -> Result { + match self.profile.profile_settings.profile_path { + Some(ref profile_path) => Ok(ProfileInfo::ProfilePath { profile_path: profile_path.to_string() }), + None => { + let profile_user = self.get_profile_user()?; + Ok(ProfileInfo::ProfileUserAndName { profile_user, profile_name: self.profile_name.to_string() }) + }, + } + } } pub fn make_deploy_data<'a, 's>( From b5625de9c918d4ea318627fc1a18943ac8c1e1b1 Mon Sep 17 00:00:00 2001 From: Simon Menke Date: Sun, 17 Sep 2023 10:34:25 +0200 Subject: [PATCH 3/4] Replace jsonschema-cli with check-jsonschema jsonschema-cli is deprecated and will be removed in the future. The recommended replacement is check-jsonschema. --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index edfb6bd7..66b2e764 100644 --- a/flake.nix +++ b/flake.nix @@ -118,7 +118,7 @@ deployChecks = deploy: builtins.mapAttrs (_: check: check deploy) { deploy-schema = deploy: final.runCommand "jsonschema-deploy-system" { } '' - ${final.python3.pkgs.jsonschema}/bin/jsonschema -i ${final.writeText "deploy.json" (builtins.toJSON deploy)} ${./interface.json} && touch $out + ${final.check-jsonschema}/bin/check-jsonschema --schemafile ${./interface.json} ${final.writeText "deploy.json" (builtins.toJSON deploy)} && touch $out ''; deploy-activate = deploy: From 5617d39d3a685539372260a8c38ee68a0abbc203 Mon Sep 17 00:00:00 2001 From: Roman Melnikov Date: Mon, 18 Sep 2023 12:40:24 +0200 Subject: [PATCH 4/4] [Chore] Run CI checks on 'pull_request' Problem: We want to be able to run CI checks on PRs from external forks. However, this is only possible with 'on: pull_request', while currently CI is triggered 'on: push' Solution: Change CI triggering condition to 'on: pull_request'. --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 0a0642f9..68124a55 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,5 +1,5 @@ name: Nix flake check -on: push +on: pull_request jobs: check: