use crate::{unwrap, File};
use rusty_yaml::Yaml;
use std::fmt::{Display, Error, Formatter};
use std::process::exit;
pub enum VersionControlSystem {
GitHub,
GitLab,
Unsupported,
}
pub const AUTH_TOKEN_PATH: &str = "auth.token";
pub struct MergeRequestHandler {
vcs: VersionControlSystem,
owner: String,
repo_name: String,
whitelist: Vec<String>,
auth_token: String,
repository_type: String,
}
impl MergeRequestHandler {
pub fn new(
vcs: VersionControlSystem,
owner: String,
repo_name: String,
whitelist: Vec<String>,
) -> Self {
let auth_token = match File::read(AUTH_TOKEN_PATH) {
Ok(s) => s.trim().to_string(),
Err(e) => {
error!(
"Could not read authentication token from file '{}' because {}",
AUTH_TOKEN_PATH, e
);
exit(1);
}
};
if auth_token.is_empty() {
error!(
"You didn't write your VCS's authentication token to '{}'!",
AUTH_TOKEN_PATH
);
exit(0);
}
Self {
vcs,
owner,
repo_name,
whitelist,
auth_token,
repository_type: String::from("git"), }
}
}
impl Display for MergeRequestHandler {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match &self.vcs {
VersionControlSystem::GitHub => writeln!(
f,
"whitelist_authors = {:?}
try:
c['change_source'].append(changes.GitHubPullrequestPoller(
owner=\"{owner}\",
repo=\"{name}\",
# right now just poll every 60 seconds
# this will need to change in the future, but this is just for testing.
pollInterval=120,
repository_type=\"{repository_type}\",
github_property_whitelist=[\"*\"],
token=\"{token}\"))
except Exception as e:
print(f\"Could not create merge request handler: {{str(e)}}\")
context = util.Interpolate(\"%(prop:buildername)s\")
github_status_service = reporters.GitHubStatusPush(token='{token}',
context=context,
startDescription='Build started.',
endDescription='Build done.')
c['services'].append(github_status_service)
def is_whitelisted(props, password):
for prop in ['github.number', 'github.comments_url', 'github.user.login']:
# If these properties arent present, its not a pull request
if not (props.hasProperty(prop)):
return True
# URL for comments info
comments_url = props['github.comments_url']
# The pull request number that we'll try to whitelist
pr_number = props['github.number']
# The author of the PR
author = props['github.user.login']
resp = req.get(comments_url)
try:
# Try to convert to a JSON object so we can read the data
json_acceptable_string = resp.text.replace(\"'\", \"\\\"\")
comments_json = json.loads(json_acceptable_string)
# Check each comment
for comment in comments_json:
# If the comment was made by an admin and matches the password
if comment['user']['login'] in whitelist_authors and re.fullmatch(password, comment['body']):
# If the pull request was not already in the whitelisted PRs, add it
print(\"ADMIN: \" + str(comment['user']['login']) + \" PASSWORD: \" + str(comment['body']))
print(f\"PR NUMBER {{pr_number}} IS GOOD TO TEST\")
return True
except Exception as e:
# There was a problem converting to JSON, github returned bad data
print(f\"There was an error: {{str(e)}}. If this error has anything to do with JSON, its likely that you've queried GitHub too many times.\")
# Write the returned webpage to BAD
open('BAD', 'w').write(resp.text)
if author in whitelist_authors:
print(\"WHITELISTED AUTHOR\")
return True
return False
",
self.whitelist,
token = self.auth_token.trim_matches('"'),
name = self.repo_name.trim_matches('"'),
owner = self.owner.trim_matches('"'),
repository_type = self.repository_type.trim_matches('"'),
),
VersionControlSystem::GitLab => writeln!(
f,
"
def is_whitelisted(props, password): return True
context = util.Interpolate(\"%(prop:buildername)s\")
gitlab_status_service = reporters.GitLabStatusPush(token='{token}',
context=context,
startDescription='Build started.',
endDescription='Build done.')
c['services'].append(gitlab_status_service)
",
token = self.auth_token.trim_matches('"'),
),
VersionControlSystem::Unsupported => writeln!(
f,
"print('We currently dont support building merge requests on your VCS.')"
),
}
}
}
impl From<Yaml> for MergeRequestHandler {
fn from(yaml: Yaml) -> Self {
for section in ["version-control-system", "owner", "repo-name", "whitelist"].iter() {
if !yaml.has_section(section) {
error!("There was an error creating the merge request handler: '{}' section not specified", section);
exit(1);
}
}
let vcs: VersionControlSystem = match unwrap(&yaml, "version-control-system").as_str() {
"github" => VersionControlSystem::GitHub,
"gitlab" => VersionControlSystem::GitLab,
_ => {
warn!(
"We do not support building merge requests on your version control system yet!"
);
warn!(
"We will proceed with the build. All other features should function as intended."
);
VersionControlSystem::Unsupported
}
};
let owner: String = unwrap(&yaml, "owner");
let repo_name: String = unwrap(&yaml, "repo-name");
let mut whitelist: Vec<String> = vec![];
for author in yaml.get_section("whitelist").unwrap() {
whitelist.push(
author
.to_string()
.trim_matches('"')
.trim_matches('\'')
.to_string(),
);
}
Self::new(vcs, owner, repo_name, whitelist)
}
}