Skip to content

Commit afc3afc

Browse files
committed
handle provide in own directory
1 parent 91901a1 commit afc3afc

File tree

3 files changed

+237
-53
lines changed

3 files changed

+237
-53
lines changed

sbuild-meta/src/metadata.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,18 @@ impl PackageMetadata {
304304
}
305305
}
306306

307+
// Checksums from annotations
308+
if self.bsum.is_none() {
309+
if let Some(bsum) = manifest.get_annotation("dev.pkgforge.soar.bsum") {
310+
self.bsum = Some(bsum.to_string());
311+
}
312+
}
313+
if self.shasum.is_none() {
314+
if let Some(shasum) = manifest.get_annotation("dev.pkgforge.soar.shasum") {
315+
self.shasum = Some(shasum.to_string());
316+
}
317+
}
318+
307319
// Generate blob reference and download URLs
308320
let filenames = manifest.filenames();
309321

sbuild/src/ghcr.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ pub struct PackageAnnotations {
3838
pub build_id: Option<String>,
3939
pub build_gha: Option<String>,
4040
pub build_script: Option<String>,
41+
/// BLAKE3 checksum of the main binary
42+
pub bsum: Option<String>,
43+
/// SHA256 checksum of the main binary
44+
pub shasum: Option<String>,
4145
}
4246

4347
/// GHCR client for pushing packages
@@ -184,6 +188,12 @@ impl GhcrClient {
184188
if let Some(ref build_script) = meta.build_script {
185189
annotations.insert("dev.pkgforge.soar.build_script".to_string(), build_script.clone());
186190
}
191+
if let Some(ref bsum) = meta.bsum {
192+
annotations.insert("dev.pkgforge.soar.bsum".to_string(), bsum.clone());
193+
}
194+
if let Some(ref shasum) = meta.shasum {
195+
annotations.insert("dev.pkgforge.soar.shasum".to_string(), shasum.clone());
196+
}
187197

188198
annotations
189199
}

sbuild/src/main.rs

Lines changed: 215 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -435,14 +435,6 @@ async fn post_build_processing(
435435
return Err(format!("GHCR login failed: {}", e));
436436
}
437437

