mirror of
https://gitlab.redox-os.org/redox-os/redox.git
synced 2026-06-17 23:44:17 +08:00
Identify outdated packages and smarter git
This commit is contained in:
parent
bb8c13cf23
commit
81788a6fd1
@ -4,6 +4,7 @@ use cookbook::config::{CookConfig, get_config, init_config};
|
||||
use cookbook::cook::cook_build::build;
|
||||
use cookbook::cook::fetch::{fetch, fetch_offline};
|
||||
use cookbook::cook::fs::{create_target_dir, run_command};
|
||||
use cookbook::cook::ident;
|
||||
use cookbook::cook::package::package;
|
||||
use cookbook::cook::pty::{PtyOut, UnixSlavePty, flush_pty, setup_pty};
|
||||
use cookbook::cook::script::KILL_ALL_PID;
|
||||
@ -185,6 +186,9 @@ fn main_inner() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
let (config, command, recipe_names) = parse_args(args)?;
|
||||
if command.is_building() {
|
||||
ident::init_ident();
|
||||
}
|
||||
if command == CliCommand::Cook && config.cook.tui {
|
||||
if let Some((name, e)) = run_tui_cook(config.clone(), recipe_names.clone())? {
|
||||
let _ = stderr().write(e.as_bytes());
|
||||
@ -534,10 +538,9 @@ fn handle_fetch(
|
||||
allow_offline: bool,
|
||||
logger: &PtyOut,
|
||||
) -> anyhow::Result<PathBuf> {
|
||||
let recipe_dir = &recipe.dir;
|
||||
let source_dir = match config.cook.offline && allow_offline {
|
||||
true => fetch_offline(recipe_dir, &recipe.recipe, logger),
|
||||
false => fetch(recipe_dir, &recipe.recipe, logger),
|
||||
true => fetch_offline(&recipe, logger),
|
||||
false => fetch(&recipe, logger),
|
||||
}
|
||||
.map_err(|e| anyhow!("failed to fetch: {:?}", e))?;
|
||||
|
||||
@ -553,7 +556,7 @@ fn handle_cook(
|
||||
) -> anyhow::Result<()> {
|
||||
let recipe_dir = &recipe.dir;
|
||||
let target_dir = create_target_dir(recipe_dir, recipe.target).map_err(|e| anyhow!(e))?;
|
||||
let (stage_dir, auto_deps) = build(
|
||||
let (stage_dirs, auto_deps) = build(
|
||||
recipe_dir,
|
||||
&source_dir,
|
||||
&target_dir,
|
||||
@ -565,15 +568,8 @@ fn handle_cook(
|
||||
)
|
||||
.map_err(|err| anyhow!("failed to build: {:?}", err))?;
|
||||
|
||||
package(
|
||||
&stage_dir,
|
||||
&target_dir,
|
||||
&recipe.name,
|
||||
&recipe.recipe,
|
||||
&auto_deps,
|
||||
logger,
|
||||
)
|
||||
.map_err(|err| anyhow!("failed to package: {:?}", err))?;
|
||||
package(&recipe, &stage_dirs, &auto_deps, logger)
|
||||
.map_err(|err| anyhow!("failed to package: {:?}", err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
use anyhow::anyhow;
|
||||
use cookbook::WALK_DEPTH;
|
||||
use cookbook::config::{get_config, init_config};
|
||||
use cookbook::cook::package as cook_package;
|
||||
use cookbook::cook::ident::{get_ident, init_ident};
|
||||
use cookbook::cook::{fetch, package as cook_package};
|
||||
use cookbook::recipe::CookRecipe;
|
||||
use pkg::package::{Repository, SourceIdentifier};
|
||||
use pkg::{Package, PackageName, recipes};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{Read, Write};
|
||||
@ -27,7 +28,6 @@ fn is_newer(src: &Path, dst: &Path) -> bool {
|
||||
#[derive(Clone)]
|
||||
struct CliConfig {
|
||||
repo_dir: PathBuf,
|
||||
nonstop: bool,
|
||||
appstream: bool,
|
||||
recipe_list: Vec<String>,
|
||||
}
|
||||
@ -40,7 +40,6 @@ impl CliConfig {
|
||||
.expect("Usage: repo_builder <REPO_DIR> <recipe1> <recipe2> ...");
|
||||
Ok(CliConfig {
|
||||
repo_dir: PathBuf::from(repo_dir),
|
||||
nonstop: get_config().cook.nonstop,
|
||||
appstream: env::var("COOKBOOK_APPSTREAM").ok().as_deref() == Some("true"),
|
||||
recipe_list: args.collect(),
|
||||
})
|
||||
@ -48,7 +47,7 @@ impl CliConfig {
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
init_config();
|
||||
init_ident();
|
||||
let conf = CliConfig::parse_args()?;
|
||||
Ok(publish_packages(&conf)?)
|
||||
}
|
||||
@ -65,7 +64,7 @@ fn publish_packages(config: &CliConfig) -> anyhow::Result<()> {
|
||||
//
|
||||
// The following adds the package dependencies of the recipes to the repo as
|
||||
// well.
|
||||
let recipe_list = Package::new_recursive(
|
||||
let (recipe_list, recipe_map) = Package::new_recursive_nonstop(
|
||||
&config
|
||||
.recipe_list
|
||||
.iter()
|
||||
@ -73,18 +72,16 @@ fn publish_packages(config: &CliConfig) -> anyhow::Result<()> {
|
||||
// Don't publish host packages
|
||||
.filter(|pkg| pkg.as_ref().is_ok_and(|p| !p.is_host()))
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
config.nonstop,
|
||||
WALK_DEPTH,
|
||||
)?
|
||||
.into_iter()
|
||||
.map(|pkg| pkg.name.clone())
|
||||
.collect::<Vec<_>>();
|
||||
);
|
||||
|
||||
let mut appstream_sources: HashMap<String, PathBuf> = HashMap::new();
|
||||
let mut packages: BTreeMap<String, String> = BTreeMap::new();
|
||||
let mut outdated_packages: BTreeMap<String, SourceIdentifier> = BTreeMap::new();
|
||||
|
||||
// === 1. Push recipes in list ===
|
||||
for recipe in &recipe_list {
|
||||
for recipe_toml in &recipe_list {
|
||||
let recipe = &recipe_toml.name;
|
||||
let Some(recipe_path) = recipes::find(recipe.name()) else {
|
||||
eprintln!("recipe {} not found", recipe);
|
||||
continue;
|
||||
@ -163,22 +160,68 @@ fn publish_packages(config: &CliConfig) -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// === 3. List outdated packages ===
|
||||
for (recipe, e) in recipe_map
|
||||
.into_iter()
|
||||
.filter_map(|(k, v)| v.err().and_then(|e| Some((k, e))))
|
||||
{
|
||||
eprintln!(
|
||||
"\x1b[0;91;49mrepo - marking {} as outdated:\x1b[0m {e}",
|
||||
recipe
|
||||
);
|
||||
|
||||
let Some(recipe_path) = recipes::find(recipe.name()) else {
|
||||
eprintln!("recipe {} not found", recipe);
|
||||
continue;
|
||||
};
|
||||
let Ok(cookbook_recipe) = CookRecipe::from_path(recipe_path, true, false) else {
|
||||
eprintln!("recipe {} unable to read", recipe);
|
||||
continue;
|
||||
};
|
||||
|
||||
match fetch::fetch_get_source_info(&cookbook_recipe) {
|
||||
Ok(source_ident) => {
|
||||
outdated_packages.insert(recipe.name().to_string(), source_ident);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"\x1b[0;91;49m source of {} is not identifiable:\x1b[0m {e}",
|
||||
recipe
|
||||
);
|
||||
let ident = get_ident();
|
||||
outdated_packages.insert(
|
||||
recipe.name().to_string(),
|
||||
SourceIdentifier {
|
||||
source_identifier: "missing_source".to_string(),
|
||||
commit_identifier: ident.commit.clone(),
|
||||
time_identifier: ident.time.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
eprintln!("\x1b[01;38;5;155mrepo - generating repo.toml\x1b[0m");
|
||||
|
||||
// === 3. Read and update repo.toml ===
|
||||
// === 4. Read and update repo.toml ===
|
||||
let repo_toml_path = repo_path.join("repo.toml");
|
||||
if repo_toml_path.exists() {
|
||||
let mut file = File::open(&repo_toml_path)?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
|
||||
let parsed: Value = toml::from_str(&contents)?;
|
||||
if let Some(pkg_table) = parsed.get("packages").and_then(|v| v.as_table()) {
|
||||
for (k, v) in pkg_table {
|
||||
if let Some(s) = v.as_str() {
|
||||
packages.insert(k.clone(), format!("\"{}\"", s));
|
||||
} else {
|
||||
packages.insert(k.clone(), v.to_string());
|
||||
let parsed: Repository = toml::from_str(&contents)?;
|
||||
for (k, v) in parsed.packages {
|
||||
packages.insert(k, v);
|
||||
}
|
||||
if parsed.outdated_packages.len() > 0 {
|
||||
let built_packages: BTreeSet<String> = recipe_list
|
||||
.iter()
|
||||
.map(|p| p.name.name().to_string())
|
||||
.collect();
|
||||
for (k, v) in parsed.outdated_packages {
|
||||
if outdated_packages.contains_key(&k) || !built_packages.contains(&k) {
|
||||
outdated_packages.insert(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -203,21 +246,16 @@ fn publish_packages(config: &CliConfig) -> anyhow::Result<()> {
|
||||
let version_str = parsed
|
||||
.get("blake3")
|
||||
.unwrap_or_else(|| parsed.get("version").unwrap_or_else(|| &empty_ver))
|
||||
.to_string(); // includes quotes
|
||||
.as_str()
|
||||
.unwrap_or("");
|
||||
let package_name = path.file_stem().unwrap().to_string_lossy().to_string();
|
||||
packages.insert(package_name, version_str);
|
||||
}
|
||||
|
||||
// FIXME: Use proper TOML serializer
|
||||
let mut output = String::from("[packages]\n");
|
||||
for (name, version) in &packages {
|
||||
output.push_str(&if name.contains('.') {
|
||||
format!("\"{name}\" = {version}\n")
|
||||
} else {
|
||||
format!("{name} = {version}\n")
|
||||
});
|
||||
packages.insert(package_name, version_str.to_string());
|
||||
}
|
||||
|
||||
let output = toml::to_string(&Repository {
|
||||
packages,
|
||||
outdated_packages,
|
||||
})?;
|
||||
let mut output_file = File::create(&repo_toml_path)?;
|
||||
output_file.write_all(output.as_bytes())?;
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
pub mod cook_build;
|
||||
pub mod fetch;
|
||||
pub mod fs;
|
||||
pub mod ident;
|
||||
pub mod package;
|
||||
pub mod pty;
|
||||
pub mod script;
|
||||
|
||||
@ -265,6 +265,15 @@ pub fn build(
|
||||
if stage_dir.is_dir() {
|
||||
remove_all(&stage_dir)?;
|
||||
}
|
||||
// delete to let repo_builder know if this build fail later
|
||||
let stage_file = stage_dir.with_added_extension("pkgar");
|
||||
if stage_file.is_file() {
|
||||
remove_all(&stage_file)?;
|
||||
}
|
||||
let stage_meta = stage_dir.with_added_extension("toml");
|
||||
if stage_meta.is_file() {
|
||||
remove_all(&stage_meta)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use pkg::package::SourceIdentifier;
|
||||
|
||||
use crate::REMOTE_PKG_SOURCE;
|
||||
use crate::config::translate_mirror;
|
||||
use crate::cook::fs::*;
|
||||
@ -8,9 +10,11 @@ use crate::cook::script::*;
|
||||
use crate::is_redox;
|
||||
use crate::log_to_pty;
|
||||
use crate::recipe::BuildKind;
|
||||
use crate::recipe::Recipe;
|
||||
use crate::recipe::CookRecipe;
|
||||
use crate::{blake3, recipe::SourceRecipe};
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
@ -30,27 +34,33 @@ pub(crate) fn get_blake3(path: &PathBuf, show_progress: bool) -> Result<String,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fetch_offline(
|
||||
recipe_dir: &Path,
|
||||
recipe: &Recipe,
|
||||
logger: &PtyOut,
|
||||
) -> Result<PathBuf, String> {
|
||||
pub fn fetch_offline(recipe: &CookRecipe, logger: &PtyOut) -> Result<PathBuf, String> {
|
||||
let recipe_dir = &recipe.dir;
|
||||
let source_dir = recipe_dir.join("source");
|
||||
if recipe.build.kind == BuildKind::None {
|
||||
// the build function doesn't need source dir exists
|
||||
return Ok(source_dir);
|
||||
}
|
||||
if recipe.build.kind == BuildKind::Remote {
|
||||
fetch_remote(recipe_dir, recipe, true, logger)?;
|
||||
return Ok(source_dir);
|
||||
match recipe.recipe.build.kind {
|
||||
BuildKind::None => {
|
||||
// the build function doesn't need source dir exists
|
||||
fetch_apply_source_info(recipe, "".to_string())?;
|
||||
return Ok(source_dir);
|
||||
}
|
||||
BuildKind::Remote => {
|
||||
fetch_remote(recipe_dir, recipe, true, logger)?;
|
||||
return Ok(source_dir);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match &recipe.source {
|
||||
let ident = match &recipe.recipe.source {
|
||||
Some(SourceRecipe::Path { path: _ }) | None => {
|
||||
return fetch(recipe_dir, recipe, logger);
|
||||
fetch(recipe, logger)?;
|
||||
"local_source".to_string()
|
||||
}
|
||||
Some(SourceRecipe::SameAs { same_as: _ }) => {
|
||||
return fetch(recipe_dir, recipe, logger);
|
||||
Some(SourceRecipe::SameAs { same_as }) => {
|
||||
let recipe = fetch_resolve_canon(recipe_dir, &same_as, recipe.name.is_host())?;
|
||||
// recursively fetch
|
||||
fetch_offline(&recipe, logger)?;
|
||||
fetch_make_symlink(&source_dir, &same_as)?;
|
||||
fetch_get_source_info(&recipe)?.source_identifier
|
||||
}
|
||||
Some(SourceRecipe::Git {
|
||||
git: _,
|
||||
@ -62,6 +72,8 @@ pub fn fetch_offline(
|
||||
shallow_clone: _,
|
||||
}) => {
|
||||
offline_check_exists(&source_dir)?;
|
||||
let (head_rev, _) = get_git_head_rev(&source_dir)?;
|
||||
head_rev
|
||||
}
|
||||
Some(SourceRecipe::Tar {
|
||||
tar: _,
|
||||
@ -93,29 +105,38 @@ pub fn fetch_offline(
|
||||
offline_check_exists(&source_dir)?;
|
||||
}
|
||||
}
|
||||
blake3.clone().unwrap_or("no_tar_blake3_hash_info".into())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fetch_apply_source_info(recipe, ident)?;
|
||||
|
||||
Ok(source_dir)
|
||||
}
|
||||
|
||||
pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &PtyOut) -> Result<PathBuf, String> {
|
||||
pub fn fetch(recipe: &CookRecipe, logger: &PtyOut) -> Result<PathBuf, String> {
|
||||
let recipe_dir = &recipe.dir;
|
||||
let source_dir = recipe_dir.join("source");
|
||||
if recipe.build.kind == BuildKind::None {
|
||||
// the build function doesn't need source dir exists
|
||||
return Ok(source_dir);
|
||||
}
|
||||
if recipe.build.kind == BuildKind::Remote {
|
||||
fetch_remote(recipe_dir, recipe, false, logger)?;
|
||||
return Ok(source_dir);
|
||||
match recipe.recipe.build.kind {
|
||||
BuildKind::None => {
|
||||
// the build function doesn't need source dir exists
|
||||
fetch_apply_source_info(recipe, "".to_string())?;
|
||||
return Ok(source_dir);
|
||||
}
|
||||
BuildKind::Remote => {
|
||||
fetch_remote(recipe_dir, recipe, false, logger)?;
|
||||
return Ok(source_dir);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match &recipe.source {
|
||||
let ident = match &recipe.recipe.source {
|
||||
Some(SourceRecipe::SameAs { same_as }) => {
|
||||
let (canon_dir, recipe) = fetch_resolve_canon(recipe_dir, &same_as)?;
|
||||
let recipe = fetch_resolve_canon(recipe_dir, &same_as, recipe.name.is_host())?;
|
||||
// recursively fetch
|
||||
fetch(&canon_dir, &recipe, logger)?;
|
||||
fetch(&recipe, logger)?;
|
||||
fetch_make_symlink(&source_dir, &same_as)?;
|
||||
fetch_get_source_info(&recipe)?.source_identifier
|
||||
}
|
||||
Some(SourceRecipe::Path { path }) => {
|
||||
if !source_dir.is_dir() || modified_dir(Path::new(&path))? > modified_dir(&source_dir)?
|
||||
@ -135,6 +156,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &PtyOut) -> Result<Path
|
||||
)
|
||||
})?;
|
||||
}
|
||||
"local_source".to_string()
|
||||
}
|
||||
Some(SourceRecipe::Git {
|
||||
git,
|
||||
@ -147,7 +169,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &PtyOut) -> Result<Path
|
||||
}) => {
|
||||
//TODO: use libgit?
|
||||
let shallow_clone = *shallow_clone == Some(true);
|
||||
if !source_dir.is_dir() {
|
||||
let can_skip_rebuild = if !source_dir.is_dir() {
|
||||
// Create source.tmp
|
||||
let source_dir_tmp = recipe_dir.join("source.tmp");
|
||||
create_dir_clean(&source_dir_tmp)?;
|
||||
@ -171,9 +193,10 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &PtyOut) -> Result<Path
|
||||
|
||||
// Move source.tmp to source atomically
|
||||
rename(&source_dir_tmp, &source_dir)?;
|
||||
|
||||
false
|
||||
} else {
|
||||
let source_git_dir = source_dir.join(".git");
|
||||
if !source_git_dir.is_dir() {
|
||||
if !source_dir.join(".git").is_dir() {
|
||||
return Err(format!(
|
||||
"'{}' is not a git repository, but recipe indicated git source",
|
||||
source_dir.display(),
|
||||
@ -191,69 +214,96 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &PtyOut) -> Result<Path
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("fetch").arg("origin");
|
||||
run_command(command, logger)?;
|
||||
}
|
||||
|
||||
if let Some(_upstream) = upstream {
|
||||
//TODO: set upstream URL
|
||||
// git remote set-url upstream "$GIT_UPSTREAM" &> /dev/null ||
|
||||
// git remote add upstream "$GIT_UPSTREAM"
|
||||
// git fetch upstream
|
||||
}
|
||||
let (head_rev, detached_rev) = get_git_head_rev(&source_dir)?;
|
||||
if detached_rev {
|
||||
if let Some(rev) = rev
|
||||
&& let Ok(exp_rev) = get_git_tag_rev(&source_dir, &rev)
|
||||
{
|
||||
exp_rev == head_rev
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
let (_, remote_branch, remote_name, remote_url) =
|
||||
get_git_remote_tracking(&source_dir)?;
|
||||
// TODO: how to get default branch and compare it here?
|
||||
if remote_name == "origin" && &remote_url == chop_dot_git(git) {
|
||||
let fetch_rev =
|
||||
get_git_fetch_rev(&source_dir, &remote_url, &remote_branch)?;
|
||||
fetch_rev == head_rev
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(rev) = rev {
|
||||
// Check out specified revision
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("checkout").arg(rev);
|
||||
run_command(command, logger)?;
|
||||
} else if !is_redox() {
|
||||
//If patches exists, we have to drop it
|
||||
if patches.len() > 0 {
|
||||
if !can_skip_rebuild {
|
||||
if let Some(_upstream) = upstream {
|
||||
//TODO: set upstream URL (is this needed?)
|
||||
// git remote set-url upstream "$GIT_UPSTREAM" &> /dev/null ||
|
||||
// git remote add upstream "$GIT_UPSTREAM"
|
||||
// git fetch upstream
|
||||
}
|
||||
|
||||
if let Some(rev) = rev {
|
||||
// Check out specified revision
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("checkout").arg(rev);
|
||||
run_command(command, logger)?;
|
||||
} else if !is_redox() {
|
||||
//If patches exists, we have to drop it
|
||||
if patches.len() > 0 {
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("reset").arg("--hard");
|
||||
run_command(command, logger)?;
|
||||
}
|
||||
//TODO: complicated stuff to check and reset branch to origin
|
||||
//TODO: redox can't undestand this (got exit status 1)
|
||||
let mut command = Command::new("bash");
|
||||
command.arg("-c").arg(GIT_RESET_BRANCH);
|
||||
if let Some(branch) = branch {
|
||||
command.env("BRANCH", branch);
|
||||
}
|
||||
command.current_dir(&source_dir);
|
||||
run_command(command, logger)?;
|
||||
}
|
||||
|
||||
if !patches.is_empty() || script.is_some() {
|
||||
// Hard reset
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("reset").arg("--hard");
|
||||
run_command(command, logger)?;
|
||||
}
|
||||
//TODO: complicated stuff to check and reset branch to origin
|
||||
//TODO: redox can't undestand this (got exit status 1)
|
||||
let mut command = Command::new("bash");
|
||||
command.arg("-c").arg(GIT_RESET_BRANCH);
|
||||
if let Some(branch) = branch {
|
||||
command.env("BRANCH", branch);
|
||||
}
|
||||
command.current_dir(&source_dir);
|
||||
run_command(command, logger)?;
|
||||
}
|
||||
|
||||
if !patches.is_empty() || script.is_some() {
|
||||
// Hard reset
|
||||
// Sync submodules URL
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("reset").arg("--hard");
|
||||
command.arg("submodule").arg("sync").arg("--recursive");
|
||||
|
||||
run_command(command, logger)?;
|
||||
|
||||
// Update submodules
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command
|
||||
.arg("submodule")
|
||||
.arg("update")
|
||||
.arg("--init")
|
||||
.arg("--recursive");
|
||||
if shallow_clone {
|
||||
command.arg("--filter=tree:0");
|
||||
}
|
||||
run_command(command, logger)?;
|
||||
|
||||
fetch_apply_patches(recipe_dir, patches, script, &source_dir, logger)?;
|
||||
}
|
||||
|
||||
// Sync submodules URL
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("submodule").arg("sync").arg("--recursive");
|
||||
|
||||
run_command(command, logger)?;
|
||||
|
||||
// Update submodules
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command
|
||||
.arg("submodule")
|
||||
.arg("update")
|
||||
.arg("--init")
|
||||
.arg("--recursive");
|
||||
if shallow_clone {
|
||||
command.arg("--filter=tree:0");
|
||||
}
|
||||
run_command(command, logger)?;
|
||||
|
||||
fetch_apply_patches(recipe_dir, patches, script, &source_dir, logger)?;
|
||||
let (head_rev, _) = get_git_head_rev(&source_dir)?;
|
||||
head_rev
|
||||
}
|
||||
Some(SourceRecipe::Tar {
|
||||
tar,
|
||||
@ -316,6 +366,7 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &PtyOut) -> Result<Path
|
||||
// Move source.tmp to source atomically
|
||||
rename(&source_dir_tmp, &source_dir)?;
|
||||
}
|
||||
blake3.clone().unwrap_or("no_tar_blake3_hash_info".into())
|
||||
}
|
||||
// Local Sources
|
||||
None => {
|
||||
@ -327,17 +378,20 @@ pub fn fetch(recipe_dir: &Path, recipe: &Recipe, logger: &PtyOut) -> Result<Path
|
||||
);
|
||||
create_dir(&source_dir)?;
|
||||
}
|
||||
"local_source".into()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let BuildKind::Cargo {
|
||||
package_path,
|
||||
cargoflags: _,
|
||||
} = &recipe.build.kind
|
||||
} = &recipe.recipe.build.kind
|
||||
{
|
||||
fetch_cargo(&source_dir, package_path.as_ref(), logger)?;
|
||||
}
|
||||
|
||||
fetch_apply_source_info(recipe, ident)?;
|
||||
|
||||
Ok(source_dir)
|
||||
}
|
||||
|
||||
@ -367,7 +421,8 @@ pub(crate) fn fetch_make_symlink(source_dir: &PathBuf, same_as: &String) -> Resu
|
||||
pub(crate) fn fetch_resolve_canon(
|
||||
recipe_dir: &Path,
|
||||
same_as: &String,
|
||||
) -> Result<(PathBuf, Recipe), String> {
|
||||
is_host: bool,
|
||||
) -> Result<CookRecipe, String> {
|
||||
let canon_dir = Path::new(recipe_dir).join(same_as);
|
||||
if canon_dir
|
||||
.to_str()
|
||||
@ -382,12 +437,8 @@ pub(crate) fn fetch_resolve_canon(
|
||||
if !canon_dir.exists() {
|
||||
return Err(format!("'{dir}' is not exists.", dir = canon_dir.display()));
|
||||
}
|
||||
let recipe_path = canon_dir.join("recipe.toml");
|
||||
let recipe_str = fs::read_to_string(&recipe_path)
|
||||
.map_err(|e| format!("unable to read {path}: {e}", path = recipe_path.display()))?;
|
||||
let recipe: Recipe = toml::from_str(&recipe_str)
|
||||
.map_err(|e| format!("Unable to parse {path}: {e}", path = recipe_path.display()))?;
|
||||
Ok((canon_dir, recipe))
|
||||
CookRecipe::from_path(canon_dir.as_path(), true, is_host)
|
||||
.map_err(|e| format!("Unable to load {dir}: {e:?}", dir = canon_dir.display()))
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_extract_tar(
|
||||
@ -455,13 +506,11 @@ fn get_pubkey_url() -> String {
|
||||
|
||||
pub fn fetch_remote(
|
||||
recipe_dir: &Path,
|
||||
recipe: &Recipe,
|
||||
recipe: &CookRecipe,
|
||||
offline_mode: bool,
|
||||
logger: &PtyOut,
|
||||
) -> Result<(), String> {
|
||||
// TODO: allow download to host target (waiting for build server to have them)
|
||||
let target = redoxer::target();
|
||||
let target_dir = create_target_dir(recipe_dir, target)?;
|
||||
let target_dir = create_target_dir(recipe_dir, recipe.target)?;
|
||||
let source_pubkey = target_dir.join("id_ed25519.pub.toml");
|
||||
if !offline_mode {
|
||||
download_wget(&get_pubkey_url(), &source_pubkey, logger)?;
|
||||
@ -469,7 +518,7 @@ pub fn fetch_remote(
|
||||
offline_check_exists(&source_pubkey)?;
|
||||
}
|
||||
|
||||
let packages = recipe.get_packages_list();
|
||||
let packages = recipe.recipe.get_packages_list();
|
||||
|
||||
let name = recipe_dir
|
||||
.file_name()
|
||||
@ -493,6 +542,28 @@ pub fn fetch_remote(
|
||||
offline_check_exists(&source_pkgar)?;
|
||||
offline_check_exists(&source_toml)?;
|
||||
}
|
||||
|
||||
// guaranteed to exist once
|
||||
if package.is_none() {
|
||||
let mut file = File::open(&source_toml)
|
||||
.map_err(|e| format!("Unable to open source.toml: {e:?}"))?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)
|
||||
.map_err(|e| format!("Unable to read source.toml: {e:?}"))?;
|
||||
|
||||
let pkg_toml = pkg::Package::from_toml(&contents)
|
||||
.map_err(|e| format!("Unable to parse source.toml: {e:?}"))?;
|
||||
|
||||
fetch_apply_source_info_from_remote(
|
||||
recipe,
|
||||
&SourceIdentifier {
|
||||
commit_identifier: pkg_toml.commit_identifier.clone(),
|
||||
source_identifier: pkg_toml.source_identifier.clone(),
|
||||
time_identifier: pkg_toml.time_identifier.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -563,3 +634,37 @@ pub(crate) fn fetch_apply_patches(
|
||||
)?;
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_apply_source_info(
|
||||
recipe: &CookRecipe,
|
||||
source_identifier: String,
|
||||
) -> Result<(), String> {
|
||||
let ident = crate::cook::ident::get_ident();
|
||||
let info = pkg::package::SourceIdentifier {
|
||||
commit_identifier: ident.commit.to_string(),
|
||||
time_identifier: ident.time.to_string(),
|
||||
source_identifier: source_identifier,
|
||||
};
|
||||
|
||||
fetch_apply_source_info_from_remote(&recipe, &info)
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_apply_source_info_from_remote(
|
||||
recipe: &CookRecipe,
|
||||
info: &pkg::package::SourceIdentifier,
|
||||
) -> Result<(), String> {
|
||||
let target_dir = create_target_dir(&recipe.dir, recipe.target)?;
|
||||
let source_toml_path = target_dir.join("source_info.toml");
|
||||
serialize_and_write(&source_toml_path, &info)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn fetch_get_source_info(recipe: &CookRecipe) -> Result<SourceIdentifier, String> {
|
||||
let target_dir = recipe.target_dir();
|
||||
let source_toml_path = target_dir.join("source_info.toml");
|
||||
let toml_content = fs::read_to_string(source_toml_path)
|
||||
.map_err(|e| format!("Unable to read source_info.toml: {:?}", e))?;
|
||||
let parsed = toml::from_str(&toml_content)
|
||||
.map_err(|e| format!("Unable to parse source_info.toml: {:?}", e))?;
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
183
src/cook/fs.rs
183
src/cook/fs.rs
@ -285,3 +285,186 @@ pub fn download_wget(url: &str, dest: &PathBuf, logger: &PtyOut) -> Result<(), S
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// get commit rev and return if it's detached or not
|
||||
pub fn get_git_head_rev(dir: &PathBuf) -> Result<(String, bool), String> {
|
||||
let git_head = dir.join(".git/HEAD");
|
||||
let head_str = fs::read_to_string(&git_head)
|
||||
.map_err(|e| format!("unable to read {path}: {e}", path = git_head.display()))?;
|
||||
if head_str.starts_with("ref: ") {
|
||||
let git_ref = dir.join(".git").join(head_str["ref: ".len()..].trim_end());
|
||||
let ref_str = fs::read_to_string(&git_ref)
|
||||
.map_err(|e| format!("unable to read {path}: {e}", path = git_ref.display()))?;
|
||||
Ok((ref_str.trim().to_string(), false))
|
||||
} else {
|
||||
Ok((head_str.trim().to_string(), true))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_git_tag_rev(dir: &PathBuf, tag: &str) -> Result<String, String> {
|
||||
if tag.len() == 40 && tag.chars().all(|f| f.is_ascii_hexdigit()) {
|
||||
return Ok(tag.to_string());
|
||||
}
|
||||
let git_refs = dir.join(".git/packed-refs");
|
||||
let refs_str = fs::read_to_string(&git_refs)
|
||||
.map_err(|e| format!("unable to read {path}: {e}", path = git_refs.display()))?;
|
||||
let expected_comment_part = format!("refs/tags/{tag}");
|
||||
for line in refs_str.lines() {
|
||||
if line.contains(&expected_comment_part) {
|
||||
let sha = line
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.ok_or_else(|| "packed-refs line is malformed.".to_string())?;
|
||||
|
||||
return Ok(sha.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"Could not find a rev tag for {}",
|
||||
expected_comment_part
|
||||
))
|
||||
}
|
||||
|
||||
/// get commit rev after fetch
|
||||
pub fn get_git_fetch_rev(
|
||||
dir: &PathBuf,
|
||||
remote_url: &str,
|
||||
remote_branch: &str,
|
||||
) -> Result<String, String> {
|
||||
let git_fetch_head = dir.join(".git/FETCH_HEAD");
|
||||
|
||||
let fetch_head_content = fs::read_to_string(&git_fetch_head).map_err(|e| {
|
||||
format!(
|
||||
"unable to read {path}: {e}",
|
||||
path = git_fetch_head.display()
|
||||
)
|
||||
})?;
|
||||
|
||||
let expected_comment_part = format!("branch '{}' of {}", remote_branch, remote_url);
|
||||
|
||||
for line in fetch_head_content.lines() {
|
||||
if line.contains(&expected_comment_part) && !line.contains("not-for-merge") {
|
||||
let sha = line
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.ok_or_else(|| "FETCH_HEAD line is malformed.".to_string())?;
|
||||
|
||||
return Ok(sha.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"Could not find a fetch target for tracking {}",
|
||||
expected_comment_part
|
||||
))
|
||||
}
|
||||
|
||||
/// (local_branch_name, remote_branch, remote_name, remote_url)
|
||||
/// -> ("fix_stuff", "master", "origin", "https://gitlab.redox-os.org/willnode/redox")
|
||||
pub fn get_git_remote_tracking(dir: &PathBuf) -> Result<(String, String, String, String), String> {
|
||||
let git_head = dir.join(".git/HEAD");
|
||||
let git_config = dir.join(".git/config");
|
||||
|
||||
let head_content = fs::read_to_string(&git_head)
|
||||
.map_err(|e| format!("unable to read {path}: {e}", path = git_head.display()))?;
|
||||
|
||||
if !head_content.starts_with("ref: ") {
|
||||
let sha = head_content.trim_end().to_string();
|
||||
return Ok((sha, "".to_string(), "".to_string(), "".to_string()));
|
||||
}
|
||||
|
||||
let local_branch_path = head_content["ref: ".len()..].trim_end();
|
||||
let local_branch_name = get_git_branch_name(local_branch_path)?;
|
||||
|
||||
let config_content = fs::read_to_string(&git_config)
|
||||
.map_err(|e| format!("unable to read {path}: {e}", path = git_config.display()))?;
|
||||
|
||||
let branch_section = format!("[branch \"{}\"]", local_branch_name);
|
||||
let mut remote_name: Option<String> = None;
|
||||
let mut remote_branch: Option<String> = None;
|
||||
let mut parsing_branch_section = false;
|
||||
|
||||
for line in config_content.lines().map(|l| l.trim()) {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if line == branch_section {
|
||||
parsing_branch_section = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if parsing_branch_section {
|
||||
if line.starts_with('[') {
|
||||
break;
|
||||
}
|
||||
if line.starts_with("remote = ") {
|
||||
remote_name = Some(line["remote = ".len()..].trim().to_string());
|
||||
}
|
||||
if line.starts_with("merge = ") {
|
||||
remote_branch = Some(get_git_branch_name(line["merge = ".len()..].trim())?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let remote_name_str = remote_name
|
||||
.ok_or_else(|| format!("Branch '{}' is not tracking a remote.", local_branch_name))?;
|
||||
let remote_branch_str = remote_branch.unwrap_or("".into());
|
||||
|
||||
let remote_section = format!("[remote \"{}\"]", remote_name_str);
|
||||
let mut remote_url: Option<String> = None;
|
||||
let mut parsing_remote_section = false;
|
||||
|
||||
for line in config_content.lines().map(|l| l.trim()) {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if line == remote_section {
|
||||
parsing_remote_section = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if parsing_remote_section {
|
||||
if line.starts_with('[') {
|
||||
break;
|
||||
}
|
||||
if line.starts_with("url = ") {
|
||||
let mut url = line["url = ".len()..].trim();
|
||||
url = chop_dot_git(url);
|
||||
remote_url = Some(url.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let remote_url_str = remote_url.ok_or_else(|| {
|
||||
format!(
|
||||
"Could not find URL for remote '{}' in .git/config.",
|
||||
remote_name_str
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok((
|
||||
local_branch_name,
|
||||
remote_branch_str,
|
||||
remote_name_str,
|
||||
remote_url_str,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn chop_dot_git(url: &str) -> &str {
|
||||
if url.ends_with(".git") {
|
||||
return &url[..url.len() - ".git".len()];
|
||||
}
|
||||
url
|
||||
}
|
||||
|
||||
fn get_git_branch_name(local_branch_path: &str) -> Result<String, String> {
|
||||
// TODO: incorrectly handle branch with slashes
|
||||
Ok(local_branch_path
|
||||
.split('/')
|
||||
.last()
|
||||
.ok_or_else(|| format!("Failed to parse branch name of {:?}", local_branch_path))?
|
||||
.to_string())
|
||||
}
|
||||
|
||||
46
src/cook/ident.rs
Normal file
46
src/cook/ident.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use std::{
|
||||
process::{Command, Stdio},
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct IdentifierConfig {
|
||||
pub commit: String,
|
||||
pub time: String,
|
||||
}
|
||||
|
||||
impl IdentifierConfig {
|
||||
fn new() -> Self {
|
||||
let (commit, _) = crate::cook::fs::get_git_head_rev(
|
||||
&std::env::current_dir().expect("unable to get $PWD"),
|
||||
)
|
||||
.expect("Can't read this repository commit");
|
||||
// better than importing heavy deps like chrono
|
||||
let time = String::from_utf8_lossy(
|
||||
&Command::new("date")
|
||||
.arg("-u")
|
||||
.arg("+%Y-%m-%dT%H:%M:%SZ")
|
||||
.stdout(Stdio::piped())
|
||||
.output()
|
||||
.expect("Failed to get current ISO-formatted time")
|
||||
.stdout
|
||||
.trim_ascii(),
|
||||
)
|
||||
.into();
|
||||
IdentifierConfig { commit, time }
|
||||
}
|
||||
}
|
||||
|
||||
static IDENTIFIER_CONFIG: OnceLock<IdentifierConfig> = OnceLock::new();
|
||||
|
||||
pub fn get_ident() -> &'static IdentifierConfig {
|
||||
IDENTIFIER_CONFIG
|
||||
.get()
|
||||
.expect("Identifier is not initialized")
|
||||
}
|
||||
|
||||
pub fn init_ident() {
|
||||
IDENTIFIER_CONFIG
|
||||
.set(IdentifierConfig::new())
|
||||
.expect("Identifier is initialized twice")
|
||||
}
|
||||
@ -7,27 +7,26 @@ use pkg::{Package, PackageName};
|
||||
|
||||
use crate::{
|
||||
blake3::hash_to_hex,
|
||||
cook::{fs::*, pty::PtyOut},
|
||||
cook::{fetch, fs::*, pty::PtyOut},
|
||||
log_to_pty,
|
||||
recipe::{BuildKind, OptionalPackageRecipe, Recipe},
|
||||
recipe::{BuildKind, CookRecipe, OptionalPackageRecipe, Recipe},
|
||||
};
|
||||
|
||||
pub fn package(
|
||||
recipe: &CookRecipe,
|
||||
stage_dirs: &Vec<PathBuf>,
|
||||
target_dir: &Path,
|
||||
name: &PackageName,
|
||||
recipe: &Recipe,
|
||||
auto_deps: &BTreeSet<PackageName>,
|
||||
logger: &PtyOut,
|
||||
) -> Result<(), String> {
|
||||
if recipe.build.kind == BuildKind::None {
|
||||
let name = &recipe.name;
|
||||
let target_dir = &recipe.target_dir();
|
||||
if recipe.recipe.build.kind == BuildKind::None {
|
||||
// metapackages don't have stage dir and optional packages
|
||||
package_toml(
|
||||
target_dir.join("stage.toml"),
|
||||
name,
|
||||
recipe,
|
||||
None,
|
||||
recipe.package.dependencies.clone(),
|
||||
recipe.recipe.package.dependencies.clone(),
|
||||
&auto_deps,
|
||||
)?;
|
||||
return Ok(());
|
||||
@ -50,7 +49,7 @@ pub fn package(
|
||||
|
||||
let stage_modified = modified_all(stage_dirs, modified_dir)?;
|
||||
|
||||
let packages = recipe.get_packages_list();
|
||||
let packages = recipe.recipe.get_packages_list();
|
||||
|
||||
for package in packages {
|
||||
let (stage_dir, package_file, package_meta) = package_stage_paths(package, target_dir);
|
||||
@ -96,11 +95,10 @@ pub fn package(
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
None => recipe.package.dependencies.clone(),
|
||||
None => recipe.recipe.package.dependencies.clone(),
|
||||
};
|
||||
package_toml(
|
||||
package_meta,
|
||||
&name,
|
||||
recipe,
|
||||
Some((Path::new(public_path), &package_file)),
|
||||
package_deps,
|
||||
@ -114,8 +112,7 @@ pub fn package(
|
||||
|
||||
pub fn package_toml(
|
||||
toml_path: PathBuf,
|
||||
name: &PackageName,
|
||||
recipe: &Recipe,
|
||||
recipe: &CookRecipe,
|
||||
package_file: Option<(&Path, &PathBuf)>,
|
||||
mut package_deps: Vec<PackageName>,
|
||||
auto_deps: &BTreeSet<PackageName>,
|
||||
@ -148,15 +145,21 @@ pub fn package_toml(
|
||||
("".into(), 0)
|
||||
};
|
||||
|
||||
let ident_source = fetch::fetch_get_source_info(recipe)?;
|
||||
|
||||
let package = Package {
|
||||
name: name.without_host(),
|
||||
version: package_version(recipe),
|
||||
target: package_target(name).to_string(),
|
||||
name: recipe.name.without_host(),
|
||||
version: package_version(&recipe.recipe),
|
||||
target: recipe.target.to_string(),
|
||||
blake3: hash,
|
||||
// this size will be different once pkgar supports compression
|
||||
network_size: size,
|
||||
storage_size: size,
|
||||
depends: package_deps,
|
||||
commit_identifier: ident_source.commit_identifier,
|
||||
source_identifier: ident_source.source_identifier,
|
||||
time_identifier: ident_source.time_identifier,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
serialize_and_write(&toml_path, &package)?;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user