Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(poetry): skip if not installed with official script #989

Merged
merged 3 commits into from
Dec 7, 2024
Merged
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 config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@
# enable_pipupgrade = true ###disabled by default
# pipupgrade_arguments = "-y -u --pip-path pip" ###disabled by default

# For the poetry step, by default, Topgrade skips its update if poetry is not
# installed with the official script. This configuration entry forces Topgrade
# to run the update in this case.
#
# (default: false)
# poetry_force_self_update = true


[composer]
# self_update = true
Expand Down
8 changes: 8 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ pub struct Python {
enable_pip_review_local: Option<bool>,
enable_pipupgrade: Option<bool>,
pipupgrade_arguments: Option<String>,
poetry_force_self_update: Option<bool>,
}

#[derive(Deserialize, Default, Debug, Merge)]
Expand Down Expand Up @@ -1627,6 +1628,13 @@ impl Config {
.and_then(|python| python.enable_pip_review_local)
.unwrap_or(false)
}
pub fn poetry_force_self_update(&self) -> bool {
self.config_file
.python
.as_ref()
.and_then(|python| python.poetry_force_self_update)
.unwrap_or(false)
}

pub fn display_time(&self) -> bool {
self.config_file
Expand Down
83 changes: 82 additions & 1 deletion src/steps/generic.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![allow(unused_imports)]

use std::ffi::OsStr;
use std::path::PathBuf;
use std::process::Command;
use std::{env, path::Path};
Expand Down Expand Up @@ -1029,8 +1030,88 @@ pub fn run_lensfun_update_data(ctx: &ExecutionContext) -> Result<()> {

pub fn run_poetry(ctx: &ExecutionContext) -> Result<()> {
let poetry = require("poetry")?;

#[cfg(unix)]
fn get_interpreter(poetry: &PathBuf) -> Result<PathBuf> {
use std::os::unix::ffi::OsStrExt;

let script = fs::read(poetry)?;
if let Some(r) = script.iter().position(|&b| b == b'\n') {
let first_line = &script[..r];
if first_line.starts_with(b"#!") {
return Ok(OsStr::from_bytes(&first_line[2..]).into());
}
}

Err(eyre!("Could not find shebang"))
}
#[cfg(windows)]
fn get_interpreter(poetry: &PathBuf) -> Result<PathBuf> {
let data = fs::read(poetry)?;

// https://bitbucket.org/vinay.sajip/simple_launcher/src/master/compare_launchers.py

let pos = match data.windows(4).rposition(|b| b == b"PK\x05\x06") {
Some(i) => i,
None => return Err(eyre!("Not a ZIP archive")),
};

let cdr_size = match data.get(pos + 12..pos + 16) {
Some(b) => u32::from_le_bytes(b.try_into().unwrap()) as usize,
None => return Err(eyre!("Invalid CDR size")),
};
let cdr_offset = match data.get(pos + 16..pos + 20) {
Some(b) => u32::from_le_bytes(b.try_into().unwrap()) as usize,
None => return Err(eyre!("Invalid CDR offset")),
};
if pos < cdr_size + cdr_offset {
return Err(eyre!("Invalid ZIP archive"));
}
let arc_pos = pos - cdr_size - cdr_offset;
let shebang = match data[..arc_pos].windows(2).rposition(|b| b == b"#!") {
Some(l) => &data[l + 2..arc_pos - 1],
None => return Err(eyre!("Could not find shebang")),
};

// shebang line is utf8
Ok(std::str::from_utf8(shebang)?.into())
}

if ctx.config().poetry_force_self_update() {
debug!("forcing poetry self update");
} else {
let interpreter = match get_interpreter(&poetry) {
Ok(p) => p,
Err(e) => {
return Err(SkipStep(format!("Could not find interpreter for {}: {}", poetry.display(), e)).into())
}
};
debug!("poetry interpreter: {}", interpreter.display());

let check_official_install_script =
"import sys; from os import path; print('Y') if path.isfile(path.join(sys.prefix, 'poetry_env')) else print('N')";
let output = Command::new(&interpreter)
.args(["-c", check_official_install_script])
.output_checked_utf8()?;
let stdout = output.stdout.trim();
let official_install = match stdout {
"N" => false,
"Y" => true,
_ => unreachable!("unexpected output from `check_official_install_script`"),
};

debug!("poetry is official install: {}", official_install);

if !official_install {
return Err(SkipStep("Not installed with the official script".to_string()).into());
}
}

print_separator("Poetry");
ctx.run_type().execute(poetry).args(["self", "update"]).status_checked()
ctx.run_type()
.execute(&poetry)
.args(["self", "update"])
.status_checked()
}

pub fn run_uv(ctx: &ExecutionContext) -> Result<()> {
Expand Down
Loading