Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .repository-projection.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
"projection": "capobara",
"projectionSchemaVersion": 1,
"sourceRepository": "dx-corp/mono",
"sourceSha": "511bd2305f5a92cfbedb91208d7bc0820fda29b5",
"sourceSha": "4ab4845398fdca60f84eb815733b8e2361b4856d",
"destinationRepository": "dx-corp/capobara",
"priorProjectedBase": "01e475cef45ab0d58e52c9c54ece7ff6cff35d03",
"priorProjectedBase": "f7484854022cf9a6b47fa93c445f3b5917e7e54d",
"definitionDigest": "7826c75cbd356dad6c867f2606ebde8b79ef4361353908cf33ada720034ec488",
"toolDigest": "e9fc82741fdf4a797b076d29d51e14aced32368b",
"contentDigest": "6966b9fe44e8b9c45addee6ee807e2fd03993de04f3763fe6b7f3d319db00f8b",
"toolDigest": "f58d71f023a4f0a27d77cb96dcc5348a40816d0b",
"contentDigest": "c364d273ea10128a23de009cd2180dde514640e12bb6494bd36f246afe778bc0",
"publicationEligible": true
}
1 change: 1 addition & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod catalog;
pub mod project;
pub mod run;
pub mod transport;
pub mod vendor;

use std::path::PathBuf;

Expand Down
50 changes: 50 additions & 0 deletions src/cli/vendor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//! `capobara vendor check` — verify a vendored upstream tree against its pin.
//!
//! Read-only by design. Capobara publishes outward and that stays true; this
//! subcommand only answers whether a vendored subtree still matches the
//! upstream commit it claims, and which divergences were declared.

use std::path::PathBuf;

use clap::Args;

use crate::vendor::{self, VendorReport};
use crate::{Error, Result};

#[derive(Args, Debug)]
pub struct VendorCliArgs {
/// The vendor-import definition to check.
#[arg(long)]
pub definition: PathBuf,
/// Mono checkout holding the vendored tree.
#[arg(long)]
pub root: PathBuf,
/// A checkout of the upstream repository containing the pinned commit.
#[arg(long)]
pub upstream: PathBuf,
/// Revision of Mono to read. Defaults to HEAD.
#[arg(long, default_value = "HEAD")]
pub rev: String,
}

pub fn check(args: VendorCliArgs) -> Result<VendorReport> {
let definition = vendor::load(&args.definition)?;
let report = vendor::check(&args.root, &args.rev, &args.upstream, &definition)?;
println!("{}", report.summary());
for divergence in &report.declared {
println!(" declared {divergence:?}");
}
for divergence in &report.drift {
println!(" DRIFT {divergence:?}");
}
if report.clean() {
Ok(report)
} else {
Err(Error::Contract(format!(
"{}: {} undeclared divergence(s) from upstream {}",
report.name,
report.drift.len(),
&report.upstream_commit[..12]
)))
}
}
30 changes: 27 additions & 3 deletions src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{Error, Result};

