Support eval instructions
This commit is contained in:
parent
dfbd52480e
commit
2150acacc6
|
@ -39,12 +39,12 @@ fn main() {
|
||||||
let mut channel = session.open_channel(2).unwrap();
|
let mut channel = session.open_channel(2).unwrap();
|
||||||
|
|
||||||
channel.basic_consume(
|
channel.basic_consume(
|
||||||
worker::new(tasks::buildfilter::BuildFilterWorker::new(
|
worker::new(tasks::githubcommentfilter::GitHubCommentWorker::new(
|
||||||
cfg.acl(),
|
cfg.acl(),
|
||||||
cfg.github()
|
cfg.github()
|
||||||
)),
|
)),
|
||||||
"build-inputs",
|
"build-inputs",
|
||||||
format!("{}-build-filter", cfg.whoami()).as_ref(),
|
format!("{}-github-comment-filter", cfg.whoami()).as_ref(),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
pub fn parse(text: &str) -> Option<Instruction> {
|
pub fn parse(text: &str) -> Option<Vec<Instruction>> {
|
||||||
let tokens: Vec<String> = text.split_whitespace()
|
let tokens: Vec<String> = text.split_whitespace()
|
||||||
.map(|s| s.to_owned()).collect();
|
.map(|s| s.to_owned()).collect();
|
||||||
|
|
||||||
|
@ -7,21 +7,37 @@ pub fn parse(text: &str) -> Option<Instruction> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (targeter_, params_) = tokens.split_at(2);
|
if tokens[0].to_lowercase() != "@grahamcofborg" {
|
||||||
let targeter: Vec<String> = targeter_.iter()
|
return None;
|
||||||
.map(|s| s.to_lowercase()).collect();
|
|
||||||
let params: Vec<String> = params_.to_vec();
|
|
||||||
|
|
||||||
if targeter == ["@grahamcofborg", "build"] {
|
|
||||||
return Some(Instruction::Build(params));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return None;
|
let commands: Vec<&[String]> = tokens
|
||||||
|
.split(|token| token.to_lowercase() == "@grahamcofborg")
|
||||||
|
.filter(|token| token.len() > 0)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut instructions: Vec<Instruction> = vec![];
|
||||||
|
for command in commands {
|
||||||
|
let (left, right) = command.split_at(1);
|
||||||
|
match left[0].as_ref() {
|
||||||
|
"build" => {
|
||||||
|
instructions.push(Instruction::Build(right.to_vec()))
|
||||||
|
}
|
||||||
|
"eval" => {
|
||||||
|
instructions.push(Instruction::Eval)
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(instructions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
Build(Vec<String>)
|
Build(Vec<String>),
|
||||||
|
Eval
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -37,37 +53,94 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bogus_comment() {
|
fn bogus_comment() {
|
||||||
assert_eq!(None, parse(":) :) :)"));
|
assert_eq!(None, parse(":) :) :) @grahamcofborg build hi"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_comment() {
|
||||||
|
assert_eq!(Some(vec![Instruction::Eval]),
|
||||||
|
parse("@grahamcofborg eval"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_and_build_comment() {
|
||||||
|
assert_eq!(Some(vec![
|
||||||
|
Instruction::Eval,
|
||||||
|
Instruction::Build(vec![
|
||||||
|
String::from("foo"),
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
parse("@grahamcofborg eval @grahamcofborg build foo"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_and_eval_and_build_comment() {
|
||||||
|
assert_eq!(Some(vec![
|
||||||
|
Instruction::Build(vec![
|
||||||
|
String::from("bar"),
|
||||||
|
]),
|
||||||
|
Instruction::Eval,
|
||||||
|
Instruction::Build(vec![
|
||||||
|
String::from("foo"),
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
parse("
|
||||||
|
@grahamcofborg build bar
|
||||||
|
@grahamcofborg eval
|
||||||
|
@grahamcofborg build foo"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_and_eval_comment() {
|
||||||
|
assert_eq!(Some(vec![
|
||||||
|
Instruction::Build(vec![
|
||||||
|
String::from("foo"),
|
||||||
|
]),
|
||||||
|
Instruction::Eval,
|
||||||
|
]),
|
||||||
|
parse("@grahamcofborg build foo @grahamcofborg eval"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_comment() {
|
fn build_comment() {
|
||||||
assert_eq!(Some(Instruction::Build(vec![
|
assert_eq!(Some(vec![Instruction::Build(vec![
|
||||||
String::from("foo"),
|
String::from("foo"),
|
||||||
String::from("bar"),
|
String::from("bar"),
|
||||||
String::from("baz")
|
String::from("baz")
|
||||||
])),
|
])]),
|
||||||
|
parse("@GrahamCOfBorg build foo bar
|
||||||
|
|
||||||
|
baz"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_comment_newlines() {
|
||||||
|
assert_eq!(Some(vec![Instruction::Build(vec![
|
||||||
|
String::from("foo"),
|
||||||
|
String::from("bar"),
|
||||||
|
String::from("baz")
|
||||||
|
])]),
|
||||||
parse("@GrahamCOfBorg build foo bar baz"));
|
parse("@GrahamCOfBorg build foo bar baz"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_comment_lower() {
|
fn build_comment_lower() {
|
||||||
assert_eq!(Some(Instruction::Build(vec![
|
assert_eq!(Some(vec![Instruction::Build(vec![
|
||||||
String::from("foo"),
|
String::from("foo"),
|
||||||
String::from("bar"),
|
String::from("bar"),
|
||||||
String::from("baz")
|
String::from("baz")
|
||||||
])),
|
])]),
|
||||||
parse("@grahamcofborg build foo bar baz"));
|
parse("@grahamcofborg build foo bar baz"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_whitespace_disregarded() {
|
fn build_whitespace_disregarded() {
|
||||||
assert_eq!(Some(Instruction::Build(vec![
|
assert_eq!(Some(vec![Instruction::Build(vec![
|
||||||
String::from("foo"),
|
String::from("foo"),
|
||||||
String::from("bar"),
|
String::from("bar"),
|
||||||
String::from("baz")
|
String::from("baz")
|
||||||
])),
|
])]),
|
||||||
parse("
|
parse("
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,11 +157,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_comment_lower_package_case_retained() {
|
fn build_comment_lower_package_case_retained() {
|
||||||
assert_eq!(Some(Instruction::Build(vec![
|
assert_eq!(Some(vec![Instruction::Build(vec![
|
||||||
String::from("foo"),
|
String::from("foo"),
|
||||||
String::from("bar"),
|
String::from("bar"),
|
||||||
String::from("baz.Baz")
|
String::from("baz.Baz")
|
||||||
])),
|
])]),
|
||||||
parse("@grahamcofborg build foo bar baz.Baz"));
|
parse("@grahamcofborg build foo bar baz.Baz"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
extern crate amqp;
|
|
||||||
extern crate env_logger;
|
|
||||||
|
|
||||||
use ofborg::ghevent;
|
|
||||||
use ofborg::acl;
|
|
||||||
use serde_json;
|
|
||||||
|
|
||||||
use hubcaps;
|
|
||||||
use ofborg::message::{Repo, Pr, buildjob};
|
|
||||||
use ofborg::worker;
|
|
||||||
use ofborg::commentparser;
|
|
||||||
use amqp::protocol::basic::{Deliver,BasicProperties};
|
|
||||||
|
|
||||||
|
|
||||||
pub struct BuildFilterWorker {
|
|
||||||
acl: acl::ACL,
|
|
||||||
github: hubcaps::Github
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BuildFilterWorker {
|
|
||||||
pub fn new(acl: acl::ACL, github: hubcaps::Github) -> BuildFilterWorker {
|
|
||||||
return BuildFilterWorker{
|
|
||||||
acl: acl,
|
|
||||||
github: github,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl worker::SimpleWorker for BuildFilterWorker {
|
|
||||||
type J = ghevent::IssueComment;
|
|
||||||
|
|
||||||
fn msg_to_job(&self, _: &Deliver, _: &BasicProperties,
|
|
||||||
body: &Vec<u8>) -> Result<Self::J, String> {
|
|
||||||
return match serde_json::from_slice(body) {
|
|
||||||
Ok(e) => { Ok(e) }
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to deserialize IsssueComment: {:?}", String::from_utf8(body.clone()));
|
|
||||||
panic!("{:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consumer(&self, job: &ghevent::IssueComment) -> worker::Actions {
|
|
||||||
let instructions = commentparser::parse(&job.comment.body);
|
|
||||||
if instructions == None {
|
|
||||||
return vec![
|
|
||||||
worker::Action::Ack
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.acl.can_build(&job.comment.user.login, &job.repository.full_name) {
|
|
||||||
println!("ACL prohibits {} from building {:?} for {}",
|
|
||||||
job.comment.user.login,
|
|
||||||
instructions,
|
|
||||||
job.repository.full_name);
|
|
||||||
return vec![
|
|
||||||
worker::Action::Ack
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Got job: {:?}", job);
|
|
||||||
|
|
||||||
let instructions = commentparser::parse(&job.comment.body);
|
|
||||||
println!("Instructions: {:?}", instructions);
|
|
||||||
|
|
||||||
let pr = self.github
|
|
||||||
.repo(job.repository.owner.login.clone(), job.repository.name.clone())
|
|
||||||
.pulls()
|
|
||||||
.get(job.issue.number)
|
|
||||||
.get();
|
|
||||||
|
|
||||||
if let Err(x) = pr {
|
|
||||||
info!("fetching PR {}#{} from GitHub yielded error {}",
|
|
||||||
job.repository.full_name,
|
|
||||||
job.issue.number,
|
|
||||||
x
|
|
||||||
);
|
|
||||||
return vec![
|
|
||||||
worker::Action::Ack
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
let pr = pr.unwrap();
|
|
||||||
|
|
||||||
match instructions {
|
|
||||||
Some(commentparser::Instruction::Build(attrs)) => {
|
|
||||||
let msg = buildjob::BuildJob{
|
|
||||||
repo: Repo {
|
|
||||||
clone_url: job.repository.clone_url.clone(),
|
|
||||||
full_name: job.repository.full_name.clone(),
|
|
||||||
owner: job.repository.owner.login.clone(),
|
|
||||||
name: job.repository.name.clone(),
|
|
||||||
},
|
|
||||||
pr: Pr {
|
|
||||||
number: job.issue.number.clone(),
|
|
||||||
head_sha: pr.head.sha,
|
|
||||||
target_branch: Some(pr.base.commit_ref)
|
|
||||||
},
|
|
||||||
attrs: attrs
|
|
||||||
};
|
|
||||||
|
|
||||||
let props = BasicProperties {
|
|
||||||
content_type: Some("application/json".to_owned()),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
return vec![
|
|
||||||
worker::Action::Publish(worker::QueueMsg{
|
|
||||||
exchange: Some("build-jobs".to_owned()),
|
|
||||||
routing_key: None,
|
|
||||||
mandatory: true,
|
|
||||||
immediate: false,
|
|
||||||
properties: Some(props),
|
|
||||||
content: serde_json::to_string(&msg).unwrap().into_bytes()
|
|
||||||
}),
|
|
||||||
worker::Action::Ack
|
|
||||||
];
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return vec![
|
|
||||||
worker::Action::Ack
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
160
ofborg/src/tasks/githubcommentfilter.rs
Normal file
160
ofborg/src/tasks/githubcommentfilter.rs
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
extern crate amqp;
|
||||||
|
extern crate env_logger;
|
||||||
|
|
||||||
|
use ofborg::ghevent;
|
||||||
|
use ofborg::acl;
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
|
use hubcaps;
|
||||||
|
use ofborg::message::{Repo, Pr, buildjob, massrebuildjob};
|
||||||
|
use ofborg::worker;
|
||||||
|
use ofborg::commentparser;
|
||||||
|
use amqp::protocol::basic::{Deliver,BasicProperties};
|
||||||
|
|
||||||
|
|
||||||
|
pub struct GitHubCommentWorker {
|
||||||
|
acl: acl::ACL,
|
||||||
|
github: hubcaps::Github
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GitHubCommentWorker {
|
||||||
|
pub fn new(acl: acl::ACL, github: hubcaps::Github) -> GitHubCommentWorker {
|
||||||
|
return GitHubCommentWorker{
|
||||||
|
acl: acl,
|
||||||
|
github: github,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl worker::SimpleWorker for GitHubCommentWorker {
|
||||||
|
type J = ghevent::IssueComment;
|
||||||
|
|
||||||
|
fn msg_to_job(&self, _: &Deliver, _: &BasicProperties,
|
||||||
|
body: &Vec<u8>) -> Result<Self::J, String> {
|
||||||
|
return match serde_json::from_slice(body) {
|
||||||
|
Ok(e) => { Ok(e) }
|
||||||
|
Err(e) => {
|
||||||
|
println!("Failed to deserialize IsssueComment: {:?}", String::from_utf8(body.clone()));
|
||||||
|
panic!("{:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consumer(&self, job: &ghevent::IssueComment) -> worker::Actions {
|
||||||
|
let instructions = commentparser::parse(&job.comment.body);
|
||||||
|
if instructions == None {
|
||||||
|
return vec![
|
||||||
|
worker::Action::Ack
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.acl.can_build(&job.comment.user.login, &job.repository.full_name) {
|
||||||
|
println!("ACL prohibits {} from building {:?} for {}",
|
||||||
|
job.comment.user.login,
|
||||||
|
instructions,
|
||||||
|
job.repository.full_name);
|
||||||
|
return vec![
|
||||||
|
worker::Action::Ack
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Got job: {:?}", job);
|
||||||
|
|
||||||
|
let instructions = commentparser::parse(&job.comment.body);
|
||||||
|
println!("Instructions: {:?}", instructions);
|
||||||
|
|
||||||
|
let pr = self.github
|
||||||
|
.repo(job.repository.owner.login.clone(), job.repository.name.clone())
|
||||||
|
.pulls()
|
||||||
|
.get(job.issue.number)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
if let Err(x) = pr {
|
||||||
|
info!("fetching PR {}#{} from GitHub yielded error {}",
|
||||||
|
job.repository.full_name,
|
||||||
|
job.issue.number,
|
||||||
|
x
|
||||||
|
);
|
||||||
|
return vec![
|
||||||
|
worker::Action::Ack
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
let pr = pr.unwrap();
|
||||||
|
|
||||||
|
let mut response: Vec<worker::Action> = vec![];
|
||||||
|
if let Some(instructions) = instructions {
|
||||||
|
for instruction in instructions {
|
||||||
|
match instruction {
|
||||||
|
commentparser::Instruction::Build(attrs) => {
|
||||||
|
let msg = buildjob::BuildJob{
|
||||||
|
repo: Repo {
|
||||||
|
clone_url: job.repository.clone_url.clone(),
|
||||||
|
full_name: job.repository.full_name.clone(),
|
||||||
|
owner: job.repository.owner.login.clone(),
|
||||||
|
name: job.repository.name.clone(),
|
||||||
|
},
|
||||||
|
pr: Pr {
|
||||||
|
number: job.issue.number.clone(),
|
||||||
|
head_sha: pr.head.sha.clone(),
|
||||||
|
target_branch: Some(pr.base.commit_ref.clone())
|
||||||
|
},
|
||||||
|
attrs: attrs
|
||||||
|
};
|
||||||
|
|
||||||
|
let props = BasicProperties {
|
||||||
|
content_type: Some("application/json".to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
response.push(
|
||||||
|
worker::Action::Publish(worker::QueueMsg{
|
||||||
|
exchange: Some("build-jobs".to_owned()),
|
||||||
|
routing_key: None,
|
||||||
|
mandatory: true,
|
||||||
|
immediate: false,
|
||||||
|
properties: Some(props),
|
||||||
|
content: serde_json::to_string(&msg).unwrap().into_bytes()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
commentparser::Instruction::Eval => {
|
||||||
|
let msg = massrebuildjob::MassRebuildJob{
|
||||||
|
repo: Repo {
|
||||||
|
clone_url: job.repository.clone_url.clone(),
|
||||||
|
full_name: job.repository.full_name.clone(),
|
||||||
|
owner: job.repository.owner.login.clone(),
|
||||||
|
name: job.repository.name.clone(),
|
||||||
|
},
|
||||||
|
pr: Pr {
|
||||||
|
number: job.issue.number.clone(),
|
||||||
|
head_sha: pr.head.sha.clone(),
|
||||||
|
target_branch: Some(pr.base.commit_ref.clone()),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let props = BasicProperties {
|
||||||
|
content_type: Some("application/json".to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
response.push(
|
||||||
|
worker::Action::Publish(worker::QueueMsg{
|
||||||
|
exchange: None,
|
||||||
|
routing_key: Some("mass-rebuild-check-jobs".to_owned()),
|
||||||
|
mandatory: true,
|
||||||
|
immediate: false,
|
||||||
|
properties: Some(props),
|
||||||
|
content: serde_json::to_string(&msg).unwrap().into_bytes()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.push(worker::Action::Ack);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,4 +2,4 @@
|
||||||
pub mod heartbeat;
|
pub mod heartbeat;
|
||||||
pub mod build;
|
pub mod build;
|
||||||
pub mod massrebuilder;
|
pub mod massrebuilder;
|
||||||
pub mod buildfilter;
|
pub mod githubcommentfilter;
|
||||||
|
|
Loading…
Reference in a new issue