mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Launcher tweaks
- Handle beta/rc tags in .version when launching Anki - Update pyproject.toml/.python_version if distributed version newer - Support prerelease marker to opt in to betas - Check for updates when using uv sync - Avoid system Python by default, as it can cause breakages (e.g. ARM Python installed on Windows)
This commit is contained in:
parent
4abc0eb8b8
commit
cd71931506
6 changed files with 67 additions and 38 deletions
|
@ -309,12 +309,17 @@ def int_version() -> int:
|
|||
"""Anki's version as an integer in the form YYMMPP, e.g. 230900.
|
||||
(year, month, patch).
|
||||
In 2.1.x releases, this was just the last number."""
|
||||
import re
|
||||
|
||||
from anki.buildinfo import version
|
||||
|
||||
# Strip non-numeric characters (handles beta/rc suffixes like '25.02b1' or 'rc3')
|
||||
numeric_version = re.sub(r"[^0-9.]", "", version)
|
||||
|
||||
try:
|
||||
[year, month, patch] = version.split(".")
|
||||
[year, month, patch] = numeric_version.split(".")
|
||||
except ValueError:
|
||||
[year, month] = version.split(".")
|
||||
[year, month] = numeric_version.split(".")
|
||||
patch = "0"
|
||||
|
||||
year_num = int(year)
|
||||
|
|
|
@ -8,6 +8,7 @@ APP_LAUNCHER="$OUTPUT_DIR/Anki.app"
|
|||
rm -rf "$APP_LAUNCHER"
|
||||
|
||||
# Build binaries for both architectures
|
||||
rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
||||
cargo build -p launcher --release --target aarch64-apple-darwin
|
||||
cargo build -p launcher --release --target x86_64-apple-darwin
|
||||
(cd ../../.. && ./ninja launcher:uv_universal)
|
||||
|
|
|
@ -5,18 +5,4 @@ description = "UV-based launcher for Anki."
|
|||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"anki-release",
|
||||
# so we can use testpypi
|
||||
"anki",
|
||||
"aqt",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
anki-release = { index = "testpypi" }
|
||||
anki = { index = "testpypi" }
|
||||
aqt = { index = "testpypi" }
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "testpypi"
|
||||
url = "https://test.pypi.org/simple/"
|
||||
publish-url = "https://test.pypi.org/legacy/"
|
||||
explicit = true
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
use std::io::stdin;
|
||||
use std::process::Command;
|
||||
|
||||
use anki_io::copy_file;
|
||||
use anki_io::copy_if_newer;
|
||||
use anki_io::create_dir_all;
|
||||
use anki_io::metadata;
|
||||
use anki_io::modified_time;
|
||||
use anki_io::remove_file;
|
||||
use anki_io::write_file;
|
||||
use anki_process::CommandExt;
|
||||
|
@ -51,6 +51,7 @@ fn run() -> Result<()> {
|
|||
.join("AnkiProgramFiles");
|
||||
|
||||
let sync_complete_marker = uv_install_root.join(".sync_complete");
|
||||
let prerelease_marker = uv_install_root.join("prerelease");
|
||||
let (exe_dir, resources_dir) = get_exe_and_resources_dirs()?;
|
||||
let dist_pyproject_path = resources_dir.join("pyproject.toml");
|
||||
let user_pyproject_path = uv_install_root.join("pyproject.toml");
|
||||
|
@ -59,14 +60,15 @@ fn run() -> Result<()> {
|
|||
let uv_lock_path = uv_install_root.join("uv.lock");
|
||||
let uv_path: std::path::PathBuf = exe_dir.join(get_uv_binary_name());
|
||||
|
||||
// Create install directory and copy project files in
|
||||
create_dir_all(&uv_install_root)?;
|
||||
copy_if_newer(&dist_pyproject_path, &user_pyproject_path)?;
|
||||
copy_if_newer(&dist_python_version_path, &user_python_version_path)?;
|
||||
|
||||
let pyproject_has_changed =
|
||||
!user_pyproject_path.exists() || !sync_complete_marker.exists() || {
|
||||
let pyproject_toml_time = metadata(&user_pyproject_path)?
|
||||
.modified()
|
||||
.context("Failed to get pyproject.toml modified time")?;
|
||||
let sync_complete_time = metadata(&sync_complete_marker)?
|
||||
.modified()
|
||||
.context("Failed to get sync marker modified time")?;
|
||||
let pyproject_toml_time = modified_time(&user_pyproject_path)?;
|
||||
let sync_complete_time = modified_time(&sync_complete_marker)?;
|
||||
Ok::<bool, anyhow::Error>(pyproject_toml_time > sync_complete_time)
|
||||
}
|
||||
.unwrap_or(true);
|
||||
|
@ -81,22 +83,21 @@ fn run() -> Result<()> {
|
|||
// we'll need to launch uv; reinvoke ourselves in a terminal so the user can see
|
||||
handle_terminal_launch()?;
|
||||
|
||||
// Create install directory and copy project files in
|
||||
create_dir_all(&uv_install_root)?;
|
||||
if !user_pyproject_path.exists() {
|
||||
copy_file(&dist_pyproject_path, &user_pyproject_path)?;
|
||||
copy_file(&dist_python_version_path, &user_python_version_path)?;
|
||||
}
|
||||
|
||||
// Remove sync marker before attempting sync
|
||||
let _ = remove_file(&sync_complete_marker);
|
||||
|
||||
// Sync the venv
|
||||
if let Err(e) = Command::new(&uv_path)
|
||||
let mut command = Command::new(&uv_path);
|
||||
command
|
||||
.current_dir(&uv_install_root)
|
||||
.args(["sync", "--refresh"])
|
||||
.ensure_success()
|
||||
{
|
||||
.args(["sync", "--upgrade", "--managed-python"]);
|
||||
|
||||
// Set UV_PRERELEASE=allow if prerelease file exists
|
||||
if prerelease_marker.exists() {
|
||||
command.env("UV_PRERELEASE", "allow");
|
||||
}
|
||||
|
||||
if let Err(e) = command.ensure_success() {
|
||||
// If sync fails due to things like a missing wheel on pypi,
|
||||
// we need to remove the lockfile or uv will cache the bad result.
|
||||
let _ = remove_file(&uv_lock_path);
|
||||
|
|
|
@ -152,6 +152,34 @@ pub fn copy_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<u64> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Copy a file from src to dst if dst doesn't exist or if src is newer than
|
||||
/// dst. Preserves the modification time from the source file.
|
||||
pub fn copy_if_newer(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<bool> {
|
||||
let src = src.as_ref();
|
||||
let dst = dst.as_ref();
|
||||
|
||||
let should_copy = if !dst.exists() {
|
||||
true
|
||||
} else {
|
||||
let src_time = modified_time(src)?;
|
||||
let dst_time = modified_time(dst)?;
|
||||
src_time > dst_time
|
||||
};
|
||||
|
||||
if should_copy {
|
||||
copy_file(src, dst)?;
|
||||
|
||||
// Preserve the modification time from the source file
|
||||
let src_mtime = modified_time(src)?;
|
||||
let times = FileTimes::new().set_modified(src_mtime);
|
||||
set_file_times(dst, times)?;
|
||||
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [read_file], but skips the section that is potentially locked by
|
||||
/// SQLite.
|
||||
pub fn read_locked_db_file(path: impl AsRef<Path>) -> Result<Vec<u8>> {
|
||||
|
@ -188,6 +216,14 @@ pub fn metadata(path: impl AsRef<Path>) -> Result<std::fs::Metadata> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Get the modification time of a file.
|
||||
pub fn modified_time(path: impl AsRef<Path>) -> Result<std::time::SystemTime> {
|
||||
metadata(&path)?.modified().context(FileIoSnafu {
|
||||
path: path.as_ref(),
|
||||
op: FileOp::Metadata,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_tempfile() -> Result<NamedTempFile> {
|
||||
NamedTempFile::new().context(FileIoSnafu {
|
||||
path: std::env::temp_dir(),
|
||||
|
|
Loading…
Reference in a new issue