/// The reviewed process boundary for git: every other module reaches git
/// only through this function (or the helpers below that call it).
fn run(root: &Path, args: &[&str]) -> Result<std::process::Output> {
fn run(root: &Path, args: &[&str], environment: &[(&str, &str)]) -> Result<std::process::Output> {
#[allow(
clippy::disallowed_methods,
reason = "git is the reviewed process boundary for capobara"
Expand All @@ -16,12 +16,17 @@ fn run(root: &Path, args: &[&str]) -> Result<std::process::Output> {
.args(args)
.stdin(Stdio::null())
.env("GIT_TERMINAL_PROMPT", "0")
.envs(environment.iter().copied())
.output()
.map_err(Error::Io)
}

pub fn git_bytes(root: &Path, args: &[&str]) -> Result<Vec<u8>> {
let out = run(root, args)?;
checked_output(root, args, &[])
}

fn checked_output(root: &Path, args: &[&str], environment: &[(&str, &str)]) -> Result<Vec<u8>> {
let out = run(root, args, environment)?;
if !out.status.success() {
return Err(Error::Invalid(format!(
"git {} failed: {}",
Expand All @@ -32,13 +37,32 @@ pub fn git_bytes(root: &Path, args: &[&str]) -> Result<Vec<u8>> {
Ok(out.stdout)
}

/// Scratch repositories retain deterministic authors and ignore host Git config.
#[cfg(test)]
pub(crate) fn test_git_bytes(root: &Path, args: &[&str]) -> Result<Vec<u8>> {
checked_output(
root,
args,
&[
("GIT_AUTHOR_NAME", "t"),
("GIT_AUTHOR_EMAIL", "t@example.invalid"),
("GIT_COMMITTER_NAME", "t"),
("GIT_COMMITTER_EMAIL", "t@example.invalid"),
("GIT_CONFIG_GLOBAL", "/dev/null"),
("GIT_CONFIG_SYSTEM", "/dev/null"),
],
)
}

pub fn git(root: &Path, args: &[&str]) -> Result<String> {
String::from_utf8(git_bytes(root, args)?)
.map_err(|_| Error::Invalid(format!("git {} produced non-UTF-8 output", args.join(" "))))
}

pub fn git_ok(root: &Path, args: &[&str]) -> bool {
run(root, args).map(|o| o.status.success()).unwrap_or(false)
run(root, args, &[])
.map(|o| o.status.success())
.unwrap_or(false)
}

pub fn is_ancestor(root: &Path, ancestor: &str, descendant: &str) -> bool {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ pub mod snapshot;
pub mod tooldigest;
pub mod transport;
pub mod tree;
pub mod vendor;
pub use error::{Error, Result, contract, invalid};
26 changes: 25 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::path::PathBuf;
use std::process::ExitCode;

use clap::{Parser, Subcommand};

Expand All @@ -7,6 +8,7 @@ use capobara::cli::catalog::CatalogCommand;
use capobara::cli::project::ProjectCommand;
use capobara::cli::run::RunArgs;
use capobara::cli::transport::{PrepareArgs, ReportArgs};
use capobara::cli::vendor::VendorCliArgs;

#[derive(Parser)]
#[command(
Expand Down Expand Up @@ -40,6 +42,9 @@ enum Command {
Verify(ProjectCliArgs),
/// Report drift between source and destination.
Check(ProjectCliArgs),
/// Verify a vendored upstream tree against its pinned commit.
#[command(subcommand)]
Vendor(VendorCommand),
/// Clone-side preparation of the destination branch.
Prepare(PrepareArgs),
/// Recheck a prepared projection before publication.
Expand Down Expand Up @@ -106,7 +111,7 @@ fn exit_catalog(result: capobara::Result<()>) -> ! {
}
}

fn main() {
fn main() -> ExitCode {
let cli = Cli::parse();
match cli.command {
Command::Plan(args) => {
Expand Down Expand Up @@ -139,5 +144,24 @@ fn main() {
Command::Preflight(args) => exit_transport(capobara::cli::transport::preflight(args)),
Command::Publish(args) => exit_transport(capobara::cli::transport::publish(args)),
Command::Run(args) => exit_transport(capobara::cli::run::run(args)),
Command::Vendor(VendorCommand::Check(args)) => {
exit_vendor(capobara::cli::vendor::check(args))
}
}
}

#[derive(Subcommand)]
enum VendorCommand {
/// Report divergence between a vendored tree and its pinned upstream commit.
Check(VendorCliArgs),
}

fn exit_vendor(result: capobara::Result<capobara::vendor::VendorReport>) -> ExitCode {
match result {
Ok(_) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("{error}");
ExitCode::from(u8::try_from(error.exit_code()).expect("capobara exit codes fit in u8"))
}
}
}
Loading