Skip to content

Commit

Permalink
Merge pull request #58 from NixOS/automatically-schedule
Browse files Browse the repository at this point in the history
Automatically schedule builds for trusted users
  • Loading branch information
grahamc authored Feb 3, 2018
2 parents eaeb97f + f37e249 commit cda4fa9
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 41 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@
2. be gentle, preferably don't run mass rebuilds / massive builds like
chromium on it

## Automatic Building

Users who are _trusted_ (see: ./config.public.json) will have their
PRs automatically trigger builds if their commits follow the
well-defined format of Nixpkgs. Example messages and the builds:

|Message|Automatic Build|
|-|-|
|`vim: 1.0.0 -> 2.0.0`|`vim`|
|`python3Packages.requests,python2Packages.requests: 1.0.0 -> 2.0.0`|`python3Packages.requests`, `python2Packages.requests`|
|`python{2,3}Packages.requests: 1.0.0 -> 2.0.0`|_nothing_|



## Commands

The comment parser is line-based, so comments can be interleaved with
Expand Down
14 changes: 14 additions & 0 deletions ofborg/src/acl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ impl ACL {
};
}

pub fn build_job_destinations_for_user_repo(&self, user: &str, repo: &str)
-> Vec<(Option<String>, Option<String>)> {
if self.can_build_unrestricted(user, repo) {
vec![(Some("build-jobs".to_owned()), None)]
} else if self.can_build_restricted(user, repo) {
vec![
(None, Some("build-inputs-x86_64-linux".to_owned())),
(None, Some("build-inputs-aarch64-linux".to_owned())),
]
} else {
vec![]
}
}

pub fn can_build_restricted(&self, user: &str, repo: &str) -> bool {
if repo.to_lowercase() != "nixos/nixpkgs" {
return false;
Expand Down
1 change: 1 addition & 0 deletions ofborg/src/bin/mass-rebuilder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ fn main() {
cloner,
nix,
cfg.github(),
cfg.acl(),
cfg.runner.identity.clone(),
events,
);
Expand Down
75 changes: 75 additions & 0 deletions ofborg/src/checkout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,32 @@ impl CachedProjectCo {
return Err(Error::new(ErrorKind::Other, "Failed to merge"));
}
}


pub fn commit_messages_from_head(&self, commit: &str) -> Result<Vec<String>, Error> {
let mut lock = self.lock()?;

let result = Command::new("git")
.arg("log")
.arg("--format=format:%s")
.arg(format!("HEAD..{}", commit))
.current_dir(self.clone_to())
.output()?;

lock.unlock();

if result.status.success() {
return Ok(
String::from_utf8_lossy(&result.stdout)
.lines()
.map(|l| l.to_owned())
.collect()
);
} else {
return Err(Error::new(ErrorKind::Other,
String::from_utf8_lossy(&result.stderr).to_lowercase()));
}
}
}

impl clone::GitClonable for CachedProjectCo {
Expand Down Expand Up @@ -197,3 +223,52 @@ impl clone::GitClonable for CachedProject {
return vec![OsStr::new("--bare")];
}
}


#[cfg(test)]
mod tests {
use super::*;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use ofborg::test_scratch::TestScratch;

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-pr.sh")
.current_dir(tpath("./test-srcs"))
.arg(bare)
.arg(co)
.stderr(Stdio::null())
.stdout(Stdio::piped())
.output()
.expect("building the test PR failed");
let hash = String::from_utf8(output.stdout).expect("Should just be a hash");
return hash.trim().to_owned();
}

