fix(tf): refine binary provider resolution

This commit is contained in:
andrussal 2026-06-02 10:36:49 +02:00
parent 14fd7130be
commit f18ae52e4d
3 changed files with 90 additions and 10 deletions

View File

@ -11,6 +11,7 @@ use std::{
process::Command, process::Command,
}; };
use sha2::{Digest as _, Sha256};
use tracing::info; use tracing::info;
use crate::binary::{ use crate::binary::{
@ -95,16 +96,31 @@ impl BuildBinaryProvider {
} }
fn lock_path(&self) -> PathBuf { fn lock_path(&self) -> PathBuf {
let lock_file_name = self let output_path = self.output_path();
.output_path let lock_file_name = self.lock_file_name(&output_path);
.file_name()
.and_then(|name| name.to_str())
.unwrap_or("binary");
self.lock_dir self.lock_dir
.clone() .clone()
.unwrap_or_else(|| self.workspace_dir().join(".tf-binaries")) .unwrap_or_else(|| self.workspace_dir().join(".tf-binaries"))
.join(format!("{lock_file_name}.lock")) .join(lock_file_name)
}
fn lock_file_name(&self, output_path: &Path) -> String {
let binary_name = output_path
.file_name()
.and_then(|name| name.to_str())
.unwrap_or("binary");
let artifact_hash = self.artifact_path_hash(output_path);
format!("{binary_name}-{artifact_hash}.lock")
}
fn artifact_path_hash(&self, output_path: &Path) -> String {
Sha256::digest(output_path.to_string_lossy().as_bytes())
.iter()
.take(8)
.map(|byte| format!("{byte:02x}"))
.collect()
} }
fn workspace_dir(&self) -> PathBuf { fn workspace_dir(&self) -> PathBuf {

View File

@ -11,8 +11,10 @@ use crate::binary::{BinaryProvider, BinaryProviderError, FallbackBinaryProvider}
impl BinaryProvider for FallbackBinaryProvider { impl BinaryProvider for FallbackBinaryProvider {
fn try_resolve(&self) -> Result<Option<PathBuf>, BinaryProviderError> { fn try_resolve(&self) -> Result<Option<PathBuf>, BinaryProviderError> {
for provider in &self.providers { for provider in &self.providers {
if let Some(path) = provider.try_resolve()? { match provider.resolve() {
return Ok(Some(path)); Ok(path) => return Ok(Some(path)),
Err(BinaryProviderError::NotFound { .. }) => continue,
Err(error) => return Err(error),
} }
} }

View File

@ -2,8 +2,11 @@ use std::{
fs, fs,
io::{Read as _, Write as _}, io::{Read as _, Write as _},
net::TcpListener, net::TcpListener,
path::Path, path::{Path, PathBuf},
sync::Arc, sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
thread, thread,
}; };
@ -54,6 +57,31 @@ fn resolves_first_available_fallback_provider() {
assert_eq!(path, binary); assert_eq!(path, binary);
} }
#[test]
fn fallback_reuses_inner_provider_cache() {
let temp = TempDir::new().expect("temp dir");
let binary = temp.path().join("node");
write_file(&binary, b"binary");
let resolve_count = Arc::new(AtomicUsize::new(0));
let cached_provider: BinaryProviderRef = Arc::new(CountingBinaryProvider::new(
&binary,
Arc::clone(&resolve_count),
));
let first = FallbackBinaryProvider::new([
missing_binary_provider(temp.path().join("missing-first")),
Arc::clone(&cached_provider),
]);
let second = FallbackBinaryProvider::new([
missing_binary_provider(temp.path().join("missing-second")),
cached_provider,
]);
assert_eq!(first.resolve().expect("first fallback resolves"), binary);
assert_eq!(second.resolve().expect("second fallback resolves"), binary);
assert_eq!(resolve_count.load(Ordering::SeqCst), 1);
}
#[test] #[test]
fn runs_build_command_and_returns_output_path() { fn runs_build_command_and_returns_output_path() {
let temp = TempDir::new().expect("temp dir"); let temp = TempDir::new().expect("temp dir");
@ -160,6 +188,40 @@ fn write_file(path: &Path, contents: &[u8]) {
fs::write(path, contents).expect("write file"); fs::write(path, contents).expect("write file");
} }
fn missing_binary_provider(path: PathBuf) -> BinaryProviderRef {
Arc::new(PathBinaryProvider::new(path))
}
struct CountingBinaryProvider {
path: PathBuf,
resolve_count: Arc<AtomicUsize>,
}
impl CountingBinaryProvider {
fn new(path: &Path, resolve_count: Arc<AtomicUsize>) -> Self {
Self {
path: path.to_owned(),
resolve_count,
}
}
}
impl BinaryProvider for CountingBinaryProvider {
fn try_resolve(&self) -> Result<Option<PathBuf>, BinaryProviderError> {
self.resolve_count.fetch_add(1, Ordering::SeqCst);
Ok(Some(self.path.clone()))
}
fn display(&self) -> String {
"counting".to_owned()
}
fn cache_key(&self) -> String {
format!("counting:{}", self.path.display())
}
}
fn sha256_hex(bytes: &[u8]) -> String { fn sha256_hex(bytes: &[u8]) -> String {
Sha256::digest(bytes) Sha256::digest(bytes)
.iter() .iter()