rusty_ci/buildbot/
merge.rs1use crate::{unwrap, File};
2
3use rusty_yaml::Yaml;
4use std::fmt::{Display, Error, Formatter};
5use std::process::exit;
6
7pub enum VersionControlSystem {
11 GitHub,
12 GitLab,
13 Unsupported,
14}
15
16pub const AUTH_TOKEN_PATH: &str = "auth.token";
19
20pub struct MergeRequestHandler {
26 vcs: VersionControlSystem,
33 owner: String,
35 repo_name: String,
39 whitelist: Vec<String>,
48 auth_token: String,
50 repository_type: String,
53}
54
55impl MergeRequestHandler {
56 pub fn new(
57 vcs: VersionControlSystem,
58 owner: String,
59 repo_name: String,
60 whitelist: Vec<String>,
61 ) -> Self {
62 let auth_token = match File::read(AUTH_TOKEN_PATH) {
63 Ok(s) => s.trim().to_string(),
64 Err(e) => {
65 error!(
66 "Could not read authentication token from file '{}' because {}",
67 AUTH_TOKEN_PATH, e
68 );
69 exit(1);
70 }
71 };
72
73 if auth_token.is_empty() {
74 error!(
75 "You didn't write your VCS's authentication token to '{}'!",
76 AUTH_TOKEN_PATH
77 );
78 exit(0);
79 }
80
81 Self {
82 vcs,
83 owner,
84 repo_name,
85 whitelist,
86 auth_token,
87 repository_type: String::from("git"), }
89 }
90}
91
92impl Display for MergeRequestHandler {
95 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
96 match &self.vcs {
97 VersionControlSystem::GitHub => writeln!(
98 f,
99 "whitelist_authors = {:?}
100
101try:
102 c['change_source'].append(changes.GitHubPullrequestPoller(
103 owner=\"{owner}\",
104 repo=\"{name}\",
105 # right now just poll every 60 seconds
106 # this will need to change in the future, but this is just for testing.
107 pollInterval=120,
108 repository_type=\"{repository_type}\",
109 github_property_whitelist=[\"*\"],
110 token=\"{token}\"))
111except Exception as e:
112 print(f\"Could not create merge request handler: {{str(e)}}\")
113
114
115context = util.Interpolate(\"%(prop:buildername)s\")
116github_status_service = reporters.GitHubStatusPush(token='{token}',
117 context=context,
118 startDescription='Build started.',
119 endDescription='Build done.')
120
121c['services'].append(github_status_service)
122
123
124def is_whitelisted(props, password):
125 for prop in ['github.number', 'github.comments_url', 'github.user.login']:
126 # If these properties arent present, its not a pull request
127 if not (props.hasProperty(prop)):
128 return True
129
130 # URL for comments info
131 comments_url = props['github.comments_url']
132
133 # The pull request number that we'll try to whitelist
134 pr_number = props['github.number']
135
136 # The author of the PR
137 author = props['github.user.login']
138
139 resp = req.get(comments_url)
140 try:
141 # Try to convert to a JSON object so we can read the data
142 json_acceptable_string = resp.text.replace(\"'\", \"\\\"\")
143 comments_json = json.loads(json_acceptable_string)
144
145
146 # Check each comment
147 for comment in comments_json:
148 # If the comment was made by an admin and matches the password
149 if comment['user']['login'] in whitelist_authors and re.fullmatch(password, comment['body']):
150 # If the pull request was not already in the whitelisted PRs, add it
151 print(\"ADMIN: \" + str(comment['user']['login']) + \" PASSWORD: \" + str(comment['body']))
152 print(f\"PR NUMBER {{pr_number}} IS GOOD TO TEST\")
153 return True
154 except Exception as e:
155 # There was a problem converting to JSON, github returned bad data
156 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.\")
157 # Write the returned webpage to BAD
158 open('BAD', 'w').write(resp.text)
159
160
161 if author in whitelist_authors:
162 print(\"WHITELISTED AUTHOR\")
163 return True
164
165 return False
166",
167 self.whitelist,
168 token = self.auth_token.trim_matches('"'),
169 name = self.repo_name.trim_matches('"'),
170 owner = self.owner.trim_matches('"'),
171 repository_type = self.repository_type.trim_matches('"'),
172
173 ),
174 VersionControlSystem::GitLab => writeln!(
175 f,
176 "
177def is_whitelisted(props, password): return True
178
179context = util.Interpolate(\"%(prop:buildername)s\")
180gitlab_status_service = reporters.GitLabStatusPush(token='{token}',
181 context=context,
182 startDescription='Build started.',
183 endDescription='Build done.')
184
185c['services'].append(gitlab_status_service)
186
187",
188 token = self.auth_token.trim_matches('"'),
189 ),
190 VersionControlSystem::Unsupported => writeln!(
191 f,
192 "print('We currently dont support building merge requests on your VCS.')"
193 ),
194 }
195 }
196}
197
198impl From<Yaml> for MergeRequestHandler {
199 fn from(yaml: Yaml) -> Self {
200 for section in ["version-control-system", "owner", "repo-name", "whitelist"].iter() {
202 if !yaml.has_section(section) {
203 error!("There was an error creating the merge request handler: '{}' section not specified", section);
204 exit(1);
205 }
206 }
207 let vcs: VersionControlSystem = match unwrap(&yaml, "version-control-system").as_str() {
210 "github" => VersionControlSystem::GitHub,
211 "gitlab" => VersionControlSystem::GitLab,
212 _ => {
213 warn!(
214 "We do not support building merge requests on your version control system yet!"
215 );
216 warn!(
217 "We will proceed with the build. All other features should function as intended."
218 );
219 VersionControlSystem::Unsupported
220 }
221 };
222
223 let owner: String = unwrap(&yaml, "owner");
225
226 let repo_name: String = unwrap(&yaml, "repo-name");
228
229 let mut whitelist: Vec<String> = vec![];
232 for author in yaml.get_section("whitelist").unwrap() {
233 whitelist.push(
234 author
235 .to_string()
236 .trim_matches('"')
237 .trim_matches('\'')
238 .to_string(),
239 );
240 }
241
242 Self::new(vcs, owner, repo_name, whitelist)
244 }
245}