mirror of
https://gitlab.redox-os.org/redox-os/redox.git
synced 2026-06-17 15:34:18 +08:00
Merge branch 'capture-rev' into 'master'
Implement build system rollback Closes #1799 See merge request redox-os/redox!2137
This commit is contained in:
commit
0acb0392b9
41
mk/repo.mk
41
mk/repo.mk
@ -47,6 +47,14 @@ else
|
||||
$(REPO_BIN) fetch $(COOKBOOK_OPTS) --with-package-deps
|
||||
endif
|
||||
|
||||
# Unfetch and clean all recipes source or binary from filesystem config
|
||||
unfetch: prefix $(FSTOOLS_TAG) FORCE
|
||||
ifeq ($(PODMAN_BUILD),1)
|
||||
$(PODMAN_RUN) make $@
|
||||
else
|
||||
$(REPO_BIN) unfetch $(COOKBOOK_OPTS) --with-package-deps
|
||||
endif
|
||||
|
||||
# Fetch Cargo dependencies for the cookbook tool (needed for REPO_OFFLINE=1 builds)
|
||||
cargo-fetch: FORCE
|
||||
ifeq ($(PODMAN_BUILD),1)
|
||||
@ -281,7 +289,7 @@ cc.%: $(FSTOOLS_TAG) FORCE
|
||||
ifeq ($(PODMAN_BUILD),1)
|
||||
$(PODMAN_RUN) make $@
|
||||
else
|
||||
$(REPO_BIN) change-rule --set-rule= $(foreach f,$(subst $(comma), ,$*),$(f)) --with-package-deps
|
||||
$(REPO_BIN) change-rule --unset $(foreach f,$(subst $(comma), ,$*),$(f)) --with-package-deps
|
||||
endif
|
||||
|
||||
# Set recipe rule to "binary" then invoke clean and rebuild
|
||||
@ -294,6 +302,37 @@ scr.%: $(FSTOOLS_TAG) FORCE
|
||||
$(MAKE) sc.$*
|
||||
$(MAKE) r.$*,--with-package-deps
|
||||
|
||||
# Save current git rev for next recipe fetch, locking git recipes frozen in time
|
||||
repo-lock: $(FSTOOLS_TAG) FORCE
|
||||
ifeq ($(PODMAN_BUILD),1)
|
||||
$(PODMAN_RUN) make $@
|
||||
else
|
||||
$(REPO_BIN) capture-rev $(COOKBOOK_OPTS) --with-package-deps
|
||||
endif
|
||||
|
||||
# Undo repo-lock, allowing git recipes to get updated by next recipe fetch
|
||||
repo-unlock: $(FSTOOLS_TAG) FORCE
|
||||
ifeq ($(PODMAN_BUILD),1)
|
||||
$(PODMAN_RUN) make $@
|
||||
else
|
||||
$(REPO_BIN) capture-rev $(COOKBOOK_OPTS) --unset --with-package-deps
|
||||
endif
|
||||
|
||||
# Like repo-lock, but also checking out the specified git rev.
|
||||
# Revert this operation by "git checkout master" "make pull" then "make repo-unlock".
|
||||
# Will not work if rolling back to a commit before 2026.
|
||||
repo-rollback.%: $(FSTOOLS_TAG) FORCE
|
||||
ifeq ($(PODMAN_BUILD),1)
|
||||
$(PODMAN_RUN) make $@
|
||||
# have to be done otherwise podman will rebuild
|
||||
touch $(CONTAINER_TAG)
|
||||
else
|
||||
git checkout $*
|
||||
$(REPO_BIN) capture-rev $(COOKBOOK_OPTS) --rollback --with-package-deps
|
||||
endif
|
||||
# have to be done otherwise cookbook will rebuild
|
||||
touch $(FSTOOLS_TAG)
|
||||
|
||||
export DEBUG_BIN?=
|
||||
|
||||
# Debug a statically linked program with gdbgui, for example: debug.drivers-initfs DEBUG_BIN=pcid
|
||||
|
||||
110
src/bin/repo.rs
110
src/bin/repo.rs
@ -2,7 +2,10 @@ use ansi_to_tui::IntoText;
|
||||
use cookbook::config::{CookConfig, CookLockOpt, get_config, init_config};
|
||||
use cookbook::cook::cook_build::{build, get_stage_dirs, remove_stage_dir};
|
||||
use cookbook::cook::fetch::{FetchResult, fetch, fetch_offline};
|
||||
use cookbook::cook::fs::{create_dir, create_target_dir, remove_all, run_command};
|
||||
use cookbook::cook::fs::{
|
||||
create_dir, create_target_dir, get_git_commit_date, get_git_head_rev, get_git_rev_before_date,
|
||||
remove_all, run_command,
|
||||
};
|
||||
use cookbook::cook::package::{package, package_handle_push};
|
||||
use cookbook::cook::pty::{PtyOut, UnixSlavePty, flush_pty, setup_pty, write_to_pty};
|
||||
use cookbook::cook::script::KILL_ALL_PID;
|
||||
@ -58,15 +61,15 @@ const REPO_HELP_STR: &str = r#"
|
||||
common flags:
|
||||
--cookbook=<cookbook_dir> the "recipes" folder, default to $PWD/recipes
|
||||
--repo=<repo_dir> the "repo" folder, default to $PWD/repo
|
||||
--sysroot=<sysroot_dir> the "root" folder used for "push" command
|
||||
For Redox, defaults to "/", else default to $PWD/sysroot
|
||||
--with-package-deps include package deps (always implied in push command)
|
||||
--all apply to all recipes in <cookbook_dir>
|
||||
--category=<category> apply to all recipes in <cookbook_dir>/<category>
|
||||
--filesystem=<filesystem> override recipes config using installer file
|
||||
--repo-binary override recipes config to use repo_binary
|
||||
--sysroot=<sysroot_dir> used in "push", the "root" dir, default to $PWD/sysroot
|
||||
--set-rule=<rule> used in "change-rule", set wanted config rule
|
||||
--rollback=<rev> used in "capture-rev", rollback to a commit instead
|
||||
--rollback used in "capture-rev", allow git to rollback
|
||||
--unset used in "capture-rev" and "change-rule", unset locks
|
||||
|
||||
cook env and their defaults:
|
||||
CI= set to any value to disable TUI
|
||||
@ -91,8 +94,9 @@ struct CliConfig {
|
||||
logs_dir: Option<PathBuf>,
|
||||
category: Option<PathBuf>,
|
||||
filesystem: Option<redox_installer::Config>,
|
||||
rollback: Option<String>,
|
||||
set_rule: Option<String>,
|
||||
unset: bool,
|
||||
with_rollback: bool,
|
||||
with_package_deps: bool,
|
||||
all: bool,
|
||||
cook: CookConfig,
|
||||
@ -187,16 +191,13 @@ impl CliConfig {
|
||||
None
|
||||
},
|
||||
category: None,
|
||||
sysroot_dir: if cfg!(target_os = "redox") {
|
||||
PathBuf::from("/")
|
||||
} else {
|
||||
current_dir.join("sysroot")
|
||||
},
|
||||
sysroot_dir: current_dir.join("sysroot"),
|
||||
with_package_deps: false,
|
||||
cook: get_config().cook.clone(),
|
||||
all: false,
|
||||
unset: false,
|
||||
filesystem: None,
|
||||
rollback: None,
|
||||
with_rollback: false,
|
||||
set_rule: None,
|
||||
})
|
||||
}
|
||||
@ -262,11 +263,8 @@ fn main_inner() -> Result<()> {
|
||||
if command == CliCommand::Push {
|
||||
return handle_push(&recipes, &config);
|
||||
}
|
||||
if command == CliCommand::ChangeRule {
|
||||
return handle_change_rule(&recipes, &config);
|
||||
}
|
||||
if command == CliCommand::CaptureRev {
|
||||
return handle_capture_rev(&recipes, &config);
|
||||
if matches!(command, CliCommand::ChangeRule | CliCommand::CaptureRev) {
|
||||
return handle_change_rule(&recipes, &config, &command);
|
||||
}
|
||||
|
||||
let verbose = config.cook.verbose;
|
||||
@ -454,7 +452,6 @@ fn parse_args(args: Vec<String>) -> Result<(CliConfig, CliCommand, Vec<CookRecip
|
||||
"--sysroot" => config.sysroot_dir = PathBuf::from(value),
|
||||
"--category" => config.category = Some(PathBuf::from(value)),
|
||||
"--set-rule" => config.set_rule = Some(value.into()),
|
||||
"--rollback" => config.rollback = Some(value.into()),
|
||||
"--filesystem" => {
|
||||
config.filesystem = Some({
|
||||
let r = redox_installer::Config::from_file(&PathBuf::from(value));
|
||||
@ -470,6 +467,8 @@ fn parse_args(args: Vec<String>) -> Result<(CliConfig, CliCommand, Vec<CookRecip
|
||||
match arg.as_str() {
|
||||
"--repo-binary" => override_filesystem_repo_binary = true,
|
||||
"--with-package-deps" => config.with_package_deps = true,
|
||||
"--rollback" => config.with_rollback = true,
|
||||
"--unset" => config.unset = true,
|
||||
"--all" => config.all = true,
|
||||
_ => bail_options_err!("Error: Unknown flag: {}", arg),
|
||||
}
|
||||
@ -718,7 +717,7 @@ fn parse_args(args: Vec<String>) -> Result<(CliConfig, CliCommand, Vec<CookRecip
|
||||
.flatten()
|
||||
{
|
||||
if let Some(SourceRecipe::Git { rev, branch, .. }) = &mut recipe.recipe.source {
|
||||
*rev = Some(gitrev);
|
||||
*rev = Some(gitrev.clone());
|
||||
*branch = None;
|
||||
} else {
|
||||
println!(
|
||||
@ -726,6 +725,7 @@ fn parse_args(args: Vec<String>) -> Result<(CliConfig, CliCommand, Vec<CookRecip
|
||||
recipe.name.as_str()
|
||||
);
|
||||
}
|
||||
recipe.pinned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -968,53 +968,87 @@ fn handle_tree(recipes: &Vec<CookRecipe>, is_build_tree: bool, _config: &CliConf
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_change_rule(recipes: &Vec<CookRecipe>, config: &CliConfig) -> Result<()> {
|
||||
fn handle_change_rule(
|
||||
recipes: &Vec<CookRecipe>,
|
||||
config: &CliConfig,
|
||||
command: &CliCommand,
|
||||
) -> Result<()> {
|
||||
let mut lock = get_config().recipe_lock.clone();
|
||||
let is_pruning = config.set_rule.as_ref().is_some_and(|s| s.is_empty());
|
||||
let cookbook_date = get_git_commit_date(&PathBuf::from("."))?;
|
||||
let is_change_rule = matches!(command, CliCommand::ChangeRule);
|
||||
let is_capture_rev = matches!(command, CliCommand::CaptureRev);
|
||||
for recipe in recipes {
|
||||
if recipe.name.is_host() {
|
||||
if is_change_rule && recipe.name.is_host() {
|
||||
// host packages will always be "source" so it's pointless to change their rule
|
||||
continue;
|
||||
}
|
||||
if is_capture_rev && !matches!(recipe.recipe.source, Some(SourceRecipe::Git { .. })) {
|
||||
continue;
|
||||
}
|
||||
let recipe_name = recipe.name.without_prefix();
|
||||
let mut recipe_lock = lock.get(recipe_name).cloned().unwrap_or_default();
|
||||
let cached = if is_pruning {
|
||||
recipe_lock.fsrule.take().is_none()
|
||||
} else {
|
||||
let new_rule = config
|
||||
.set_rule
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| recipe.rule.clone());
|
||||
let cached = if is_change_rule {
|
||||
if config.unset {
|
||||
recipe_lock.fsrule.take().is_none()
|
||||
} else {
|
||||
let new_rule = config
|
||||
.set_rule
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| recipe.rule.clone());
|
||||
|
||||
let old_rule = recipe_lock.fsrule.replace(new_rule.clone());
|
||||
old_rule == Some(new_rule)
|
||||
let old_rule = recipe_lock.fsrule.replace(new_rule.clone());
|
||||
old_rule == Some(new_rule)
|
||||
}
|
||||
} else if is_capture_rev {
|
||||
if config.unset {
|
||||
recipe_lock.gitrev.take().is_none()
|
||||
} else {
|
||||
let source_dir = recipe.dir.join("source");
|
||||
let rev = if config.with_rollback {
|
||||
// invoke fetch as the git tracking can be different
|
||||
match handle_fetch(recipe, config, false, &None) {
|
||||
Ok(_) => get_git_rev_before_date(&source_dir, &cookbook_date),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
} else {
|
||||
get_git_head_rev(&source_dir).map(|r| r.0)
|
||||
};
|
||||
match rev {
|
||||
Ok(rev) => {
|
||||
let old_rev = recipe_lock.gitrev.replace(rev.clone());
|
||||
old_rev == Some(rev)
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Skipping {}: {e}", recipe.name.as_str());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
if recipe_lock.is_empty() {
|
||||
lock.remove(recipe_name);
|
||||
} else {
|
||||
lock.insert(recipe_name.to_string(), recipe_lock);
|
||||
}
|
||||
let clean_cached = if !cached {
|
||||
let clean_cached = if !cached && is_change_rule {
|
||||
handle_clean(recipe, config, &CliCommand::Clean)?
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if cached && clean_cached {
|
||||
print_cached(&CliCommand::ChangeRule, &recipe.name);
|
||||
print_cached(command, &recipe.name);
|
||||
} else {
|
||||
print_success(&CliCommand::ChangeRule, &recipe.name);
|
||||
print_success(command, &recipe.name);
|
||||
}
|
||||
}
|
||||
CookLockOpt { recipes: lock }.save();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_capture_rev(_recipes: &Vec<CookRecipe>, _config: &CliConfig) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
//
|
||||
// ------------- TUI SPECIFIC CODE -------------------
|
||||
//
|
||||
|
||||
@ -186,6 +186,7 @@ pub fn fetch(recipe: &CookRecipe, check_source: bool, logger: &PtyOut) -> Result
|
||||
}) => {
|
||||
//TODO: use libgit?
|
||||
let shallow_clone = *shallow_clone == Some(true);
|
||||
let mut fetch_is_ran = false;
|
||||
let cached = if !source_dir.is_dir() {
|
||||
// Create source.tmp
|
||||
let source_dir_tmp = recipe_dir.join("source.tmp");
|
||||
@ -234,18 +235,6 @@ pub fn fetch(recipe: &CookRecipe, check_source: bool, logger: &PtyOut) -> Result
|
||||
);
|
||||
}
|
||||
|
||||
// Reset origin
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("remote").arg("set-url").arg("origin").arg(git);
|
||||
run_command(command, logger)?;
|
||||
|
||||
// Fetch origin
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(&source_dir);
|
||||
command.arg("fetch").arg("origin");
|
||||
run_command(command, logger)?;
|
||||
|
||||
let (head_rev, detached_rev) = get_git_head_rev(&source_dir)?;
|
||||
match (rev, detached_rev) {
|
||||
(Some(rev), true) => {
|
||||
@ -274,6 +263,8 @@ pub fn fetch(recipe: &CookRecipe, check_source: bool, logger: &PtyOut) -> Result
|
||||
} else if remote_name != "origin" || &remote_url != chop_dot_git(git) {
|
||||
false
|
||||
} else {
|
||||
git_run_fetch(logger, &source_dir, git)?;
|
||||
fetch_is_ran = true;
|
||||
match get_git_fetch_rev(&source_dir, &remote_url, &remote_branch) {
|
||||
Ok(fetch_rev) => fetch_rev == head_rev,
|
||||
Err(e) => {
|
||||
@ -288,6 +279,9 @@ pub fn fetch(recipe: &CookRecipe, check_source: bool, logger: &PtyOut) -> Result
|
||||
};
|
||||
|
||||
if !cached {
|
||||
if !fetch_is_ran {
|
||||
git_run_fetch(logger, &source_dir, git)?;
|
||||
}
|
||||
if let Some(_upstream) = upstream {
|
||||
//TODO: set upstream URL (is this needed?)
|
||||
// git remote set-url upstream "$GIT_UPSTREAM" &> /dev/null ||
|
||||
@ -459,6 +453,18 @@ pub fn fetch(recipe: &CookRecipe, check_source: bool, logger: &PtyOut) -> Result
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn git_run_fetch(logger: &PtyOut, source_dir: &PathBuf, git: &String) -> Result<()> {
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(source_dir);
|
||||
command.arg("remote").arg("set-url").arg("origin").arg(git);
|
||||
run_command(command, logger)?;
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C").arg(source_dir);
|
||||
command.arg("fetch").arg("origin");
|
||||
run_command(command, logger)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn manual_git_recursive_submodule(
|
||||
logger: &PtyOut,
|
||||
source_dir: &PathBuf,
|
||||
|
||||
@ -476,3 +476,35 @@ fn get_git_branch_name(local_branch_path: &str) -> Result<String> {
|
||||
))?
|
||||
.to_string())
|
||||
}
|
||||
|
||||
pub fn get_git_commit_date(dir: &PathBuf) -> Result<String> {
|
||||
let mut git = process::Command::new("git");
|
||||
git.args(["log", "-1", "--date=iso-strict-local", "--format=%ad"]);
|
||||
git.env("TZ", "UTC");
|
||||
git.current_dir(dir);
|
||||
git.stdout(Stdio::piped());
|
||||
|
||||
git.output()
|
||||
.map_err(wrap_io_err!("Executing git log"))
|
||||
.map(|s| String::from_utf8_lossy(&s.stdout).trim().to_string())
|
||||
}
|
||||
|
||||
pub fn get_git_rev_before_date(dir: &PathBuf, date: &str) -> Result<String> {
|
||||
let mut git = process::Command::new("git");
|
||||
git.args(["rev-list", "-n", "1", &format!("--before={}", date), "HEAD"]);
|
||||
git.current_dir(dir);
|
||||
git.stdout(Stdio::piped());
|
||||
|
||||
let output = git
|
||||
.output()
|
||||
.map_err(wrap_io_err!("Executing git rev-list"))?;
|
||||
let rev = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
|
||||
if rev.is_empty() {
|
||||
return Err(Error::from(format!(
|
||||
"No commit found before {} in {:?}",
|
||||
date, dir
|
||||
)));
|
||||
}
|
||||
Ok(rev)
|
||||
}
|
||||
|
||||
@ -187,6 +187,8 @@ pub struct CookRecipe {
|
||||
/// If false, it's listed on install config
|
||||
pub is_deps: bool,
|
||||
pub rule: String,
|
||||
/// whether if this recipe is pinned from cookbook.lock
|
||||
pub pinned: bool,
|
||||
}
|
||||
|
||||
impl Recipe {
|
||||
@ -244,6 +246,7 @@ impl CookRecipe {
|
||||
target,
|
||||
is_deps: false,
|
||||
rule: "".into(),
|
||||
pinned: false,
|
||||
})
|
||||
}
|
||||
|
||||
@ -426,6 +429,10 @@ impl CookRecipe {
|
||||
}
|
||||
|
||||
pub fn reload_recipe(&mut self) -> Result<(), PackageError> {
|
||||
if self.pinned {
|
||||
// TODO: print?
|
||||
return Ok(());
|
||||
}
|
||||
self.recipe = Self::from_path(&self.dir, true, self.name.is_host())?.recipe;
|
||||
let _ = self.apply_filesystem_config(&self.rule.clone());
|
||||
Ok(())
|
||||
|
||||
Loading…
Reference in New Issue
Block a user