Skip to content

Commit

Permalink
Add basic Python release resolution to huak_python_manager
Browse files Browse the repository at this point in the history
  • Loading branch information
cnpryer committed Oct 20, 2023
1 parent 3102a34 commit ccf4599
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 42 deletions.
53 changes: 45 additions & 8 deletions crates/huak_python_manager/scripts/generate_python_releases.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,14 @@ def get_checksum(url: str) -> str | None:
has_checksum.add(asset["browser_download_url"].removesuffix(".sha256"))


module = f"""\
//! This file was generated with `{FILE.name}`.
module = (
f"""//! This file was generated with `{FILE.name}`."""
"""\n\nuse std::{cmp::Ordering, fmt::Display};
#[allow(dead_code)]
#[rustfmt::skip]
pub const RELEASES: &[Release] = &[\
""" # noqa
pub(crate) const RELEASES: &[Release] = &["""
)
for release in release_json:
for asset in release["assets"]:
# Avoid making requests for releases we've already generated.
Expand Down Expand Up @@ -126,8 +127,8 @@ def get_checksum(url: str) -> str | None:
module += "\n\t" + release.to_rust_string() + ","
module += """\n];
#[derive(Copy, Clone)]
pub struct Release<'a> {
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(crate) struct Release<'a> {
pub kind: &'a str,
pub version: Version,
pub os: &'a str,
Expand Down Expand Up @@ -160,8 +161,8 @@ def get_checksum(url: str) -> str | None:
}
}
#[derive(Copy, Clone)]
pub struct Version {
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(crate) struct Version {
pub major: u8,
pub minor: u8,
pub patch: u8,
Expand All @@ -177,6 +178,42 @@ def get_checksum(url: str) -> str | None:
}
}
}
impl Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}
impl PartialOrd<Self> for Version {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Version {
fn cmp(&self, other: &Self) -> Ordering {
match compare_version(*self, *other) {
Ordering::Less => Ordering::Less,
Ordering::Equal => Ordering::Equal,
Ordering::Greater => Ordering::Greater,
}
}
}
fn compare_version(this: Version, other: Version) -> Ordering {
for (a, b) in [
(this.major, other.major),
(this.minor, other.minor),
(this.patch, other.patch),
] {
if a != b {
return a.cmp(&b);
}
}
Ordering::Equal
}
"""

path = ROOT / "crates" / CRATE / "src" / "releases.rs"
Expand Down
16 changes: 9 additions & 7 deletions crates/huak_python_manager/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub(crate) struct Cli {
impl Cli {
pub(crate) fn run(self) -> Result<(), Error> {
match self.command {
Commands::Install { version } => cmd::install(&version),
Commands::Install { version } => cmd::install(version),
}
}
}
Expand All @@ -34,12 +34,14 @@ enum Commands {
}

mod cmd {
use super::Error;
use super::RequestedVersion;
use crate::install;
use crate::resolve::Strategy;
use super::{Error, RequestedVersion};
use crate::install::install_to_home;
use crate::resolve::{Options, Strategy};

pub(crate) fn install(version: &RequestedVersion) -> Result<(), Error> {
install::install_to_home(version, &Strategy::Auto)
pub(crate) fn install(version: RequestedVersion) -> Result<(), Error> {
install_to_home(&Strategy::Selection(Options {
version: Some(version),
..Default::default()
}))
}
}
29 changes: 18 additions & 11 deletions crates/huak_python_manager/src/install.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
resolve::{get_release, Strategy},
version::RequestedVersion,
releases::Release,
resolve::{resolve_release, Strategy},
};
use anyhow::{bail, Context, Error, Ok}; // TODO(cnpryer): Use thiserror in library code.
use huak_home::huak_home_dir;
Expand All @@ -9,19 +9,17 @@ use tar::Archive;
use tempfile::TempDir;
use zstd::decode_all;

pub(crate) fn install_to_home(
version: &RequestedVersion,
strategy: &Strategy,
) -> Result<(), Error> {
let release = get_release(version, strategy).context("requested release data")?;
/// Install a Python release to `~/.huak/bin/`.
pub(crate) fn install_to_home(strategy: &Strategy) -> Result<(), Error> {
let release = resolve_release(strategy).context("requested release data")?;
let tmp_dir = TempDir::new()?;
let tmp_name = "tmp.tar.zst";
let tmp_path = tmp_dir.path().join(tmp_name);
let target_dir = huak_home_dir()
.context("requested huak's home directory")?
.join("bin");

download_file(release.url, &tmp_path)?;
download_release(&release, &tmp_path)?;

let mut archive = File::open(tmp_path)?;
let decoded = decode_all(&mut archive)?;
Expand All @@ -30,15 +28,24 @@ pub(crate) fn install_to_home(
Ok(archive.unpack(target_dir)?)
}

fn download_file(url: &str, to: &PathBuf) -> Result<(), Error> {
let mut response = reqwest::blocking::get(url)?;
/// Download the release to a temporary archive file (`to`).
fn download_release(release: &Release, to: &PathBuf) -> Result<(), Error> {
validate_release(release)?;

let mut response = reqwest::blocking::get(release.url)?;

if !response.status().is_success() {
bail!("failed to download file from {url}");
bail!("failed to download file from {}", release.url);
}

let mut file = File::create(to)?;
response.copy_to(&mut file)?;

Ok(())
}

/// Validation for release installation. The following is verified prior to installation:
/// - checksum
fn validate_release(_release: &Release) -> Result<(), Error> {
todo!()
}
49 changes: 44 additions & 5 deletions crates/huak_python_manager/src/releases.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
//! This file was generated with `generate_python_releases.py`.
use std::{cmp::Ordering, fmt::Display};

#[allow(dead_code)]
#[rustfmt::skip]
pub const RELEASES: &[Release] = &[
// TODO(cnpryer): Perf
pub(crate) const RELEASES: &[Release] = &[
Release::new("cpython", Version::new(3, 10, 13), "apple", "aarch64", "pgo+lto", "a2635841454295c5bc2c18740346fd8308f2a8adcce2407b87c9faf261fed29c", "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.10.13%2B20231002-aarch64-apple-darwin-pgo%2Blto-full.tar.zst"),
Release::new("cpython", Version::new(3, 10, 13), "apple", "aarch64", "pgo", "67b64174b8d33aa1b2e3bb3a4a3e475ff96d511c540f46e3c0774f8b77be4d91", "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.10.13%2B20231002-aarch64-apple-darwin-pgo-full.tar.zst"),
Release::new("cpython", Version::new(3, 10, 13), "windows", "i686", "pgo", "1c015e64732d3a18951fcea30d364c80fb83322363fec1a2c85c70840fb75a92", "https://github.com/indygreg/python-build-standalone/releases/download/20231002/cpython-3.10.13%2B20231002-i686-pc-windows-msvc-shared-pgo-full.tar.zst"),
Expand Down Expand Up @@ -475,8 +478,8 @@ pub const RELEASES: &[Release] = &[
Release::new("cpython", Version::new(3, 9, 10), "linux", "x86_64", "pgo", "d23017bc20b640615af8f5eab0f1bf0c9264526bcb8c2a326f4a13b21725cff1", "https://github.com/indygreg/python-build-standalone/releases/download/20220227/cpython-3.9.10%2B20220227-x86_64-unknown-linux-gnu-pgo-full.tar.zst"),
];

#[derive(Copy, Clone)]
pub struct Release<'a> {
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(crate) struct Release<'a> {
pub kind: &'a str,
pub version: Version,
pub os: &'a str,
Expand Down Expand Up @@ -509,8 +512,8 @@ impl Release<'static> {
}
}

#[derive(Copy, Clone)]
pub struct Version {
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(crate) struct Version {
pub major: u8,
pub minor: u8,
pub patch: u8,
Expand All @@ -526,3 +529,39 @@ impl Version {
}
}
}

impl Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}

impl PartialOrd<Self> for Version {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl Ord for Version {
fn cmp(&self, other: &Self) -> Ordering {
match compare_version(*self, *other) {
Ordering::Less => Ordering::Less,
Ordering::Equal => Ordering::Equal,
Ordering::Greater => Ordering::Greater,
}
}
}

fn compare_version(this: Version, other: Version) -> Ordering {
for (a, b) in [
(this.major, other.major),
(this.minor, other.minor),
(this.patch, other.patch),
] {
if a != b {
return a.cmp(&b);
}
}

Ordering::Equal
}
Loading

0 comments on commit ccf4599

Please sign in to comment.