Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: NixOS/ofborg
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 040c33717ca8
Choose a base ref
...
head repository: NixOS/ofborg
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 6912713eb5d4
Choose a head ref
  • 14 commits
  • 13 files changed
  • 1 contributor

Commits on Jan 3, 2019

  1. Copy the full SHA
    8333409 View commit details
  2. Copy the full SHA
    a7de079 View commit details
  3. Copy the full SHA
    789e0d8 View commit details
  4. Calculate maintainers

    grahamc committed Jan 3, 2019
    Copy the full SHA
    5b6a387 View commit details
  5. Copy the full SHA
    31f4ec7 View commit details
  6. Copy the full SHA
    293ca1f View commit details
  7. Use nice errors

    grahamc committed Jan 3, 2019
    Copy the full SHA
    810cdbe View commit details
  8. Copy the full SHA
    c058b26 View commit details
  9. Copy the full SHA
    65bec86 View commit details
  10. Copy the full SHA
    9ba201d View commit details
  11. Copy the full SHA
    30372bb View commit details
  12. Copy the full SHA
    07a5a14 View commit details
  13. Fixup formatting

    grahamc committed Jan 3, 2019
    Copy the full SHA
    5c04e88 View commit details
  14. Merge pull request #293 from NixOS/experiment/maintainer-ping

    Experiment/maintainer ping
    grahamc authored Jan 3, 2019
    Copy the full SHA
    6912713 View commit details
1 change: 1 addition & 0 deletions ofborg/src/lib.rs
Original file line number Diff line number Diff line change
@@ -39,6 +39,7 @@ pub mod evalchecker;
pub mod files;
pub mod ghevent;
pub mod locks;
pub mod maintainers;
pub mod message;
pub mod nix;
pub mod notifyworker;
107 changes: 107 additions & 0 deletions ofborg/src/maintainers.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
{ changedattrsjson, changedpathsjson }:
let
pkgs = import ./. {};

changedattrs = builtins.fromJSON (builtins.readFile changedattrsjson);
changedpaths = builtins.fromJSON (builtins.readFile changedpathsjson);

anyMatchingFile = filename:
let
matching = builtins.filter
(changed: pkgs.lib.strings.hasSuffix changed filename)
changedpaths;
in (builtins.length matching) > 0;

anyMatchingFiles = files:
(builtins.length (builtins.filter anyMatchingFile files)) > 0;

enrichedAttrs = builtins.map
(path: {
path = path;
name = builtins.concatStringsSep "." path;
})
changedattrs;

validPackageAttributes = builtins.filter
(pkg:
if (pkgs.lib.attrsets.hasAttrByPath pkg.path pkgs)
then (if (builtins.tryEval (pkgs.lib.attrsets.attrByPath pkg.path null pkgs)).success
then true
else builtins.trace "Failed to access ${pkg.name} even though it exists" false)
else builtins.trace "Failed to locate ${pkg.name}." false
)
enrichedAttrs;

