74 Entry point for release job.
77 if not check():
return 0
79 header = {
"PRIVATE-TOKEN": kwargs.get(
"token",os.getenv(
"GIT_PASSWORD",
None))}
80 url = kwargs.get(
"base_url",os.getenv(
"CI_API_V4_URL",
None))
81 project = kwargs.get(
"project_url",os.getenv(
"CI_REPOSITORY_URL",
None))
84 if not all([x
for x
in [header[
"PRIVATE-TOKEN"], url, project]]):
86 warnings.warn(
"Could not validate release CLI input parameters. One or all of <token>, <base_url> and <project_url> are missing.", RuntimeWarning)
90 server =
'{uri.scheme}://{uri.netloc}/'.format(uri=urlparse(project))
91 namespace = os.path.splitext(project.split(server)[-1])[0]
92 server = str(2*posixpath.sep).join([server.split(posixpath.sep*2)[0], server.split(
"@")[-1].split(posixpath.sep*2)[-1]])
95 options = kwargs.get(
"assets",{
"generic":{
"tar.gz":
"Source code (tar.gz)",
96 "zip":
"Source code (zip)",
97 "whl":
"Python installer (whl)",
98 "exe":
"Standalone application (exe)"},
99 "pypi":{
"whl":
"PyPi package (whl)"}})
102 if not all([x
for x
in [url, server, namespace]]):
pass
105 repository = namespace.split(posixpath.sep)[-1]
108 group_url = posixpath.dirname(posixpath.join(server,
"groups",namespace))
112 for page
in range(1,int(requests.head(posixpath.join(url,
"groups"), headers=header).headers[
'x-total-pages']) + 1)
113 for group
in requests.get(posixpath.join(url,
"groups"), params={
"page":str(page)}, headers=header).json()
114 if group_url
in group[
"web_url"]]
117 if found: found = [group
for group
in found
if len(group[
"web_url"]) == len(group_url)][0]
118 else:
raise RuntimeError(
"Could not determine group of current project.")
124 for page
in range(1,int(requests.head(posixpath.join(url,
"groups",str(found[
"id"]),
"projects"), headers=header).headers[
'x-total-pages']) + 1)
126 for project
in requests.get(posixpath.join(url,
"groups",str(found[
"id"]),
"projects"), params={
"page":str(page)}, headers=header).json()
128 if any(repository.lower()
in match.lower()
for match
in [project[
"name"],project[
"path"]] ) ][0]
131 for page
in range(1,int(requests.head(posixpath.join(url,
"projects",str(found[
"id"]),
"releases"), headers=header).headers.get(
'x-total-pages',0)) + 1)
132 for release
in requests.get(posixpath.join(url,
"projects",str(found[
"id"]),
"releases"), params={
"page":str(page)}, headers=header).json()]
135 versions = [d[
"tag_name"][1:]
for d
in releases]
137 try: versions.sort(key=StrictVersion)
138 except: versions.sort(key=LooseVersion)
142 try: tag = kwargs.get(
"tag",os.getenv(
"TAG"))
143 except:
raise RuntimeError(
"Could not determine project tag. Please create an environment variable explicitly named 'TAG'.")
144 else: tag = kwargs.get(
"tag",os.getenv(
"TAG",
"v%s" % versions[-1]))
148 for page
in range(1,int(requests.head(posixpath.join(url,
"projects",str(found[
"id"]),
"packages"), headers=header).headers[
'x-total-pages']) + 1)
149 for package
in requests.get(posixpath.join(url,
"projects",str(found[
"id"]),
"packages"), params={
"page":str(page)}, headers=header).json()
150 if version.parse(package[
"version"]) == version.parse(tag.split(
"v")[-1])]
153 if not package:
raise RuntimeError(
"Could not determine latest package associated with tag: %s" % str(tag))
156 sha = [x[
"pipeline"][
"sha"]
for x
in package
if set([x[
"pipeline"][
"project_id"],x[
"name"]]) == set([found[
"id"],found[
"name"]])]
157 assets = {x[
"package_type"]:posixpath.join(server,x[
"_links"][
"web_path"][1:])
for x
in package}
160 files = [(key, x)
for key, asset
in assets.items()
161 for x
in requests.get(posixpath.join(url,
"projects",str(found[
"id"]),
"packages",asset.split(posixpath.sep)[-1],
"package_files"), headers=header).json() ]
164 for key
in list(assets.keys()): assets.update({key:{
"url":posixpath.join(server,namespace,
"-",
"package_files")}})
165 for key
in list(assets.keys()): assets[key].update({
"files": [{ x[-1][
"file_name"] : x[-1][
"id"] }
for x
in files
if key
in x[0] ]})
168 collector = [{
"name":name,
"url": posixpath.join(assets[key][
"url"],str(ID),
"download")}
170 for extension, name
in options[key].items()
171 for entry
in assets.get(key,{}).get(
"files",[])
172 for file, ID
in entry.items()
if file.endswith(extension)]
175 collector = [entry
for entry
in collector
if requests.get(entry[
"url"]).status_code == 200]
176 collector = [{
"name":key,
"url":value}
for key, value
in {entry[
"name"]:entry[
"url"]
for entry
in collector}.items()]
179 release = {
"name":
"Release of %s" % tag,
"description":
"Created using CLI",
"tag_name":str(tag),
"assets": {
"links":collector}}
182 if sha: release.update({
"ref":sha[0]})
183 elif os.getenv(
"CI_COMMIT_SHA",
""): release.update({
"ref":os.getenv(
"CI_COMMIT_SHA")})
186 if kwargs.get(
"silent_update",
True): requests.delete(posixpath.join(url,
"projects",str(found[
"id"]),
"releases",tag), headers=header)
189 r = requests.post(posixpath.join(url,
"projects",str(found[
"id"]),
"releases"), json=release, headers=header)
190 try: r.raise_for_status()
192 warnings.warn(
"Creating or updating the release raised an unexpected return code. Please verify the result.", RuntimeWarning)
193 warnings.warn(
"%s" % r.text, RuntimeWarning)
194 warnings.warn(
"%s" % str(release), RuntimeWarning)
200 Main command line parser.
203 options = {
"choices":kwargs.pop(
"choices",[
"release",
"housekeeping"])}
204 commands = globals(); commands.update(kwargs.get(
"register",{}))
205 options[
"choices"] += list(kwargs.pop(
"register",{}).keys())
206 if not kwargs.get(
"method",
""):
207 parser = argparse.ArgumentParser(description=
'CLI wrapper options for GitLab CI.')
208 parser.add_argument(
'method', metavar=
'namespace', type=str, nargs=1, choices=options[
"choices"],
209 help=
'An option identifier. Unknown arguments are ignored. Allowed values are: '+
', '.join(options[
"choices"]))
211 parser.add_argument(
'-b',
'--base_url', type=str, nargs=1, help=
"Base API v4 URL of a GitLab instance. Defaults to GitLab instance of DLR.")
212 parser.add_argument(
'-t',
'--token', type=str, nargs=1, help=
"Token to be used for authentication.")
213 parser.add_argument(
'-i',
'--identifier', type=str, nargs=1, help=
"A valid, unique project identifier.")
214 parser.add_argument(
'-p',
'--package', type=str, nargs=1, help=
"A valid project name.")
215 parser.add_argument(
'-v',
'--version', type=str, nargs=1, help=
"A valid version identifier for PyPI.")
216 parser.add_argument(
"-o",
'--output', type=str, nargs=1, help=
"Absolute path to output directory. Defaults to current project folder.")
218 args, _ = parser.parse_known_args()
219 method = str(args.method[0])
221 given = [x
for x
in vars(args)
if getattr(args,x)]
223 settings.update({x:next(iter(getattr(args,x)))
for x
in given})
224 else: method = kwargs.get(
"method")
225 settings.update(copy.deepcopy(kwargs))
227 if not method
in globals():
raise RuntimeError(
"Unknown GitLab CI namespace option.")
228 else: commands[method](**settings)