Skip to content

Commit b85eabf

Browse files
committed
feat(ZNTA-1964): Deploy war to server
1 parent edbaa6d commit b85eabf

7 files changed

Lines changed: 770 additions & 54 deletions

JenkinsHelper.py

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
#!/usr/bin/env python
2+
"""Jenkins Helper functions
3+
It contains jenkins helper
4+
Run JenkinsHelper --help or JenkinsHelper --help <command> for
5+
detail help."""
6+
7+
from __future__ import (
8+
absolute_import, division, print_function, unicode_literals)
9+
import ast
10+
import logging
11+
import os
12+
import os.path
13+
import re
14+
import sys
15+
16+
try:
17+
from typing import List, Any # noqa: F401 # pylint: disable=unused-import
18+
except ImportError:
19+
sys.stderr.write("python typing module is not installed" + os.linesep)
20+
21+
from ZanataFunctions import UrlHelper
22+
from ZanataArgParser import ZanataArgParser
23+
24+
25+
try:
26+
# We need to import 'List' and 'Any' for mypy to work
27+
from typing import List, Any # noqa: E501,F401,F811 # pylint: disable=unused-import
28+
except ImportError:
29+
sys.stderr.write("python typing module is not installed" + os.linesep)
30+
31+
32+
class JenkinsServer(object):
33+
"""JenkinsServer can connect to a Jenkins server"""
34+
def __init__(self, server_url, user, token):
35+
# type: (str, str,str) -> None
36+
self.url_helper = UrlHelper(
37+
server_url, user, token)
38+
self.server_url = server_url
39+
self.user = user
40+
self.token = token
41+
42+
@classmethod
43+
def add_parser(cls, arg_parser=None):
44+
# type: (ZanataArgParser) -> ZanataArgParser
45+
"""Add JenkinsServer parameters to a parser"""
46+
if not arg_parser:
47+
arg_parser = ZanataArgParser(description=__doc__)
48+
49+
# Add env
50+
arg_parser.add_env('JENKINS_URL', dest='server_url', required=True)
51+
arg_parser.add_env('ZANATA_JENKINS_USER', dest='user', required=True)
52+
arg_parser.add_env('ZANATA_JENKINS_TOKEN', dest='token', required=True)
53+
return arg_parser
54+
55+
@classmethod
56+
def init_from_parsed_args(cls, args):
57+
"""New an instance from parsed args"""
58+
return cls(args.server_url, args.user, args.token)
59+
60+
61+
class JenkinsJob(object):
62+
"""JenkinsJob object can access a Jenkins Job"""
63+
64+
@staticmethod
65+
def dict_get_elem_by_path(dic, path):
66+
# type (dict, str) -> object
67+
"""Return the elem in python dictionary given path
68+
for example: you can use a/b to retrieve answer from following
69+
dict:
70+
{ 'a': { 'b': 'answer' }}"""
71+
obj = dic
72+
for key in path.split('/'):
73+
if obj[key]:
74+
obj = obj[key]
75+
else:
76+
return None
77+
return obj
78+
79+
@staticmethod
80+
def print_key_value(key, value):
81+
# type (str, str) -> None
82+
"""Pretty print the key and value"""
83+
return "%30s : %s" % (key, value)
84+
85+
def get_elem(self, path):
86+
# type: (str) -> object
87+
"""Get element from the job object"""
88+
return JenkinsJob.dict_get_elem_by_path(self.content, path)
89+
90+
def __repr__(self):
91+
# type: () -> str
92+
result = "\n".join([
93+
JenkinsJob.print_key_value(tup[0], tup[1]) for tup in [
94+
['job_name', self.job_name],
95+
['folder', self.folder],
96+
['branch', self.branch]]])
97+
if self.content:
98+
result += "\n\n%s" % "\n".join([
99+
JenkinsJob.print_key_value(
100+
key, self.get_elem(key)) for key in [
101+
'displayName',
102+
'fullName',
103+
'lastBuild/number',
104+
'lastCompletedBuild/number',
105+
'lastFailedBuild/number',
106+
'lastSuccessfulBuild/number']])
107+
return result
108+
109+
def __init__(self, server, job_name, folder='', branch=''):
110+
# type (JenkinsServer, str, str, str) -> None
111+
self.server = server
112+
self.job_name = job_name
113+
self.folder = folder
114+
self.branch = branch
115+
self.content = None
116+
job_path = "job/%s" % self.job_name
117+
if folder:
118+
job_path = "job/%s/%s" % (folder, job_path)
119+
if branch:
120+
job_path += "/job/%s" % branch
121+
self.url = "%s%s" % (self.server.server_url, job_path)
122+
123+
@classmethod
124+
def add_parser(cls, arg_parser=None, only_options=False):
125+
# type: (ZanataArgParser, bool) -> ZanataArgParser
126+
"""Add JenkinsJob parameters to parser
127+
arg_parser: existing parser to be appended to
128+
only_options: Add only options and JenkinsServer env"""
129+
if not arg_parser or not arg_parser.has_env('JENKINS_URL'):
130+
arg_parser = JenkinsServer.add_parser(arg_parser)
131+
arg_parser.add_common_argument(
132+
'-b', '--branch', type=str,
133+
help='branch or PR name')
134+
arg_parser.add_common_argument(
135+
'-F', '--folder', type=str,
136+
help='GitHub Organization Folder')
137+
if not only_options:
138+
arg_parser.add_common_argument(
139+
'job_name', type=str, help='job name')
140+
141+
# Add sub commands
142+
arg_parser.add_sub_command(
143+
'get-job', None,
144+
help='Show job objects')
145+
arg_parser.add_sub_command(
146+
'get-last-successful-build', None,
147+
help=cls.get_last_successful_build.__doc__)
148+
arg_parser.add_sub_command(
149+
'get-last-successful-artifacts',
150+
{
151+
'-p --artifact-path-patterns': {
152+
'type': str, 'default': '.*',
153+
'help': 'comma split artifact path regex pattern'}}, # noqa: E501, #pylint: disable=line-too-long
154+
help='Get matching last-successful artifacts. Default: .*')
155+
arg_parser.add_sub_command(
156+
'download-last-successful-artifacts',
157+
{
158+
'-p --artifact-path-patterns': {
159+
'type': str, 'default': '.*',
160+
'help': 'comma split artifact path regex'
161+
},
162+
'-d --download-dir': {
163+
'type': str, 'default': '.',
164+
'help': 'Download directory'}
165+
},
166+
help='Get matching last-successful artifacts. Default: .*')
167+
return arg_parser
168+
169+
@classmethod
170+
def init_from_parsed_args(cls, args):
171+
"""New an instance from parsed args"""
172+
server = JenkinsServer.init_from_parsed_args(args)
173+
kwargs = {'job_name': args.job_name}
174+
for k in ['folder', 'branch']:
175+
if hasattr(args, k):
176+
kwargs[k] = getattr(args, k)
177+
return cls(server, **kwargs)
178+
179+
def load(self):
180+
# type: () -> None
181+
"""Load the build object from Jenkins server"""
182+
logging.debug("Loading job from %s/api/python", self.url)
183+
self.content = ast.literal_eval(UrlHelper.read(
184+
"%s/api/python" % self.url))
185+
186+
def get_last_successful_build(self):
187+
# type: () -> JenkinsJobBuild
188+
"""Get last successful build"""
189+
if not self.content:
190+
self.load()
191+
192+
if not self.content:
193+
raise AssertionError("Failed to load job from %s" % self.url)
194+
return JenkinsJobBuild(
195+
self,
196+
int(self.get_elem('lastSuccessfulBuild/number')),
197+
self.get_elem('lastSuccessfulBuild/url'))
198+
199+
def get_last_successful_artifacts(
200+
self, artifact_path_patterns=None):
201+
# type: (List[str]) -> List[str]
202+
"""Get last successful artifacts that matches patterns"""
203+
build = self.get_last_successful_build()
204+
return build.list_artifacts_related_paths(artifact_path_patterns)
205+
206+
def download_last_successful_artifacts(
207+
self, artifact_path_patterns=None, download_dir='.'):
208+
# type: (List[str])-> List[str]
209+
"""Download last successful artifacts that matches patterns.
210+
Returns related path of artifacts
211+
212+
Note the directory structure will be flattern."""
213+
if not artifact_path_patterns:
214+
artifact_path_patterns = ['.*']
215+
build = self.get_last_successful_build()
216+
artifact_path_list = build.list_artifacts_related_paths(
217+
artifact_path_patterns)
218+
for artifact_path in artifact_path_list:
219+
UrlHelper.download_file(
220+
build.url + 'artifact/' + artifact_path,
221+
download_dir=download_dir)
222+
return artifact_path_list
223+
224+
225+
class JenkinsJobBuild(object):
226+
"""Build object for Jenkins job"""
227+
228+
def __init__(self, parent_job, build_number, build_url):
229+
# type (object, int, str) -> None
230+
self.parent_job = parent_job
231+
self.number = build_number
232+
self.url = build_url
233+
self.content = None
234+
235+
def get_elem(self, path):
236+
# type: (str) -> object
237+
"""Get element from the build object"""
238+
return JenkinsJob.dict_get_elem_by_path(self.content, path)
239+
240+
def load(self):
241+
"""Load the build object from Jenkins server"""
242+
logging.debug("Loading build from %sapi/python", self.url)
243+
self.content = ast.literal_eval(UrlHelper.read(
244+
"%s/api/python" % self.url))
245+
246+
def list_artifacts_related_paths(self, artifact_path_patterns=None):
247+
# type: (str) -> List[str]
248+
"""Return a List of relativePaths of artifacts
249+
that matches the path patterns"""
250+
if not artifact_path_patterns:
251+
artifact_path_patterns = ['.*']
252+
if not self.content:
253+
self.load()
254+
if not self.content:
255+
raise AssertionError("Failed to load build from %s" % self.url)
256+
result = []
257+
for artifact in self.content['artifacts']:
258+
for pattern in artifact_path_patterns:
259+
if re.search(pattern, artifact['relativePath']):
260+
result.append(artifact['relativePath'])
261+
break # Only append once
262+
return result
263+
264+
def __repr__(self):
265+
# type: () -> str
266+
result = "\n".join([
267+
JenkinsJob.print_key_value(
268+
tup[0], str(tup[1])) for tup in [
269+
['number', self.number],
270+
['url', self.url]]])
271+
272+
if self.content:
273+
result += "\n\n%s" % "\n".join([
274+
JenkinsJob.print_key_value(
275+
key, self.get_elem(key)) for key in [
276+
'nextBuild/number',
277+
'previousBuild/number']])
278+
result += "\n\nArtifacts:\n%s" % "\n ".join(
279+
self.list_artifacts_related_paths())
280+
return result
281+
282+
283+
def run_sub_command(args):
284+
# type (ZanataArgParser.Namespace) -> None
285+
"""Run the sub command"""
286+
job = JenkinsJob.init_from_parsed_args(args)
287+
job.load()
288+
if args.sub_command == 'get_job':
289+
print(job)
290+
elif args.sub_command == 'get_last_successful_build':
291+
build = job.get_last_successful_build()
292+
build.load()
293+
print(build)
294+
elif args.sub_command == 'get_last_successful_artifacts':
295+
print('\n'.join(
296+
job.get_last_successful_artifacts(
297+
args.artifact_path_patterns.split(','))
298+
))
299+
elif args.sub_command == 'download_last_successful_artifacts':
300+
artifact_path_list = job.download_last_successful_artifacts(
301+
args.artifact_path_patterns.split(','),
302+
args.download_dir)
303+
print("Downloaded files %s" % '\n'.join(artifact_path_list))
304+
305+
306+
if __name__ == '__main__':
307+
run_sub_command(JenkinsJob.add_parser().parse_all())

0 commit comments

Comments
 (0)