1
0
Fork 0
mirror of https://codeberg.org/icewind/haze.git synced 2026-06-03 17:14:08 +02:00

git pull command

This commit is contained in:
Robin Appelman 2025-11-21 20:01:20 +01:00
commit 1804a7fd93
7 changed files with 121 additions and 37 deletions

2
Cargo.lock generated
View file

@ -1011,7 +1011,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [ dependencies = [
"hermit-abi 0.5.2", "hermit-abi 0.5.2",
"libc", "libc",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]

30
flake.lock generated
View file

@ -22,11 +22,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1748868585, "lastModified": 1763383859,
"narHash": "sha256-DrrbahOQAwvNM8l5EuGxxkVS7X5/S59zcG0N9ZWQFhk=", "narHash": "sha256-f4sICqPgoDv4yMYOt5j7UikDoqYtt1DgR5ECxpitUHI=",
"owner": "nix-community", "owner": "nix-community",
"repo": "flakelight", "repo": "flakelight",
"rev": "dfbecd12d99c1bf82906521a6a7d5b75d2aa1ca2", "rev": "3cea16878d9e296da0c40e2cc22ebcd15696e70c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -44,26 +44,26 @@
"rust-overlay": "rust-overlay" "rust-overlay": "rust-overlay"
}, },
"locked": { "locked": {
"lastModified": 1747926214, "lastModified": 1763591898,
"narHash": "sha256-e/7klyoQpe9wsYeQIUfm/9Yqa78et24L+nSpsCz937k=", "narHash": "sha256-aHSMj7CIa9EJYxdf05wOWRGp0KRsT/TAox7uwVSdDb8=",
"owner": "icewind1991", "ref": "refs/heads/main",
"repo": "mill-scale", "rev": "2d9b2da2c9f384f93ef977c48f8ee35ce586529b",
"rev": "394979573123e5d4762d29cc78b5e11b3d35cc6b", "revCount": 66,
"type": "github" "type": "git",
"url": "https://codeberg.org/icewind/mill-scale.git"
}, },
"original": { "original": {
"owner": "icewind1991", "type": "git",
"repo": "mill-scale", "url": "https://codeberg.org/icewind/mill-scale.git"
"type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1749086602, "lastModified": 1763622513,
"narHash": "sha256-DJcgJMekoxVesl9kKjfLPix2Nbr42i7cpEHJiTnBUwU=", "narHash": "sha256-1jQnuyu82FpiSxowrF/iFK6Toh9BYprfDqfs4BB+19M=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "4792576cb003c994bd7cc1edada3129def20b27d", "rev": "c58bc7f5459328e4afac201c5c4feb7c818d604b",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -6,7 +6,7 @@
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
mill-scale = { mill-scale = {
url = "github:icewind1991/mill-scale"; url = "git+https://codeberg.org/icewind/mill-scale.git";
inputs.flakelight.follows = "flakelight"; inputs.flakelight.follows = "flakelight";
}; };
}; };

View file

@ -2,7 +2,9 @@
rustPlatform, rustPlatform,
pkg-config, pkg-config,
lib, lib,
git,
}: let }: let
inherit (lib) getExe;
inherit (lib.sources) sourceByRegex; inherit (lib.sources) sourceByRegex;
inherit (builtins) fromTOML readFile; inherit (builtins) fromTOML readFile;
src = sourceByRegex ../. ["Cargo.*" "(src|redis-certificates)(/.*)?"]; src = sourceByRegex ../. ["Cargo.*" "(src|redis-certificates)(/.*)?"];
@ -13,6 +15,8 @@ in
inherit src version; inherit src version;
GIT_BINARY = getExe git;
cargoLock = { cargoLock = {
lockFile = ../Cargo.lock; lockFile = ../Cargo.lock;
outputHashes = { outputHashes = {

View file

@ -115,6 +115,8 @@ pub enum HazeArgs {
)] )]
#[strum(serialize_all = "lowercase")] #[strum(serialize_all = "lowercase")]
pub enum GitOperation { pub enum GitOperation {
/// Pull the active branch in all apps
Pull,
/// Checkout a branch in all apps /// Checkout a branch in all apps
/// ///
/// "main" and "master" can be used interchangeably. /// "main" and "master" can be used interchangeably.
@ -333,6 +335,9 @@ impl HazeArgs {
operation: GitOperation::Checkout { branch }, operation: GitOperation::Checkout { branch },
}) })
} }
"pull" => Ok(HazeArgs::Git {
operation: GitOperation::Pull,
}),
operation => Err(Report::msg(format!("Unknown git operation {operation}"))), operation => Err(Report::msg(format!("Unknown git operation {operation}"))),
} }
} }

View file

