chore: satisfy CI fmt/clippy gates; make the release publish step idempotent
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
`cargo fmt --all -- --check` and `cargo clippy --all-targets -- -D warnings` are
CI gates and both were failing on this release's code.
- rustfmt across the files touched by the release
- `.split('/').last()` walks the whole iterator on a DoubleEndedIterator; use
`.next_back()` (4 sites in the setup/featured + delete handlers)
- `set_progress` had two identical branches; the intent is "take the first
reading, then only move forward", which is one condition
release.yml published unconditionally on a version tag, so a version already on
crates.io (released by hand, or a re-run of the job) failed the workflow — a
version can never be re-uploaded. It now checks the registry and skips instead.
This commit is contained in:
+76
-18
@@ -206,10 +206,9 @@ impl JobStore {
|
||||
if let Some(j) = self.jobs.lock().unwrap().get_mut(id) {
|
||||
let f = frac.clamp(0.0, 1.0);
|
||||
let cur = j.progress.unwrap_or(0.0);
|
||||
// monotonic only — never go backwards (curl reprints, re-download)
|
||||
if f > cur + 0.001 {
|
||||
j.progress = Some(f);
|
||||
} else if j.progress.is_none() {
|
||||
// monotonic only — never go backwards (curl reprints, re-download),
|
||||
// but an unset bar takes whatever the first reading is
|
||||
if j.progress.is_none() || f > cur + 0.001 {
|
||||
j.progress = Some(f);
|
||||
}
|
||||
if !phase.is_empty() {
|
||||
@@ -306,7 +305,9 @@ impl JobStore {
|
||||
let key = format!("{}::{}", j.repo, j.variant.clone().unwrap_or_default());
|
||||
match map.get(&key) {
|
||||
Some(prev) if prev.started >= j.started => {}
|
||||
_ => { map.insert(key, j); }
|
||||
_ => {
|
||||
map.insert(key, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut v: Vec<Job> = map.into_values().collect();
|
||||
@@ -434,7 +435,11 @@ pub async fn hf_repo_variants(id: &str, token: Option<&str>) -> Vec<(String, u64
|
||||
let sz = item
|
||||
.get("size")
|
||||
.and_then(|v| v.as_u64())
|
||||
.or_else(|| item.get("lfs").and_then(|l| l.get("size")).and_then(|v| v.as_u64()))
|
||||
.or_else(|| {
|
||||
item.get("lfs")
|
||||
.and_then(|l| l.get("size"))
|
||||
.and_then(|v| v.as_u64())
|
||||
})
|
||||
.unwrap_or(0);
|
||||
if sz == 0 {
|
||||
continue;
|
||||
@@ -559,7 +564,8 @@ async fn download_cmf_repo(
|
||||
}
|
||||
|
||||
let mut cmf_files: Vec<(String, u64)> = Vec::new();
|
||||
let mut sharded_dirs: std::collections::HashMap<String, Vec<String>> = std::collections::HashMap::new();
|
||||
let mut sharded_dirs: std::collections::HashMap<String, Vec<String>> =
|
||||
std::collections::HashMap::new();
|
||||
let mut dir_sizes: std::collections::HashMap<String, u64> = std::collections::HashMap::new();
|
||||
if let Ok(resp) = req.send().await {
|
||||
if resp.status().is_success() {
|
||||
@@ -573,13 +579,20 @@ async fn download_cmf_repo(
|
||||
let sz = item
|
||||
.get("size")
|
||||
.and_then(|v| v.as_u64())
|
||||
.or_else(|| item.get("lfs").and_then(|l| l.get("size")).and_then(|v| v.as_u64()))
|
||||
.or_else(|| {
|
||||
item.get("lfs")
|
||||
.and_then(|l| l.get("size"))
|
||||
.and_then(|v| v.as_u64())
|
||||
})
|
||||
.unwrap_or(0);
|
||||
if path.to_ascii_lowercase().ends_with(".cmf") {
|
||||
cmf_files.push((path.to_string(), sz));
|
||||
} else if path.contains('/') && path.starts_with("parts-") {
|
||||
let top = path.split('/').next().unwrap_or(path).to_string();
|
||||
sharded_dirs.entry(top.clone()).or_default().push(path.to_string());
|
||||
sharded_dirs
|
||||
.entry(top.clone())
|
||||
.or_default()
|
||||
.push(path.to_string());
|
||||
*dir_sizes.entry(top).or_default() += sz;
|
||||
}
|
||||
}
|
||||
@@ -591,12 +604,19 @@ async fn download_cmf_repo(
|
||||
|
||||
// Decide what to download: single .cmf file vs sharded variant directory
|
||||
let is_sharded = !sharded_dirs.is_empty();
|
||||
let chosen_variant = variant.as_deref().map(|v| v.trim()).filter(|v| !v.is_empty());
|
||||
let chosen_variant = variant
|
||||
.as_deref()
|
||||
.map(|v| v.trim())
|
||||
.filter(|v| !v.is_empty());
|
||||
|
||||
if is_sharded {
|
||||
// pick variant: explicit > first alphabetically
|
||||
let target_dir = if let Some(v) = chosen_variant {
|
||||
if sharded_dirs.contains_key(v) { v.to_string() } else { sharded_dirs.keys().next().cloned().unwrap_or_default() }
|
||||
if sharded_dirs.contains_key(v) {
|
||||
v.to_string()
|
||||
} else {
|
||||
sharded_dirs.keys().next().cloned().unwrap_or_default()
|
||||
}
|
||||
} else {
|
||||
// prefer smallest? use first sorted
|
||||
let mut keys: Vec<String> = sharded_dirs.keys().cloned().collect();
|
||||
@@ -611,9 +631,23 @@ async fn download_cmf_repo(
|
||||
return;
|
||||
}
|
||||
let total_sz = dir_sizes.get(&target_dir).copied().unwrap_or(0);
|
||||
let q = if target_dir.contains("q2tp") { "Q2TP" } else if target_dir.contains("q4tp") { "Q4TP" } else { "READY_CMF" };
|
||||
let q = if target_dir.contains("q2tp") {
|
||||
"Q2TP"
|
||||
} else if target_dir.contains("q4tp") {
|
||||
"Q4TP"
|
||||
} else {
|
||||
"READY_CMF"
|
||||
};
|
||||
store.update_quant(&id, q.to_string());
|
||||
store.push_line(&id, format!("→ downloading {} ({} files, {}) from HuggingFace...", target_dir, files.len(), format_bytes(total_sz)));
|
||||
store.push_line(
|
||||
&id,
|
||||
format!(
|
||||
"→ downloading {} ({} files, {}) from HuggingFace...",
|
||||
target_dir,
|
||||
files.len(),
|
||||
format_bytes(total_sz)
|
||||
),
|
||||
);
|
||||
store.set_total(&id, total_sz);
|
||||
store.set_progress(&id, 0.001, "downloading".into());
|
||||
|
||||
@@ -635,7 +669,13 @@ async fn download_cmf_repo(
|
||||
let url = format!("https://huggingface.co/{repo}/resolve/main/{rel}");
|
||||
store.push_line(&id, format!(" [{}/{}] {filename}", idx + 1, files.len()));
|
||||
let mut cmd = tokio::process::Command::new("curl");
|
||||
cmd.arg("-f").arg("-L").arg("-s").arg("-S").arg("-o").arg(&dest).arg(&url);
|
||||
cmd.arg("-f")
|
||||
.arg("-L")
|
||||
.arg("-s")
|
||||
.arg("-S")
|
||||
.arg("-o")
|
||||
.arg(&dest)
|
||||
.arg(&url);
|
||||
if let Some(t) = &hf_token {
|
||||
cmd.arg("-H").arg(format!("Authorization: Bearer {t}"));
|
||||
}
|
||||
@@ -758,7 +798,11 @@ async fn download_cmf_repo(
|
||||
.unwrap_or(0)
|
||||
};
|
||||
store.push_line(&id, {
|
||||
let sz = if total > 0 { format!(" ({})", format_bytes(total)) } else { String::new() };
|
||||
let sz = if total > 0 {
|
||||
format!(" ({})", format_bytes(total))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!("→ downloading {cmf_path}{sz} from HuggingFace...")
|
||||
});
|
||||
store.set_total(&id, total);
|
||||
@@ -773,7 +817,12 @@ async fn download_cmf_repo(
|
||||
let _poller = spawn_size_poller(store.clone(), id.clone(), output_abs.clone());
|
||||
|
||||
let mut cmd = tokio::process::Command::new("curl");
|
||||
cmd.arg("-f").arg("-L").arg("-s").arg("-S").arg("-o").arg(&output_abs);
|
||||
cmd.arg("-f")
|
||||
.arg("-L")
|
||||
.arg("-s")
|
||||
.arg("-S")
|
||||
.arg("-o")
|
||||
.arg(&output_abs);
|
||||
|
||||
if let Some(t) = &hf_token {
|
||||
cmd.arg("-H").arg(format!("Authorization: Bearer {t}"));
|
||||
@@ -905,7 +954,12 @@ pub fn start_import(store: Arc<JobStore>, cfg: &CmfCfg, p: ImportParams) -> Resu
|
||||
sanitize(&p.name)
|
||||
};
|
||||
// for sharded variants make output distinct (e.g. DeepSeek parts-q2tp vs q4tp); single-file CMF ignores variant
|
||||
if let Some(v) = p.variant.as_deref().map(|s| s.trim()).filter(|s| !s.is_empty()) {
|
||||
if let Some(v) = p
|
||||
.variant
|
||||
.as_deref()
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty())
|
||||
{
|
||||
if v.starts_with("parts-") {
|
||||
let vs = sanitize(v);
|
||||
if !base.ends_with(&vs) {
|
||||
@@ -921,7 +975,11 @@ pub fn start_import(store: Arc<JobStore>, cfg: &CmfCfg, p: ImportParams) -> Resu
|
||||
.map(|d| d.join(format!("{base}.cmf")).to_string_lossy().to_string())
|
||||
.unwrap_or_else(|_| output.clone());
|
||||
|
||||
let id = gen_id(&format!("{}::{}", p.repo, p.variant.as_deref().unwrap_or("")));
|
||||
let id = gen_id(&format!(
|
||||
"{}::{}",
|
||||
p.repo,
|
||||
p.variant.as_deref().unwrap_or("")
|
||||
));
|
||||
// if same repo/variant is already running, don't reset progress — just return it
|
||||
if let Some(existing) = store.get(&id) {
|
||||
if existing.state == "running" {
|
||||
|
||||
Reference in New Issue
Block a user