SPEC.md and ARCHITECTURE.md were sitting at the repo root alongside Cargo.toml/Makefile.toml/packages.d — moved both into docs/ (doubleo7 already does this for its own supplementary docs, so this matches an existing convention in the fleet rather than inventing a new one). Updated every doc-comment cross-reference across src/*.rs and Makefile.toml (23 references) to the new docs/SPEC.md / docs/ ARCHITECTURE.md paths. The two files' own cross-references to each other didn't need changing — they're still same-directory relative references. Also updated the project reference memory pointing at ARCHITECTURE.md's location, so it doesn't go stale pointing at a path that no longer exists. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
136 lines
4.8 KiB
Rust
136 lines
4.8 KiB
Rust
//! Persists two independent per-package facts as plain files: the last
|
|
//! published version, and any version currently pending human review.
|
|
//! The only module that touches `state/` on disk.
|
|
|
|
use anyhow::Result;
|
|
use std::path::Path;
|
|
|
|
/// Last-known-published version per package, so re-runs don't re-flag a
|
|
/// version already handled. Deliberately just one file per package for
|
|
/// now — this is where a real review-queue persistence layer plugs in
|
|
/// later (see docs/SPEC.md > Architecture > Reviewer queue).
|
|
pub fn load_last_version(state_dir: &Path, name: &str) -> Option<String> {
|
|
std::fs::read_to_string(state_dir.join(format!("{name}.version")))
|
|
.ok()
|
|
.map(|s| s.trim().to_string())
|
|
}
|
|
|
|
pub fn save_last_version(state_dir: &Path, name: &str, version: &str) -> Result<()> {
|
|
std::fs::create_dir_all(state_dir)?;
|
|
std::fs::write(state_dir.join(format!("{name}.version")), version)?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Tag currently awaiting human review for a tier 4-6 package (see
|
|
/// docs/SPEC.md > Architecture > Reviewer queue), if any. Separate from
|
|
/// `load_last_version`/`save_last_version`: approving a review doesn't
|
|
/// mean future versions auto-publish, so the two must be tracked
|
|
/// independently.
|
|
pub fn load_pending_version(state_dir: &Path, name: &str) -> Option<String> {
|
|
std::fs::read_to_string(state_dir.join(format!("{name}.pending")))
|
|
.ok()
|
|
.map(|s| s.trim().to_string())
|
|
}
|
|
|
|
pub fn save_pending_version(state_dir: &Path, name: &str, version: &str) -> Result<()> {
|
|
std::fs::create_dir_all(state_dir)?;
|
|
std::fs::write(state_dir.join(format!("{name}.pending")), version)?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Clears a pending review, e.g. once it's been approved and published.
|
|
/// Not an error if there was nothing pending.
|
|
pub fn clear_pending_version(state_dir: &Path, name: &str) -> Result<()> {
|
|
match std::fs::remove_file(state_dir.join(format!("{name}.pending"))) {
|
|
Ok(()) => Ok(()),
|
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
|
|
Err(e) => Err(e.into()),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn load_last_version_missing_file_returns_none() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
assert_eq!(load_last_version(dir.path(), "nonexistent"), None);
|
|
}
|
|
|
|
#[test]
|
|
fn save_then_load_roundtrips() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
save_last_version(dir.path(), "uv", "0.12.15").unwrap();
|
|
assert_eq!(
|
|
load_last_version(dir.path(), "uv"),
|
|
Some("0.12.15".to_string())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn load_last_version_trims_trailing_whitespace() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
std::fs::write(dir.path().join("uv.version"), "0.12.15\n").unwrap();
|
|
assert_eq!(
|
|
load_last_version(dir.path(), "uv"),
|
|
Some("0.12.15".to_string())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn save_last_version_creates_state_dir_if_missing() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let nested = dir.path().join("nested/state");
|
|
save_last_version(&nested, "uv", "0.12.15").unwrap();
|
|
assert_eq!(
|
|
load_last_version(&nested, "uv"),
|
|
Some("0.12.15".to_string())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn load_pending_version_missing_file_returns_none() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
assert_eq!(load_pending_version(dir.path(), "scaleway-cli"), None);
|
|
}
|
|
|
|
#[test]
|
|
fn save_then_load_pending_roundtrips() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
save_pending_version(dir.path(), "scaleway-cli", "v2.62.0").unwrap();
|
|
assert_eq!(
|
|
load_pending_version(dir.path(), "scaleway-cli"),
|
|
Some("v2.62.0".to_string())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn clear_pending_version_removes_it() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
save_pending_version(dir.path(), "scaleway-cli", "v2.62.0").unwrap();
|
|
clear_pending_version(dir.path(), "scaleway-cli").unwrap();
|
|
assert_eq!(load_pending_version(dir.path(), "scaleway-cli"), None);
|
|
}
|
|
|
|
#[test]
|
|
fn clear_pending_version_is_a_noop_when_nothing_pending() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
assert!(clear_pending_version(dir.path(), "scaleway-cli").is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn pending_and_last_version_are_tracked_independently() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
save_last_version(dir.path(), "scaleway-cli", "v2.61.0").unwrap();
|
|
save_pending_version(dir.path(), "scaleway-cli", "v2.62.0").unwrap();
|
|
assert_eq!(
|
|
load_last_version(dir.path(), "scaleway-cli"),
|
|
Some("v2.61.0".to_string())
|
|
);
|
|
assert_eq!(
|
|
load_pending_version(dir.path(), "scaleway-cli"),
|
|
Some("v2.62.0".to_string())
|
|
);
|
|
}
|
|
}
|