438-
// Collect files to push
439-
let files: Vec<PathBuf> = std::fs::read_dir(outdir)
440-
.map_err(|e| format!("Failed to read output directory: {}", e))?
441-
.filter_map(|e| e.ok())
442-
.map(|e| e.path())
443-
.filter(|p| p.is_file())
444-
.collect();
445-
446438
// Read version from .version file
447439
let version = std::fs::read_dir(outdir)
448440
.ok()
@@ -458,63 +450,233 @@ async fn post_build_processing(
458450

459451
// Get architecture
460452
let arch = format!("{}-{}", std::env::consts::ARCH, std::env::consts::OS);
453+
let tag = format!("{}-{}", version, arch.to_lowercase());
461454

462-
// Build GHCR repo path: base_repo/pkg_family/recipe_name
463-
// e.g., pkgforge/bincache/hello/static or pkgforge/pkgcache/cat/appimage.cat.stable
464-
let (full_repo, pkg) = if let Some(url) = recipe_url {
465-
if let Some((pkg_family, recipe_name)) = parse_ghcr_path(url) {
466-
let full_path = format!("{}/{}/{}", base_repo, pkg_family, recipe_name);
467-
info!("GHCR path: {}", full_path);
468-
(full_path, recipe_name)
469-
} else {
470-
warn!("Could not parse GHCR path from recipe URL, using base repo");
471-
(base_repo.clone(), pkg_name.unwrap_or("unknown").to_string())
472-
}
473-
} else {
474-
(base_repo.clone(), pkg_name.unwrap_or("unknown").to_string())
475-
};
455+
// Get pkg_family from recipe URL
456+
let pkg_family = recipe_url
457+
.and_then(|url| parse_ghcr_path(url))
458+
.map(|(family, _)| family)
459+
.unwrap_or_else(|| pkg_name.unwrap_or("unknown").to_string());
476460

477461
// Read metadata from SBUILD file
478462
let metadata = read_sbuild_metadata(outdir).unwrap_or_default();
479463

480-
let annotations = PackageAnnotations {
481-
pkg: if metadata.pkg.is_empty() || metadata.pkg == "unknown" {
482-
pkg.clone()
483-
} else {
484-
metadata.pkg
485-
},
486-
pkg_id: metadata.pkg_id,
487-
pkg_type: metadata.pkg_type,
488-
version: version.clone(),
489-
description: metadata.description,
490-
homepage: metadata.homepage,
491-
license: metadata.license,
492-
build_date: chrono::Utc::now().to_rfc3339(),
493-
build_id: env::var("GITHUB_RUN_ID").ok(),
494-
build_gha: env::var("GITHUB_RUN_ID")
495-
.ok()
496-
.map(|id| format!("https://github.com/{}/actions/runs/{}",
497-
env::var("GITHUB_REPOSITORY").unwrap_or_default(), id)),
498-
build_script: recipe_url.map(|s| s.to_string()),
499-
};
464+
let mut push_success = true;
465+
let mut pushed_urls = Vec::new();
466+
467+
// Check for soar-packages/ directory (explicit multi-package structure)
468+
let soar_packages_dir = outdir.join("soar-packages");
469+
470+
if soar_packages_dir.is_dir() {
471+
// Explicit multi-package mode
472+
info!("Found soar-packages/ directory, using explicit package structure");
473+
474+
// Collect shared files from root (everything except soar-packages/)
475+
let shared_files: Vec<PathBuf> = std::fs::read_dir(outdir)
476+
.map_err(|e| format!("Failed to read output directory: {}", e))?
477+
.filter_map(|e| e.ok())
478+
.map(|e| e.path())
479+
.filter(|p| p.is_file())
480+
.collect();
481+
482+
// Each subdirectory in soar-packages/ is a package
483+
let package_dirs: Vec<PathBuf> = std::fs::read_dir(&soar_packages_dir)
484+
.map_err(|e| format!("Failed to read soar-packages directory: {}", e))?
485+
.filter_map(|e| e.ok())
486+
.map(|e| e.path())
487+
.filter(|p| p.is_dir())
488+
.collect();
489+
490+
if package_dirs.is_empty() {
491+
warn!("soar-packages/ directory is empty");
492+
return Ok(());
493+
}
500494

501-
let tag = format!("{}-{}", version, arch.to_lowercase());
495+
for pkg_dir in &package_dirs {
496+
let pkg_name_dir = pkg_dir
497+
.file_name()
498+
.and_then(|n| n.to_str())
499+
.unwrap_or("unknown");
500+
501+
// Build GHCR repo path for this package
502+
let full_repo = format!("{}/{}/{}", base_repo, pkg_family, pkg_name_dir);
503+
info!("Pushing package {} to {}", pkg_name_dir, full_repo);
502504

503-
match client.push(&files, &full_repo, &tag, &annotations) {
504-
Ok(target) => {
505-
info!("Pushed to {}", target);
506-
if cli.ci {
507-
write_github_env("GHCRPKG_URL", &target);
508-
write_github_env("PUSH_SUCCESSFUL", "YES");
505+
// Collect files: package-specific files + shared files
506+
let mut files_to_push: Vec<PathBuf> = std::fs::read_dir(pkg_dir)
507+
.map_err(|e| format!("Failed to read package directory: {}", e))?
508+
.filter_map(|e| e.ok())
509+
.map(|e| e.path())
510+
.filter(|p| p.is_file())
511+
.collect();
512+
513+
// Add shared files from root
514+
files_to_push.extend(shared_files.clone());
515+
516+
// Find the main binary (same name as directory)
517+
let main_binary = files_to_push.iter().find(|f| {
518+
f.file_name().and_then(|n| n.to_str()) == Some(pkg_name_dir)
519+
});
520+
521+
// Compute checksums for the main binary
522+
let (bsum, shasum) = if let Some(binary_path) = main_binary {
523+
(
524+
checksum::b3sum(binary_path).ok(),
525+
checksum::sha256sum(binary_path).ok(),
526+
)
527+
} else {
528+
(None, None)
529+
};
530+
531+
let annotations = PackageAnnotations {
532+
pkg: pkg_name_dir.to_string(),
533+
pkg_id: metadata.pkg_id.clone(),
534+
pkg_type: metadata.pkg_type.clone(),
535+
version: version.clone(),
536+
description: metadata.description.clone(),
537+
homepage: metadata.homepage.clone(),
538+
license: metadata.license.clone(),
539+
build_date: chrono::Utc::now().to_rfc3339(),
540+
build_id: env::var("GITHUB_RUN_ID").ok(),
541+
build_gha: env::var("GITHUB_RUN_ID")
542+
.ok()
543+
.map(|id| format!("https://github.com/{}/actions/runs/{}",
544+
env::var("GITHUB_REPOSITORY").unwrap_or_default(), id)),
545+
build_script: recipe_url.map(|s| s.to_string()),
546+
bsum,
547+
shasum,
548+
};
549+
550+
match client.push(&files_to_push, &full_repo, &tag, &annotations) {
551+
Ok(target) => {
552+
info!("Pushed {} to {}", pkg_name_dir, target);
553+
pushed_urls.push(target);
554+
}
555+
Err(e) => {
556+
error!("Failed to push {}: {}", pkg_name_dir, e);
557+
push_success = false;
558+
}
509559
}
510560
}
511-
Err(e) => {
512-
if cli.ci {
513-
write_github_env("PUSH_SUCCESSFUL", "NO");
561+
} else {
562+
// Fallback: auto-detect binaries from flat structure
563+
info!("Using auto-detection for package structure");
564+
565+
// Collect all files
566+
let all_files: Vec<PathBuf> = std::fs::read_dir(outdir)
567+
.map_err(|e| format!("Failed to read output directory: {}", e))?
568+
.filter_map(|e| e.ok())
569+
.map(|e| e.path())
570+
.filter(|p| p.is_file())
571+
.collect();
572+
573+
// Identify binary files (exclude metadata files)
574+
let binary_files: Vec<&PathBuf> = all_files
575+
.iter()
576+
.filter(|p| {
577+
let ext = p.extension().and_then(|e| e.to_str()).unwrap_or("");
578+
// Exclude metadata files
579+
!matches!(ext, "json" | "log" | "version" | "sig" | "minisig" | "txt" | "yaml" | "yml" | "png" | "svg")
580+
})
581+
.collect();
582+
583+
if binary_files.is_empty() {
584+
warn!("No binary files found to push");
585+
return Ok(());
586+
}
587+
588+
// Collect shared files (files that don't match any binary name pattern)
589+
let shared_files: Vec<&PathBuf> = all_files
590+
.iter()
591+
.filter(|p| {
592+
let stem = p.file_stem().and_then(|s| s.to_str()).unwrap_or("");
593+
// Shared if no binary has this stem as its name
594+
!binary_files.iter().any(|b| {
595+
b.file_name().and_then(|n| n.to_str()) == Some(stem)
596+
})
597+
})
598+
.filter(|p| {
599+
let ext = p.extension().and_then(|e| e.to_str()).unwrap_or("");
600+
// Only include certain file types as shared
601+
matches!(ext, "log" | "version" | "txt")
602+
})
603+
.collect();
604+
605+
// Push each binary separately to {base_repo}/{pkg_family}/{binary_name}
606+
for binary_path in &binary_files {
607+
let binary_name = binary_path
608+
.file_name()
609+
.and_then(|n| n.to_str())
610+
.unwrap_or("unknown");
611+
612+
// Build GHCR repo path for this binary
613+
let full_repo = format!("{}/{}/{}", base_repo, pkg_family, binary_name);
614+
info!("Pushing {} to {}", binary_name, full_repo);
615+
616+
// Collect files for this binary: binary + associated metadata + shared files
617+
let mut files_to_push: Vec<PathBuf> = vec![(*binary_path).clone()];
618+
619+
// Add associated files (json, sig, png, svg, log)
620+
for ext in &["json", "sig", "minisig", "png", "svg", "log"] {
621+
let assoc_file = outdir.join(format!("{}.{}", binary_name, ext));
622+
if assoc_file.exists() {
623+
files_to_push.push(assoc_file);
624+
}
625+
}
626+
627+
// Add shared files
628+
for shared in &shared_files {
629+
files_to_push.push((*shared).clone());
630+
}
631+
632+
// Compute checksums for this binary
633+
let bsum = checksum::b3sum(binary_path).ok();
634+
let shasum = checksum::sha256sum(binary_path).ok();
635+
636+
let annotations = PackageAnnotations {
637+
pkg: binary_name.to_string(),
638+
pkg_id: metadata.pkg_id.clone(),
639+
pkg_type: metadata.pkg_type.clone(),
640+
version: version.clone(),
641+
description: metadata.description.clone(),
642+
homepage: metadata.homepage.clone(),
643+
license: metadata.license.clone(),
644+
build_date: chrono::Utc::now().to_rfc3339(),
645+
build_id: env::var("GITHUB_RUN_ID").ok(),
646+
build_gha: env::var("GITHUB_RUN_ID")
647+
.ok()
648+
.map(|id| format!("https://github.com/{}/actions/runs/{}",
649+
env::var("GITHUB_REPOSITORY").unwrap_or_default(), id)),
650+
build_script: recipe_url.map(|s| s.to_string()),
651+
bsum,
652+
shasum,
653+
};
654+
655+
match client.push(&files_to_push, &full_repo, &tag, &annotations) {
656+
Ok(target) => {
657+
info!("Pushed {} to {}", binary_name, target);
658+
pushed_urls.push(target);
659+
}
660+
Err(e) => {
661+
error!("Failed to push {}: {}", binary_name, e);
662+
push_success = false;
663+
}
514664
}
515-
return Err(format!("GHCR push failed: {}", e));
516665
}
517666
}
667+
668+
if cli.ci {
669+
if push_success && !pushed_urls.is_empty() {
670+
write_github_env("GHCRPKG_URL", &pushed_urls.join(","));
671+
write_github_env("PUSH_SUCCESSFUL", "YES");
672+
} else {
673+
write_github_env("PUSH_SUCCESSFUL", "NO");
674+
}
675+
}
676+
677+
if !push_success {
678+
return Err("One or more GHCR pushes failed".to_string());
679+
}
518680
} else {
519681
warn!("--push specified but --ghcr-token or --ghcr-repo not provided");
520682
}

0 commit comments

Comments
 (0)