@ -1,26 +1,45 @@
use crate::Result; use crate::Result;
use git2::build::CheckoutBuilder; use git2::build::CheckoutBuilder;
use git2::{Branch, BranchType, ObjectType, Repository}; use git2::{Branch, BranchType, ObjectType, Repository, RepositoryState};
use miette::{Context, IntoDiagnostic}; use miette::{Context, IntoDiagnostic};
use std::fs::read_dir; use std::fs::read_dir;
use std::path::Path; use std::path::{Path, PathBuf};
use std::process::Command;
fn find_app_repos(root: impl AsRef<Path>) -> Result<impl Iterator<Item = PathBuf>> {
let apps_dir = root.as_ref().join("apps");
Ok(read_dir(apps_dir)
.into_diagnostic()?
.flatten()
.filter(|app| app.path().join(".git").is_dir())
.map(|app| app.path()))
}
fn longest_app_branch(root: impl AsRef<Path>) -> Result<(usize, usize)> {
Ok(find_app_repos(root)?
.filter_map(|app_dir| {
let app_name = app_dir.file_name()?.to_str()?;
let repo = Repository::init(&app_dir).ok()?;
let branch_name = current_branch_name(&repo)?;
Some((app_name.len(), branch_name.len()))
})
.max()
.unwrap_or_default())
}
pub fn checkout_all<P: AsRef<Path>>(sources_root: P, mut name: &str) -> Result<()> { pub fn checkout_all<P: AsRef<Path>>(sources_root: P, mut name: &str) -> Result<()> {
// "main" and "master" are interchangeable // "main" and "master" are interchangeable
if name == "main" { if name == "main" {
name = "master"; name = "master";
} }
let apps_dir = sources_root.as_ref().join("apps");
for app in read_dir(apps_dir).into_diagnostic()? { for app_dir in find_app_repos(sources_root)? {
let app = app.into_diagnostic()?;
if app.metadata().into_diagnostic()?.is_dir() && app.path().join(".git").is_dir() {
let app_dir = app.path();
let repo = Repository::init(&app_dir) let repo = Repository::init(&app_dir)
.into_diagnostic() .into_diagnostic()
.wrap_err_with(|| format!("Failed to open repository {}", app_dir.display()))?; .wrap_err_with(|| format!("Failed to open repository {}", app_dir.display()))?;
if let Some(branch) = get_branch(&repo, name)? { if let Some(branch) = get_branch(&repo, name)? {
if !branch.is_head() { if !branch.is_head() {
print!("{}", app.file_name().to_string_lossy()); print!("{}", app_dir.file_name().unwrap().to_string_lossy());
if let Err(e) = checkout(&repo, branch) { if let Err(e) = checkout(&repo, branch) {
println!(": {:#}", e); println!(": {:#}", e);
} else { } else {
@ -29,6 +48,59 @@ pub fn checkout_all<P: AsRef<Path>>(sources_root: P, mut name: &str) -> Result<(
} }
}; };
} }
Ok(())
}
fn current_branch_name(repo: &Repository) -> Option<String> {
let (branch, _) = repo
.branches(None)
.ok()?
.flatten()
.find(|(b, _)| b.is_head())?;
branch.name().ok().flatten().map(String::from)
}
const GIT_BINARY: &str = match option_env!("GIT_BINARY") {
Some(git) => git,
None => "git",
};
pub fn pull_all<P: AsRef<Path>>(sources_root: P) -> Result<()> {
let sources_root = sources_root.as_ref();
let (max_app, max_branch) = longest_app_branch(sources_root)?;
for app_dir in find_app_repos(sources_root)? {
let app_name = app_dir.file_name().unwrap().to_string_lossy();
let repo = Repository::init(&app_dir)
.into_diagnostic()
.wrap_err_with(|| format!("Failed to open repository {}", app_dir.display()))?;
let branch_name = current_branch_name(&repo).unwrap_or("unknown".into());
print!(
"{app_name:<app_width$} - {branch_name:<branch_width$}",
app_width = max_app,
branch_width = max_branch
);
if repo.state() != RepositoryState::Clean {
println!(": repository not clean ❌");
continue;
}
let output = Command::new(GIT_BINARY)
.arg("pull")
.current_dir(&app_dir)
.output()
.into_diagnostic()
.wrap_err_with(|| format!("Failed to run git pull for {}", app_dir.display()))?;
if output.status.success() {
println!("");
} else {
println!("");
eprintln!("{}", String::from_utf8_lossy(&output.stderr))
}
} }
Ok(()) Ok(())
} }

View file

@ -5,7 +5,7 @@ use crate::cloud::{Cloud, CloudOptions};
use crate::config::HazeConfig; use crate::config::HazeConfig;
use crate::database::DatabaseFamily; use crate::database::DatabaseFamily;
use crate::exec::{container_logs, exec, exec_tty}; use crate::exec::{container_logs, exec, exec_tty};
use crate::git::checkout_all; use crate::git::{checkout_all, pull_all};
use crate::help::help; use crate::help::help;
use crate::image::update_image; use crate::image::update_image;
use crate::network::clear_networks; use crate::network::clear_networks;
@ -366,6 +366,9 @@ async fn main() -> Result<ExitCode> {
GitOperation::Checkout { branch } => { GitOperation::Checkout { branch } => {
checkout_all(&config.sources_root, &branch)?; checkout_all(&config.sources_root, &branch)?;
} }
GitOperation::Pull => {
pull_all(&config.sources_root)?;
}
}, },
HazeArgs::Env { HazeArgs::Env {
filter, filter,