Skip to content

Commit

Permalink
mass-rebuilder: Redo the evaluator
Browse files Browse the repository at this point in the history
The code that figures out the changed packages now uses the new code
from nixpkgs that performs way better. This comes at the cost of
performance statistics which were removed because they are now handled
by the GitHub action.
  • Loading branch information
dasJ committed Dec 29, 2024
1 parent 24e678d commit 371fc6e
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 766 deletions.
1 change: 0 additions & 1 deletion ofborg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ pub mod maintainers;
pub mod message;
pub mod nix;
pub mod nixenv;
pub mod nixstats;
pub mod notifyworker;
pub mod outpathdiff;
pub mod stats;
Expand Down
102 changes: 36 additions & 66 deletions ofborg/src/nixenv.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
//! Evaluates the expression like Hydra would, with regards to
//! architecture support and recursed packages.
use crate::nix;
use crate::nixstats::EvaluationStats;
use crate::outpathdiff;

use std::fs::{self, File};
use std::io::{self, BufRead, BufReader, Read, Seek, SeekFrom, Write};
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read};
use std::path::PathBuf;

use tracing::warn;

pub struct HydraNixEnv {
path: PathBuf,
nix: nix::Nix,
Expand All @@ -25,15 +22,15 @@ impl HydraNixEnv {
}
}

pub fn execute_with_stats(
&self,
) -> Result<(outpathdiff::PackageOutPaths, EvaluationStats), Error> {
self.place_nix()?;
let (status, stdout, stderr, stats) = self.run_nix_env();
self.remove_nix()?;
pub fn execute(&self) -> Result<outpathdiff::PackageOutPaths, Error> {
let (status, stdout, stderr) = self.run_nix_env();
println!("{status:#?}, {stdout:#?}");

if status {
let outpaths = outpathdiff::parse_lines(&mut BufReader::new(stdout));
let outpaths = outpathdiff::parse_json(
File::open(self.outpaths_json()).map_err(|e| Error::Io(e))?,
)
.map_err(|e| Error::Internal(e))?;

let evaluation_errors = BufReader::new(stderr)
.lines()
Expand All @@ -47,82 +44,57 @@ impl HydraNixEnv {
return Err(Error::UncleanEvaluation(evaluation_errors));
}

let mut stats = stats.expect("Failed to open stats path, not created?");
let stats = serde_json::from_reader(&mut stats).map_err(|err| {
let seek = stats.seek(SeekFrom::Start(0));

Error::StatsParse(stats, seek, err)
})?;
Ok((outpaths, stats))
Ok(outpaths)
} else {
Err(Error::CommandFailed(stderr))
}
}

/// Put outpaths.nix in to the project root, which is what
/// emulates Hydra's behavior.
fn place_nix(&self) -> Result<(), Error> {
let outpath = self.outpath_nix_path();
let mut file = File::create(&outpath).map_err(|e| Error::CreateFile(outpath, e))?;

file.write_all(include_bytes!("outpaths.nix"))
.map_err(|e| Error::WriteFile(file, e))
}

fn remove_nix(&self) -> Result<(), Error> {
let outpath_nix = self.outpath_nix_path();
let outpath_stats = self.outpath_stats_path();

fs::remove_file(&outpath_nix).map_err(|e| Error::RemoveFile(outpath_nix, e))?;

// Removing the stats file can fail if `nix` itself errored, for example
// when it fails to evaluate something. In this case, we can ignore (but
// warn about) the error.
if let Err(e) = fs::remove_file(&outpath_stats) {
warn!("Failed to remove file {:?}: {:?}", outpath_stats, e)
}

Ok(())
}

fn outpath_nix_path(&self) -> PathBuf {
self.path.join(".gc-of-borg-outpaths.nix")
}

fn outpath_stats_path(&self) -> PathBuf {
self.path.join(".gc-of-borg-stats.json")
fn outpaths_json(&self) -> PathBuf {
self.path.join("result/outpaths.json")
}

fn run_nix_env(&self) -> (bool, File, File, Result<File, io::Error>) {
fn run_nix_env(&self) -> (bool, File, File) {
let check_meta = if self.check_meta { "true" } else { "false" };

let mut cmd = self.nix.safe_command(
&nix::Operation::QueryPackagesOutputs,
let cmd = self.nix.safe_command(
&nix::Operation::Build,
&self.path,
&[
"-f",
".gc-of-borg-outpaths.nix",
"ci",
"-A",
"eval.full",
"--max-jobs",
"1",
"--cores",
"4",
"--arg",
"nixpkgs",
self.path.to_str().unwrap(),
"--arg",
"chunkSize",
"10000",
"--arg",
"evalSystems",
"[\"x86_64-linux\"]",
"--arg",
"checkMeta",
check_meta,
"--out-link",
&format!("{}/result", self.path.to_str().unwrap()),
],
&[],
);
cmd.env("NIX_SHOW_STATS", "1");
cmd.env("NIX_SHOW_STATS_PATH", self.outpath_stats_path());

let (status, stdout, stderr) = self.nix.run_stderr_stdout(cmd);
let stats = File::open(self.outpath_stats_path());

(status, stdout, stderr, stats)
(status, stdout, stderr)
}
}

pub enum Error {
Io(io::Error),
CreateFile(PathBuf, io::Error),
RemoveFile(PathBuf, io::Error),
WriteFile(File, io::Error),
Internal(Box<dyn std::error::Error>),
CommandFailed(File),
StatsParse(File, Result<u64, io::Error>, serde_json::Error),
UncleanEvaluation(Vec<String>),
Expand All @@ -138,9 +110,7 @@ impl Error {
pub fn display(self) -> String {
match self {
Error::Io(err) => format!("Failed during the setup of executing nix-env: {err:?}"),
Error::CreateFile(path, err) => format!("Failed to create file {path:?}: {err:?}"),
Error::RemoveFile(path, err) => format!("Failed to remove file {path:?}: {err:?}"),
Error::WriteFile(file, err) => format!("Failed to write to file '{file:?}': {err:?}"),
Error::Internal(err) => format!("Internal error: {err:?}"),
Error::CommandFailed(mut fd) => {
let mut buffer = Vec::new();
let read_result = fd.read_to_end(&mut buffer);
Expand Down
Loading

0 comments on commit 371fc6e

Please sign in to comment.