#[test]
pub fn test_commit_msg_list() {
let workingdir = TestScratch::new_dir("test-test-commit-msg-list");

let bare = TestScratch::new_dir("bare-commit-messages");
let mk_co = TestScratch::new_dir("mk-commit-messages");
let hash = make_pr_repo(&bare.path(), &mk_co.path());

let cloner = cached_cloner(&workingdir.path());
let project = cloner.project("commit-msg-list".to_owned(), bare.string());
let working_co = project.clone_for("testing-commit-msgs".to_owned(), "123".to_owned()).expect("clone should work");
working_co.checkout_origin_ref(OsStr::new("master"));

let expect: Vec<String> = vec![
"check out this cool PR".to_owned()
];

assert_eq!(
working_co.commit_messages_from_head(&hash).expect("fetching messages should work"),
expect
);
}
}
33 changes: 31 additions & 2 deletions ofborg/src/message/buildjob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,37 @@ pub struct BuildJob {
pub pr: Pr,
pub subset: Option<Subset>,
pub attrs: Vec<String>,
pub logs: Option<(Option<String>, Option<String>)>, // (Exchange, Routing Key)
pub statusreport: Option<(Option<String>, Option<String>)>, // (Exchange, Routing Key)
pub logs: Option<ExchangeQueue>, // (Exchange, Routing Key)
pub statusreport: Option<ExchangeQueue>, // (Exchange, Routing Key)
}

pub type ExchangeQueue = (Option<Exchange>, Option<RoutingKey>);
type Exchange = String;
type RoutingKey = String;

impl BuildJob {
pub fn new(repo: Repo,
pr: Pr,
subset: Subset,
attrs: Vec<String>,
logs: Option<ExchangeQueue>,
statusreport: Option<ExchangeQueue>) -> BuildJob {

let logbackrk = format!(
"{}.{}",
repo.full_name.clone(),
pr.number,
).to_lowercase();

BuildJob {
repo: repo,
pr: pr,
subset: Some(subset),
attrs: attrs,
logs: Some(logs.unwrap_or((Some("logs".to_owned()), Some(logbackrk)))),
statusreport: Some(statusreport.unwrap_or((Some("build-results".to_owned()), None))),
}
}
}

pub fn from(data: &Vec<u8>) -> Result<BuildJob, serde_json::error::Error> {
Expand Down
5 changes: 3 additions & 2 deletions ofborg/src/message/massrebuildjob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ impl Actions {
return vec![worker::Action::Ack];
}

pub fn done(&mut self, _job: &MassRebuildJob) -> worker::Actions {
return vec![worker::Action::Ack];
pub fn done(&mut self, _job: &MassRebuildJob, mut response: worker::Actions) -> worker::Actions {
response.push(worker::Action::Ack);
return response;
}
}
46 changes: 12 additions & 34 deletions ofborg/src/tasks/githubcommentfilter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,30 +53,13 @@ impl worker::SimpleWorker for GitHubCommentWorker {
return vec![worker::Action::Ack];
}

let build_destinations: Vec<(Option<String>, Option<String>)>;

if self.acl.can_build_unrestricted(
&job.comment.user.login,
&job.repository.full_name,
)
{
build_destinations = vec![(Some("build-jobs".to_owned()), None)];
} else if self.acl.can_build_restricted(
let build_destinations = self.acl.build_job_destinations_for_user_repo(
&job.comment.user.login,
&job.repository.full_name,
)
{
build_destinations = vec![
(None, Some("build-inputs-x86_64-linux".to_owned())),
(None, Some("build-inputs-aarch64-linux".to_owned())),
];
} else {
println!(
"ACL prohibits {} from building {:?} for {}",
job.comment.user.login,
instructions,
job.repository.full_name
);
);

if build_destinations.len() == 0 {
// Don't process comments if they can't build anything
return vec![worker::Action::Ack];
}

Expand Down Expand Up @@ -124,19 +107,14 @@ impl worker::SimpleWorker for GitHubCommentWorker {
for instruction in instructions {
match instruction {
commentparser::Instruction::Build(subset, attrs) => {
let logbackrk = format!(
"{}.{}",
job.repository.full_name.clone(),
job.issue.number.clone()
let msg = buildjob::BuildJob::new(
repo_msg.clone(),
pr_msg.clone(),
subset,
attrs,
None,
None
);
let msg = buildjob::BuildJob {
repo: repo_msg.clone(),
pr: pr_msg.clone(),
subset: Some(subset),
attrs: attrs,
logs: Some((Some("logs".to_owned()), Some(logbackrk.to_lowercase()))),
statusreport: Some((Some("build-results".to_owned()), None)),
};

for (exch, rk) in build_destinations.clone() {
response.push(worker::publish_serde_action(exch, rk, &msg));
Expand Down
Loading

0 comments on commit cda4fa9

Please sign in to comment.