From b440066ebb310fed22bf11a881b88e84236833f9 Mon Sep 17 00:00:00 2001 From: Salama Ashoush Date: Tue, 10 Sep 2024 21:12:37 +0200 Subject: [PATCH] :ambulance: fix broken macos installation --- .changeset/rich-goats-own.md | 5 ++ README.md | 14 ++++ src/commands/install.rs | 13 +++- src/config.rs | 6 +- src/downloader.rs | 13 ++-- src/remote_pact_index.rs | 27 +++---- src/system_info.rs | 141 ++++++++++++++++++++--------------- 7 files changed, 134 insertions(+), 85 deletions(-) create mode 100644 .changeset/rich-goats-own.md diff --git a/.changeset/rich-goats-own.md b/.changeset/rich-goats-own.md new file mode 100644 index 0000000..a5e5949 --- /dev/null +++ b/.changeset/rich-goats-own.md @@ -0,0 +1,5 @@ +--- +"pactup": patch +--- + +fix macos installation diff --git a/README.md b/README.md index 5b6b854..1dbfc65 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,20 @@ cargo test - The Pact binaries are problematic; they are not consistent in each release, and often, releases are missing binaries. For example, the latest release, 4.12, does not have any Mac binaries on GitHub. Expect some issues with this. - Some older versions might require older system libs (eg. libncurses5). +## Troubleshooting + +**Error: "Can't download the requested binary: Permission denied (os error 13)"** + +This error occurs when installing the `development-latest` nightly version, and then attempting to force install or remove it. The issue stems from permission problems in older versions of `pactup`. + +To resolve this, update to the latest `pactup` version (>=0.2.18), and run: + +``` +sudo pactup uninstall development-latest +``` + +After this, you should be able to run install/uninstall commands without using `sudo`. + ## Credit Pact version manager is ported from the amazing [fnm](https://github.com/Schniz/fnm) codebase. diff --git a/src/commands/install.rs b/src/commands/install.rs index 3e383f1..ec4294a 100644 --- a/src/commands/install.rs +++ b/src/commands/install.rs @@ -142,9 +142,15 @@ impl Command for Install { format!("Pact {}", &version).cyan(), config.arch.to_string() ); + let Some(download_url) = release.download_url() else { + return Err(Error::CantFindReleaseAsset { + requested_version: current_version, + }); + }; + match install_pact_dist( version, - &release.download_url(), + &download_url, config.installations_dir(), &config.arch, show_progress, @@ -207,6 +213,11 @@ pub enum Error { requested_version )] CantFindPactVersion { requested_version: UserVersion }, + #[error( + "Can't find a release asset for the requested version: {}", + requested_version + )] + CantFindReleaseAsset { requested_version: UserVersion }, #[error("Can't find nightly version named {}", nightly_tag)] CantFindNightly { nightly_tag: String }, #[error("Can't find any versions in the upstream version index.")] diff --git a/src/config.rs b/src/config.rs index b23af98..db73a52 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ use crate::log_level::LogLevel; use crate::path_ext::PathExt; use crate::version_file_strategy::VersionFileStrategy; -use crate::{directories::Directories, system_info::Arch}; +use crate::{directories::Directories, system_info::PlatformArch}; #[derive(clap::Parser, Debug)] pub struct PactupConfig { @@ -65,7 +65,7 @@ pub struct PactupConfig { hide_env_values = true, hide_default_value = true )] - pub arch: Arch, + pub arch: PlatformArch, /// A strategy for how to resolve the Pact version. Used whenever `pactup use` or `pactup install` is /// called without a version, or when `--use-on-cd` is configured on evaluation. @@ -102,7 +102,7 @@ impl Default for PactupConfig { base_dir: None, multishell_path: None, log_level: LogLevel::Info, - arch: Arch::default(), + arch: PlatformArch::default(), version_file_strategy: VersionFileStrategy::default(), directories: Directories::default(), resolve_engines: false, diff --git a/src/downloader.rs b/src/downloader.rs index bff8ec2..643dfdb 100644 --- a/src/downloader.rs +++ b/src/downloader.rs @@ -3,7 +3,7 @@ use crate::archive::extract::ArchiveType; use crate::archive::{Error as ExtractError, Extract}; use crate::directory_portal::DirectoryPortal; use crate::progress::ResponseProgress; -use crate::system_info::Arch; +use crate::system_info::PlatformArch; use crate::version::Version; use indicatif::ProgressDrawTarget; use log::debug; @@ -33,7 +33,10 @@ pub enum Error { // #[error("The downloaded archive is empty")] // TarIsEmpty, #[error("{} for {} not found upstream.\nYou can `pactup ls-remote` to see available versions or try a different `--arch`.", version, arch)] - VersionNotFound { version: Version, arch: Arch }, + VersionNotFound { + version: Version, + arch: PlatformArch, + }, #[error("Version already installed at {:?}", path)] VersionAlreadyInstalled { path: PathBuf }, } @@ -55,7 +58,7 @@ pub fn install_pact_dist>( version: &Version, download_url: &Url, installations_dir: P, - arch: &Arch, + arch: &PlatformArch, show_progress: bool, force: bool, ) -> Result<(), Error> { @@ -149,9 +152,9 @@ mod tests { fn install_in(path: &Path) -> PathBuf { let version = Version::parse("4.11.0").unwrap(); #[cfg(target_arch = "x86_64")] - let arch = Arch::X64; + let arch = PlatformArch::X64; #[cfg(target_arch = "aarch64")] - let arch = Arch::Arm64; + let arch = PlatformArch::Arm64; // github release asset url let pact_dist_mirror = Url::parse( "https://github.com/kadena-io/pact/releases/download/v4.11.0/pact-4.11.0-linux-20.04.zip", diff --git a/src/remote_pact_index.rs b/src/remote_pact_index.rs index 452b001..0a0df29 100644 --- a/src/remote_pact_index.rs +++ b/src/remote_pact_index.rs @@ -1,5 +1,5 @@ use crate::{ - system_info::{get_platform, Arch, Platform}, + system_info::{get_platform, Platform, PlatformArch, PlatformOS}, version::Version, }; use chrono::{DateTime, Utc}; @@ -35,10 +35,7 @@ impl Release { let version = &self.tag_name; let platform = get_platform(); match platform { - Platform { - os: "linux", - arch: Arch::X64, - } => { + Platform(PlatformOS::Linux, PlatformArch::X64) => { let regex = if version.is_nightly() { // match the nightly version format for linux pact-binary-bundle.ubuntu-*. r"pact-binary-bundle\.(ubuntu-latest)\.(tar\.gz|zip)$" @@ -48,10 +45,7 @@ impl Release { }; Regex::new(regex).map_err(|e| format!("Regex creation error: {e}")) } - Platform { - os: "macos", - arch: Arch::X64, - } => { + Platform(PlatformOS::MacOS, PlatformArch::X64) => { let regex = if version.is_nightly() { // match the nightly version format for mac pact-binary-bundle.macos-latest. r"pact-binary-bundle\.macos-latest\.(tar\.gz|zip)$" @@ -61,10 +55,7 @@ impl Release { }; Regex::new(regex).map_err(|e| format!("Regex creation error: {e}")) } - Platform { - os: "macos", - arch: Arch::Arm64, - } => { + Platform(PlatformOS::MacOS, PlatformArch::Arm64) => { let regex = if version.is_nightly() { // match the nightly version format for mac pact-binary-bundle.macos-m1. r"pact-binary-bundle\.macos-m1\.(tar\.gz|zip)$" @@ -86,12 +77,16 @@ impl Release { /// Checks if the release has a supported asset for the current platform. pub fn has_supported_asset(&self) -> bool { + println!( + "Checking if release has supported asset {:?}", + self.asset_for_current_platform() + ); self.asset_for_current_platform().is_some() } - pub fn download_url(&self) -> Url { - let asset = self.asset_for_current_platform().expect("Can't find asset"); - asset.browser_download_url.clone() + pub fn download_url(&self) -> Option { + let asset = self.asset_for_current_platform(); + asset.map(|asset| asset.browser_download_url.clone()) } } diff --git a/src/system_info.rs b/src/system_info.rs index 991b931..78f9361 100644 --- a/src/system_info.rs +++ b/src/system_info.rs @@ -1,52 +1,87 @@ #[cfg(target_os = "windows")] -pub fn platform_os() -> &'static str { - "windows" +pub fn platform_os() -> PlatformOS { + OS::Windows } #[cfg(target_os = "macos")] -pub fn platform_os() -> &'static str { - "darwin" +pub fn platform_os() -> PlatformOS { + PlatformOS::MacOS } #[cfg(target_os = "linux")] -pub fn platform_os() -> &'static str { - "linux" +pub fn platform_os() -> PlatformOS { + PlatformOS::Linux +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum PlatformOS { + Windows, + MacOS, + Linux, +} + +impl std::fmt::Display for PlatformOS { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PlatformOS::Windows => write!(f, "windows"), + PlatformOS::MacOS => write!(f, "macos"), + PlatformOS::Linux => write!(f, "linux"), + } + } +} + +impl Default for PlatformOS { + fn default() -> PlatformOS { + platform_os() + } +} + +impl std::str::FromStr for PlatformOS { + type Err = PlatformError; + fn from_str(s: &str) -> Result { + match s { + "windows" => Ok(PlatformOS::Windows), + "macos" => Ok(PlatformOS::MacOS), + "linux" => Ok(PlatformOS::Linux), + unknown => Err(PlatformError::new(&format!("Unknown OS: {unknown}"))), + } + } } #[cfg(all( target_pointer_width = "32", any(target_arch = "arm", target_arch = "aarch64") ))] -pub fn platform_arch() -> &'static str { - "armv7l" +pub fn platform_arch() -> PlatformArch { + PlatformArch::Armv7l } #[cfg(all( target_pointer_width = "32", not(any(target_arch = "arm", target_arch = "aarch64")) ))] -pub fn platform_arch() -> &'static str { - "x86" +pub fn platform_arch() -> PlatformArch { + PlatformArch::X86 } #[cfg(all( target_pointer_width = "64", any(target_arch = "arm", target_arch = "aarch64") ))] -pub fn platform_arch() -> &'static str { - "arm64" +pub fn platform_arch() -> PlatformArch { + PlatformArch::Arm64 } #[cfg(all( target_pointer_width = "64", not(any(target_arch = "arm", target_arch = "aarch64")) ))] -pub fn platform_arch() -> &'static str { - "x64" +pub fn platform_arch() -> PlatformArch { + PlatformArch::X64 } #[derive(Clone, PartialEq, Eq, Debug)] -pub enum Arch { +pub enum PlatformArch { X86, X64, Arm64, @@ -56,41 +91,38 @@ pub enum Arch { S390x, } -impl Default for Arch { - fn default() -> Arch { - match platform_arch().parse() { - Ok(arch) => arch, - Err(e) => panic!("{}", e.details), - } +impl Default for PlatformArch { + fn default() -> PlatformArch { + platform_arch() } } -impl std::str::FromStr for Arch { - type Err = ArchError; - fn from_str(s: &str) -> Result { +impl std::str::FromStr for PlatformArch { + type Err = PlatformError; + fn from_str(s: &str) -> Result { match s { - "x86" => Ok(Arch::X86), - "x64" => Ok(Arch::X64), - "arm64" => Ok(Arch::Arm64), - "armv7l" => Ok(Arch::Armv7l), - "ppc64le" => Ok(Arch::Ppc64le), - "ppc64" => Ok(Arch::Ppc64), - "s390x" => Ok(Arch::S390x), - unknown => Err(ArchError::new(&format!("Unknown Arch: {unknown}"))), + "x86" => Ok(PlatformArch::X86), + "x64" => Ok(PlatformArch::X64), + "arm64" => Ok(PlatformArch::Arm64), + "armv7l" => Ok(PlatformArch::Armv7l), + "ppc64le" => Ok(PlatformArch::Ppc64le), + "ppc64" => Ok(PlatformArch::Ppc64), + "s390x" => Ok(PlatformArch::S390x), + unknown => Err(PlatformError::new(&format!("Unknown Arch: {unknown}"))), } } } -impl std::fmt::Display for Arch { +impl std::fmt::Display for PlatformArch { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let arch_str = match self { - Arch::X86 => String::from("x86"), - Arch::X64 => String::from("x64"), - Arch::Arm64 => String::from("arm64"), - Arch::Armv7l => String::from("armv7l"), - Arch::Ppc64le => String::from("ppc64le"), - Arch::Ppc64 => String::from("ppc64"), - Arch::S390x => String::from("s390x"), + PlatformArch::X86 => String::from("x86"), + PlatformArch::X64 => String::from("x64"), + PlatformArch::Arm64 => String::from("arm64"), + PlatformArch::Armv7l => String::from("armv7l"), + PlatformArch::Ppc64le => String::from("ppc64le"), + PlatformArch::Ppc64 => String::from("ppc64"), + PlatformArch::S390x => String::from("s390x"), }; write!(f, "{arch_str}") @@ -98,47 +130,36 @@ impl std::fmt::Display for Arch { } #[derive(Debug)] -pub struct ArchError { +pub struct PlatformError { details: String, } -impl ArchError { - fn new(msg: &str) -> ArchError { - ArchError { +impl PlatformError { + fn new(msg: &str) -> PlatformError { + PlatformError { details: msg.to_string(), } } } -impl std::fmt::Display for ArchError { +impl std::fmt::Display for PlatformError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.details) } } -impl std::error::Error for ArchError { +impl std::error::Error for PlatformError { fn description(&self) -> &str { &self.details } } -pub struct Platform { - pub os: &'static str, - pub arch: Arch, -} - -impl Default for Platform { - fn default() -> Platform { - Platform { - os: platform_os(), - arch: Arch::default(), - } - } -} +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct Platform(pub PlatformOS, pub PlatformArch); impl std::fmt::Display for Platform { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{name}-{arch}", name = self.os, arch = self.arch) + write!(f, "{os}-{arch}", os = self.0, arch = self.1) } }