a generation another binary built is rebuilt instead of counting as unchanged
Signed-off-by: dawn <90008@klbr.net>
6 unmodified lines789101112131415161718192021222324252627282930313233343536108 unmodified linesExpand all6 unmodified linesuse crate::config::ValidatedConfig;
pub const STATIC_VERSION: &str = "v1.15.0-static.1";
/// the running binary by path, size and mtime: a deploy or rebuild changes it, hashing it would cost a readpub fn built_by() -> &'static str { static BUILT_BY: std::sync::OnceLock<String> = std::sync::OnceLock::new(); BUILT_BY.get_or_init(|| { let identify = || -> std::io::Result<String> { let path = std::fs::canonicalize(std::env::current_exe()?)?; let meta = std::fs::metadata(&path)?; let mtime = meta .modified()? .duration_since(std::time::UNIX_EPOCH) .unwrap_or_default(); Ok(format!( "{}@{}+{}", path.display(), mtime.as_nanos(), meta.len() )) }; identify().unwrap_or_default() })}
pub fn health_body() -> serde_json::Value { serde_json::json!({ "version": format!("knot-static {STATIC_VERSION}") })}
108 unmodified linesExpand all1084 unmodified linesExpand all1085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321084 unmodified linesExpand all index.contains("first") && index.contains("second"), "{index}" );}
#[test]fn a_generation_another_binary_built_is_built_again_once() { let env = setup_test_env(); let unchanged = || { let generated = knot_static(&env.config, ["generate"]); assert!(generated.status.success()); String::from_utf8_lossy(&generated.stderr).contains("unchanged, nothing to do") }; assert!(!unchanged()); let first = get_only_generation_dir(&env.output); let first_id = first.file_name().unwrap().to_str().unwrap().to_string(); assert!( knot_static(&env.config, ["activate", &first_id]) .status .success() ); assert!(unchanged(), "the same binary takes the shortcut");
let manifest = first.join("manifest.json"); let rewrite = |value: &serde_json::Value| { let mut permissions = std::fs::metadata(&manifest).unwrap().permissions(); permissions.set_readonly(false); std::fs::set_permissions(&manifest, permissions).unwrap(); std::fs::write(&manifest, serde_json::to_vec(value).unwrap()).unwrap(); }; let mut value: serde_json::Value = serde_json::from_slice(&std::fs::read(&manifest).unwrap()).unwrap(); let built_by = value["built_by"].take(); assert!( built_by.as_str().is_some_and(|by| !by.is_empty()), "{value}" ); value["built_by"] = "an older binary".into(); rewrite(&value); assert!( !unchanged(), "a generation from another binary is built again" );
value["built_by"] = built_by; rewrite(&value); assert!(unchanged(), "and only once");}79 unmodified lines808182838485868788899042 unmodified lines13313413513613713813914014127 unmodified lines79 unmodified lines pub knot_did: String, pub hostname: String, pub owner: String, pub static_version: String, /// the binary that built it, a generation built by another one isn't unchanged #[serde(default, skip_serializing_if = "String::is_empty")] pub built_by: String, pub profile: serde_json::Value, pub repos: Vec<RepoEntry>, #[serde(default, skip_serializing_if = "Option::is_none")] pub global_input_digest: Option<String>,42 unmodified lines knot_did: "did:web:knot.test".to_string(), hostname: "knot.test".to_string(), owner: "did:plc:owner".to_string(), static_version: "test".to_string(), built_by: String::new(), profile: serde_json::json!({ "atproto": {} }), repos: Vec::new(), global_input_digest: None, handles_digest: None,27 unmodified lines618 unmodified linesExpand all61962062162262362462562662798 unmodified lines618 unmodified linesExpand all knot_did: config.knot_did.as_str().to_string(), hostname: config.hostname.as_str().to_string(), owner: config.owner.as_str().to_string(), static_version: identity::STATIC_VERSION.to_string(), built_by: identity::built_by().to_string(), profile: serde_json::json!({ "atproto": atproto_heads, "atproto_entries": atproto_entries, "git_xrpc_entries": git_xrpc_entries,98 unmodified lines187 unmodified linesExpand all188189190191192193194195196127 unmodified linesExpand all187 unmodified linesExpand all knot_did: "did:web:knot.test".to_string(), hostname: "knot.test".to_string(), owner: "did:plc:owner".to_string(), static_version: "test".to_string(), built_by: String::new(), profile: serde_json::json!({}), repos: vec![RepoEntry { release_id: None, repo_did: did.to_string(),127 unmodified linesExpand all320 unmodified linesExpand all321322323324325326327328329181 unmodified linesExpand all320 unmodified linesExpand all knot_did: "did:web:knot.test".to_string(), hostname: "knot.test".to_string(), owner: "did:plc:owner".to_string(), static_version: "test".to_string(), built_by: String::new(), profile: serde_json::json!({}), repos: vec![entry(did, fingerprint, Some("release-1"))], global_input_digest: Some("cfg".to_string()), handles_digest: None,181 unmodified linesExpand all264 unmodified linesExpand all2652662672682692692702712722732743 unmodified lines27827928028128228328428528628728883 unmodified lines264 unmodified linesExpand all span.end(); Ok(files)}
/// a deleted repo must not pass as unchanged. the hosted set has to match the generation's exactly./// a deleted repo must not pass as unchanged. the hosted set has to match the generation's exactly,/// and a generation another binary built is built again once./// this runs before the route sections are read.pub(crate) fn unchanged_shortcut( snapshots: &[crate::snapshot::RepoSnapshot], outcomes: &[RepoOutcome],3 unmodified lines) -> bool { let Some(active) = active else { return false; }; if active.manifest.built_by != crate::identity::built_by() { return false; } if !outcomes .iter() .all(|o| matches!(o, RepoOutcome::Unchanged { .. })) {83 unmodified lines29 unmodified lines303132333435363738263 unmodified linesExpand all30230330430530630730830931031131231331431531631731831932032132232 unmodified lines29 unmodified lines knot_did: "did:web:knot.example".to_string(), hostname: "knot.example".to_string(), owner: "did:plc:owner".to_string(), static_version: "fixture".to_string(), built_by: crate::identity::built_by().to_string(), profile: serde_json::json!({}), repos: vec![entry("did:plc:fixture")], global_input_digest: None, handles_digest: None,263 unmodified linesExpand all Some(&active_ab) ), "a removed active repo must not ghost through the unchanged check" );
let mut older = active.manifest.clone(); older.built_by = "an older binary".to_string(); assert!( !unchanged_shortcut( std::slice::from_ref(&snap("did:plc:a")), std::slice::from_ref(&outcome), &carried, &hosted, Some(&plan::ActiveGeneration { manifest: older }) ), "a generation another binary built is built again" );}
#[cfg(unix)]#[test]32 unmodified lines460 unmodified linesExpand all461462463464465466467468469957 unmodified linesExpand all460 unmodified linesExpand all knot_did: "did:web:knot.test".into(), hostname: "knot.test".into(), owner: "did:plc:owner".into(), static_version: "test".into(), built_by: String::new(), profile: serde_json::json!({}), repos: vec![repo_entry("did:plc:repo")], rebuilt_repos: Vec::new(), global_input_digest: None,957 unmodified linesExpand all