From 33da3a4a0c80b521ba06f9b2b6989e456359749f Mon Sep 17 00:00:00 2001 From: Yannik Sander Date: Tue, 1 Mar 2022 13:50:18 +0100 Subject: [PATCH] Add group reports for flake-group check (#436) * `nix flake {info -> metadata}` * Fix error variant for io errors * Enable backtrace support for anyhow * Improve error printing * Write error report file * Format workflow file * Use report file * Set non-zero exit status if a group fails * Do not use `local` * Apply suggestions from review * Move exit outside the loop * Fix multi line output * Fix var substitution * Different work around for multi lines --- .github/workflows/check-flake-files.yml | 28 +++++++++++-- flake-info/Cargo.lock | 48 +++++++++++++++++++++++ flake-info/Cargo.toml | 2 +- flake-info/src/bin/flake-info.rs | 47 +++++++++++----------- flake-info/src/commands/nix_flake_info.rs | 4 +- flake-info/src/data/source.rs | 6 +-- 6 files changed, 104 insertions(+), 31 deletions(-) diff --git a/.github/workflows/check-flake-files.yml b/.github/workflows/check-flake-files.yml index 07cdddb..d8c565c 100644 --- a/.github/workflows/check-flake-files.yml +++ b/.github/workflows/check-flake-files.yml @@ -4,11 +4,10 @@ on: workflow_dispatch: pull_request: paths: - - 'flakes/**.toml' + - "flakes/**.toml" jobs: automatic-custom-flakes-check: - runs-on: ubuntu-latest strategy: @@ -34,7 +33,30 @@ jobs: - name: Try importing all custom flakes run: | shopt -s globstar + + had_error=0 + for flake_group in flakes/**/*.toml do - ./result/bin/flake-info group "$flake_group" "$(basename "$flake_group" .toml)" + echo "::group::Group \"$(basename $flake_group .toml)\"" + + ./result/bin/flake-info group "$flake_group" "$(basename "$flake_group" .toml)" --report + + if [[ -f "./report.txt" ]] + then + had_error=1 + + # sic.: + # Workaround for multi line output + report="$(< ./report.txt)" + report="${report//'%'/'%25'}" + report="${report//$'\n'/'%0A'}" + report="${report//$'\r'/'%0D'}" + + echo "::error file=$flake_group::$report" + fi + + echo ::endgroup:: + done + exit $had_error diff --git a/flake-info/Cargo.lock b/flake-info/Cargo.lock index 1fc0212..56dd780 100644 --- a/flake-info/Cargo.lock +++ b/flake-info/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -31,6 +40,9 @@ name = "anyhow" version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf" +dependencies = [ + "backtrace", +] [[package]] name = "async-compression" @@ -62,6 +74,21 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "backtrace" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.11.0" @@ -448,6 +475,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + [[package]] name = "h2" version = "0.3.4" @@ -766,6 +799,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.8.0" @@ -1070,6 +1112,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc_version" version = "0.2.3" diff --git a/flake-info/Cargo.toml b/flake-info/Cargo.toml index cb5e679..0409e99 100644 --- a/flake-info/Cargo.toml +++ b/flake-info/Cargo.toml @@ -12,7 +12,7 @@ serde = {version="1.0", features = ["derive"]} serde_json = "1.0" serde_path_to_error = "0.1.5" toml = "0.5" -anyhow = "1.0" +anyhow = { version= "1.0", features = ["backtrace"] } thiserror = "1.0" structopt = "0.3" command-run = "0.13" diff --git a/flake-info/src/bin/flake-info.rs b/flake-info/src/bin/flake-info.rs index 430a20e..f1ba9e4 100644 --- a/flake-info/src/bin/flake-info.rs +++ b/flake-info/src/bin/flake-info.rs @@ -6,11 +6,13 @@ use flake_info::elastic::{ElasticsearchError, ExistsStrategy}; use flake_info::{commands, elastic}; use log::{debug, error, info, warn}; use sha2::Digest; -use std::fs; use std::path::{Path, PathBuf}; use std::ptr::hash; +use std::{fs, io}; use structopt::{clap::ArgGroup, StructOpt}; use thiserror::Error; +use tokio::fs::File; +use tokio::io::AsyncWriteExt; #[derive(StructOpt, Debug)] #[structopt( @@ -74,6 +76,9 @@ enum Command { #[structopt(long, help = "Whether to gc the store after info or not")] gc: bool, + + #[structopt(long, help = "Whether write an error report about failed packages")] + report: bool, }, } @@ -155,17 +160,6 @@ async fn main() -> Result<()> { let command_result = run_command(args.command, args.kind, &args.extra).await; if let Err(error) = command_result { - match error { - FlakeInfoError::Flake(ref e) - | FlakeInfoError::Nixpkgs(ref e) - | FlakeInfoError::IO(ref e) => { - error!("{}", e); - } - FlakeInfoError::Group(ref el) => { - el.iter().for_each(|e| error!("{}", e)); - } - } - return Err(error.into()); } @@ -187,11 +181,10 @@ enum FlakeInfoError { Flake(anyhow::Error), #[error("Getting nixpkgs info caused an error: {0:?}")] Nixpkgs(anyhow::Error), - #[error("Getting group info caused one or more errors: {0:?}")] - Group(Vec), - + #[error("Some members of the group '{0}' could not be processed: \n {}", .1.iter().enumerate().map(|(n, e)| format!("{}: {:?}", n+1, e)).collect::>().join("\n\n"))] + Group(String, Vec), #[error("Couldn't perform IO: {0}")] - IO(anyhow::Error), + IO(#[from] io::Error), } async fn run_command( @@ -239,8 +232,14 @@ async fn run_command( temp_store, gc, name, + report, } => { - let sources = Source::read_sources_file(&targets).map_err(FlakeInfoError::IO)?; + // if reporting is enabled delete old report + if report && tokio::fs::metadata("report.txt").await.is_ok() { + tokio::fs::remove_file("report.txt").await?; + } + + let sources = Source::read_sources_file(&targets)?; let (exports_and_hashes, errors) = sources .iter() .map(|source| match source { @@ -273,15 +272,19 @@ async fn run_command( .collect::>(); if !errors.is_empty() { + let error = FlakeInfoError::Group(name.clone(), errors); if exports.is_empty() { - return Err(FlakeInfoError::Group(errors)); + return Err(error); } + warn!("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="); - warn!( - "Some group members could not be evaluated: {}", - FlakeInfoError::Group(errors) - ); + warn!("{}", error); warn!("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="); + + if report { + let mut file = File::create("report.txt").await?; + file.write_all(format!("{}", error).as_bytes()).await?; + } } let hash = { diff --git a/flake-info/src/commands/nix_flake_info.rs b/flake-info/src/commands/nix_flake_info.rs index e7cdbbf..a05220b 100644 --- a/flake-info/src/commands/nix_flake_info.rs +++ b/flake-info/src/commands/nix_flake_info.rs @@ -6,13 +6,13 @@ use std::path::PathBuf; use crate::data::Flake; /// Uses `nix` to fetch the provided flake and read general information -/// about it using `nix flake info` +/// about it using `nix flake metadata` pub fn get_flake_info + Display>( flake_ref: T, temp_store: bool, extra: &[String], ) -> Result { - let args = ["flake", "info", "--json", "--no-write-lock-file"].iter(); + let args = ["flake", "metadata", "--json", "--no-write-lock-file"]; let mut command = Command::with_args("nix", args); let command = command.add_arg(flake_ref.as_ref()); if temp_store { diff --git a/flake-info/src/data/source.rs b/flake-info/src/data/source.rs index 91d1233..2267bed 100644 --- a/flake-info/src/data/source.rs +++ b/flake-info/src/data/source.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use std::{ ffi::OsStr, fs::{self, File}, - io::Read, + io::{Read, self}, path::Path, }; @@ -74,8 +74,8 @@ impl Source { } } - pub fn read_sources_file(path: &Path) -> Result> { - let mut file = File::open(path).with_context(|| "Failed to open input file")?; + pub fn read_sources_file(path: &Path) -> io::Result> { + let mut file = File::open(path)?; let mut buf = String::new(); file.read_to_string(&mut buf)?;