Add CUB package builder and include in all Red Bear OS configs
CUB (Red Bear OS Package Builder) is a Rust CLI tool that combines package management and building:
- RBPKGBUILD parser (TOML format) with full spec support
- Cookbook adapter converting RBPKGBUILD to recipe.toml
- PKGBUILD (Arch AUR) to RBPKGBUILD conversion with Linuxism detection
- Dependency mapping (Arch to Redox names)
- pkgar package creation integration
- Build environment setup with Cookbook env vars
- CLI with pacman-style shortcuts: -S, -Ss, -B, -G, -Pi, -Sua, -Sc, --import-aur
28 cub-lib tests passing. cub-cli compiles with local pkgutils.
Added cub = {} to redbear-desktop, redbear-full, redbear-minimal configs.
Created recipe symlink and updated integrate-redbear.sh.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
use std::path::Path;
|
||||
|
||||
use serde_derive::Serialize;
|
||||
|
||||
use crate::error::CubError;
|
||||
use crate::rbpkgbuild::RbPkgBuild;
|
||||
|
||||
pub struct PackageCreator {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub target: String,
|
||||
}
|
||||
|
||||
impl PackageCreator {
|
||||
pub fn create_from_stage(
|
||||
stage_dir: &Path,
|
||||
output_path: &Path,
|
||||
secret_key_path: &Path,
|
||||
) -> Result<(), CubError> {
|
||||
if !stage_dir.is_dir() {
|
||||
return Err(CubError::PackageNotFound(format!(
|
||||
"stage directory does not exist: {}",
|
||||
stage_dir.display()
|
||||
)));
|
||||
}
|
||||
|
||||
pkgar_keys::get_skey(secret_key_path).map_err(|err| {
|
||||
CubError::BuildFailed(format!(
|
||||
"failed to load pkgar secret key {}: {err}",
|
||||
secret_key_path.display()
|
||||
))
|
||||
})?;
|
||||
|
||||
pkgar::folder_entries(stage_dir).map_err(|err| {
|
||||
CubError::BuildFailed(format!(
|
||||
"failed to scan stage directory {}: {err}",
|
||||
stage_dir.display()
|
||||
))
|
||||
})?;
|
||||
|
||||
let flags = pkgar_core::HeaderFlags::latest(
|
||||
pkgar_core::Architecture::Independent,
|
||||
pkgar_core::Packaging::Uncompressed,
|
||||
);
|
||||
pkgar::create_with_flags(secret_key_path, output_path, stage_dir, flags).map_err(|err| {
|
||||
CubError::BuildFailed(format!(
|
||||
"failed to create pkgar archive {}: {err}",
|
||||
output_path.display()
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generate_package_toml(rbpkg: &RbPkgBuild) -> String {
|
||||
#[derive(Serialize)]
|
||||
struct PackageMetadata {
|
||||
name: String,
|
||||
version: String,
|
||||
target: String,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
depends: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
optdepends: Vec<String>,
|
||||
}
|
||||
|
||||
let metadata = PackageMetadata {
|
||||
name: rbpkg.package.name.clone(),
|
||||
version: if rbpkg.package.release > 0 {
|
||||
format!("{}-{}", rbpkg.package.version, rbpkg.package.release)
|
||||
} else {
|
||||
rbpkg.package.version.clone()
|
||||
},
|
||||
target: rbpkg
|
||||
.package
|
||||
.architectures
|
||||
.first()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| "x86_64-unknown-redox".to_string()),
|
||||
depends: rbpkg.dependencies.runtime.clone(),
|
||||
optdepends: rbpkg.dependencies.optional.clone(),
|
||||
};
|
||||
|
||||
match toml::to_string_pretty(&metadata) {
|
||||
Ok(rendered) => rendered,
|
||||
Err(_) => format!(
|
||||
"name = \"{}\"\nversion = \"{}\"\ntarget = \"{}\"\n",
|
||||
metadata.name, metadata.version, metadata.target
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::rbpkgbuild::{
|
||||
BuildSection, CompatSection, ConversionStatus, DependenciesSection, InstallSection,
|
||||
PackageSection, PatchesSection, PolicySection, RbPkgBuild, SourceSection,
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
|
||||
fn sample_rbpkgbuild() -> RbPkgBuild {
|
||||
RbPkgBuild {
|
||||
format: 1,
|
||||
package: PackageSection {
|
||||
name: "demo".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
release: 1,
|
||||
description: "demo package".to_string(),
|
||||
homepage: String::new(),
|
||||
license: Vec::new(),
|
||||
architectures: vec!["x86_64-unknown-redox".to_string()],
|
||||
maintainers: Vec::new(),
|
||||
},
|
||||
source: SourceSection::default(),
|
||||
dependencies: DependenciesSection {
|
||||
build: Vec::new(),
|
||||
runtime: vec!["openssl3".to_string()],
|
||||
check: Vec::new(),
|
||||
optional: Vec::new(),
|
||||
provides: vec!["demo-virtual".to_string()],
|
||||
conflicts: vec!["demo-old".to_string()],
|
||||
},
|
||||
build: BuildSection {
|
||||
build_script: vec!["make".to_string()],
|
||||
install_script: vec!["make install".to_string()],
|
||||
..BuildSection::default()
|
||||
},
|
||||
install: InstallSection::default(),
|
||||
patches: PatchesSection::default(),
|
||||
compat: CompatSection {
|
||||
imported_from: String::new(),
|
||||
original_pkgbuild: String::new(),
|
||||
conversion_status: ConversionStatus::Full,
|
||||
target: String::new(),
|
||||
},
|
||||
policy: PolicySection::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generates_package_toml() {
|
||||
let mut rbpkg = sample_rbpkgbuild();
|
||||
rbpkg.dependencies.optional = vec!["git".to_string()];
|
||||
|
||||
let rendered = PackageCreator::generate_package_toml(&rbpkg);
|
||||
|
||||
assert!(rendered.contains("name = \"demo\""));
|
||||
assert!(rendered.contains("version = \"1.0.0-1\""));
|
||||
assert!(rendered.contains("target = \"x86_64-unknown-redox\""));
|
||||
assert!(rendered.contains("depends = [\"openssl3\"]"));
|
||||
assert!(rendered.contains("optdepends = [\"git\"]"));
|
||||
assert!(!rendered.contains("dependencies ="));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors_when_stage_dir_is_missing() {
|
||||
let temp = tempdir().expect("tempdir");
|
||||
let err = PackageCreator::create_from_stage(
|
||||
&temp.path().join("missing-stage"),
|
||||
&temp.path().join("out.pkgar"),
|
||||
&temp.path().join("secret.toml"),
|
||||
)
|
||||
.expect_err("missing stage should fail");
|
||||
|
||||
assert!(matches!(err, CubError::PackageNotFound(_)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user