attrsWithPackages = builtins.map
(pkg: pkg // { package = pkgs.lib.attrsets.attrByPath pkg.path null pkgs; })
validPackageAttributes;

attrsWithMaintainers = builtins.map
(pkg: pkg // { maintainers = (pkg.package.meta or {}).maintainers or []; })
attrsWithPackages;

attrsWeCanPing = builtins.filter
(pkg: if (builtins.length pkg.maintainers) > 0
then true
else builtins.trace "Package has no maintainers: ${pkg.name}" false
)
attrsWithMaintainers;

relevantFilenames = drv:
(pkgs.lib.lists.unique
(builtins.map
(pos: pos.file)
(builtins.filter (x: x != null)
[
(builtins.unsafeGetAttrPos "maintainers" (drv.meta or {}))
(builtins.unsafeGetAttrPos "src" drv)
# broken because name is always set by stdenv:
# # A hack to make `nix-env -qa` and `nix search` ignore broken packages.
# # TODO(@oxij): remove this assert when something like NixOS/nix#1771 gets merged into nix.
# name = assert validity.handled; name + lib.optionalString
#(builtins.unsafeGetAttrPos "name" drv)
(builtins.unsafeGetAttrPos "pname" drv)
(builtins.unsafeGetAttrPos "version" drv)
]
)));

attrsWithFilenames = builtins.map
(pkg: pkg // { filenames = relevantFilenames pkg.package; })
attrsWithMaintainers;

attrsWithModifiedFiles = builtins.filter
(pkg: anyMatchingFiles pkg.filenames)
attrsWithFilenames;

listToPing = pkgs.lib.lists.flatten
(builtins.map
(pkg:
builtins.map (maintainer: {
handle = maintainer.github;
packageName = pkg.name;
dueToFiles = pkg.filenames;
})
pkg.maintainers
)
attrsWithModifiedFiles);

byMaintainer = pkgs.lib.lists.foldr
(ping: collector: collector // { "${ping.handle}" = [ { inherit (ping) packageName dueToFiles; } ] ++ (collector."${ping.handle}" or []); })
{}
listToPing;

textForPackages = packages:
pkgs.lib.strings.concatStringsSep ", " (
builtins.map (pkg: pkg.packageName)
packages);

textPerMaintainer = pkgs.lib.attrsets.mapAttrs
(maintainer: packages: "- @${maintainer} for ${textForPackages packages}")
byMaintainer;

packagesPerMaintainer = pkgs.lib.attrsets.mapAttrs
(maintainer: packages:
builtins.map (pkg: pkg.packageName)
packages)
byMaintainer;
in packagesPerMaintainer
172 changes: 172 additions & 0 deletions ofborg/src/maintainers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
use ofborg::nix::Nix;
use std::collections::HashMap;
use std::io::Write;
use std::path::Path;
use tempfile::NamedTempFile;

#[derive(Deserialize, Debug, Eq, PartialEq)]
pub struct ImpactedMaintainers(HashMap<Maintainer, Vec<Package>>);
#[derive(Deserialize, Debug, Eq, PartialEq, Hash)]
struct Maintainer(String);
impl<'a> From<&'a str> for Maintainer {
fn from(name: &'a str) -> Maintainer {
Maintainer(name.to_owned())
}
}
#[derive(Deserialize, Debug, Eq, PartialEq, Hash)]
struct Package(String);
impl<'a> From<&'a str> for Package {
fn from(name: &'a str) -> Package {
Package(name.to_owned())
}
}

#[derive(Debug)]
pub enum CalculationError {
DeserializeError(serde_json::Error),
Io(std::io::Error),
Utf8(std::string::FromUtf8Error),
}
impl From<serde_json::Error> for CalculationError {
fn from(e: serde_json::Error) -> CalculationError {
CalculationError::DeserializeError(e)
}
}
impl From<std::io::Error> for CalculationError {
fn from(e: std::io::Error) -> CalculationError {
CalculationError::Io(e)
}
}
impl From<std::string::FromUtf8Error> for CalculationError {
fn from(e: std::string::FromUtf8Error) -> CalculationError {
CalculationError::Utf8(e)
}
}

impl ImpactedMaintainers {
pub fn calculate(
nix: &Nix,
checkout: &Path,
paths: &[String],
attributes: &[Vec<&str>],
) -> Result<ImpactedMaintainers, CalculationError> {
let mut path_file = NamedTempFile::new()?;
let pathstr = serde_json::to_string(&paths)?;
write!(path_file, "{}", pathstr)?;

let mut attr_file = NamedTempFile::new()?;
let attrstr = serde_json::to_string(&attributes)?;
write!(attr_file, "{}", attrstr)?;

let mut argstrs: HashMap<&str, &str> = HashMap::new();
argstrs.insert("changedattrsjson", attr_file.path().to_str().unwrap());
argstrs.insert("changedpathsjson", path_file.path().to_str().unwrap());

let mut cmd = nix.safely_evaluate_expr_cmd(
&checkout,
include_str!("./maintainers.nix"),
argstrs,
&[path_file.path(), attr_file.path()],
);

let ret = cmd.output()?;

Ok(serde_json::from_str(&String::from_utf8(ret.stdout)?)?)
}
}

impl std::fmt::Display for ImpactedMaintainers {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let d = self
.0
.iter()
.map(|(maintainer, packages)| {
format!(
"{}: {}",
maintainer.0,
packages
.iter()
.map(|pkg| pkg.0.clone())
.collect::<Vec<String>>()
.join(", ")
)
})
.collect::<Vec<String>>()
.join("\n");
write!(f, "{}", d)
}
}

#[cfg(test)]
mod tests {
use super::*;
use checkout::cached_cloner;
use clone::GitClonable;
use ofborg::test_scratch::TestScratch;
use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::process::Stdio;

fn tpath(component: &str) -> PathBuf {
return Path::new(env!("CARGO_MANIFEST_DIR")).join(component);
}

fn make_pr_repo(bare: &Path, co: &Path) -> String {
let output = Command::new("./make-maintainer-pr.sh")
.current_dir(tpath("./test-srcs"))
.arg(bare)
.arg(co)
.stdout(Stdio::piped())
.output()
.expect("building the test PR failed");

let stderr =
String::from_utf8(output.stderr).unwrap_or_else(|err| format!("warning: {}", err));
println!("{}", stderr);

let hash = String::from_utf8(output.stdout).expect("Should just be a hash");
return hash.trim().to_owned();
}

#[test]
fn example() {
let workingdir = TestScratch::new_dir("test-maintainers-example");

let bare = TestScratch::new_dir("test-maintainers-example-bare");
let mk_co = TestScratch::new_dir("test-maintainers-example-co");
let hash = make_pr_repo(&bare.path(), &mk_co.path());

let attributes = vec![vec!["foo", "bar", "packageA"]];

let cloner = cached_cloner(&workingdir.path());
let project = cloner.project("maintainer-test", bare.string());

let working_co = project
.clone_for("testing-maintainer-list".to_owned(), "123".to_owned())
.expect("clone should work");

working_co
.checkout_origin_ref(&OsStr::new("master"))
.unwrap();

let paths = working_co.files_changed_from_head(&hash).unwrap();

working_co.checkout_ref(&OsStr::new(&hash)).unwrap();

let remote = env::var("NIX_REMOTE").unwrap_or("".to_owned());
let nix = Nix::new("x86_64-linux".to_owned(), remote, 1800, None);

let parsed =
ImpactedMaintainers::calculate(&nix, &working_co.clone_to(), &paths, &attributes);

let mut expect = ImpactedMaintainers(HashMap::new());
expect.0.insert(
Maintainer::from("test"),
vec![Package::from("foo.bar.packageA")],
);

assert_eq!(parsed.unwrap(), expect);
}
}
Loading