#!/usr/bin/python import requests import json import argparse import sys parser = argparse.ArgumentParser(description='Gitlab Release Uploader') parser.add_argument('--privateToken', '-p', help='Private token to access Gitlab', required=True) parser.add_argument('--projectId', '-i', help='ProjectID of the related project', required=True) parser.add_argument('--projectUrl', '-u', help='URL of the related project at Gitlab', required=True) parser.add_argument('--file', '-f', help='File to be released, can appear multiple times', action='append', required=False) parser.add_argument('--releaseName', '-n', help='Name of the release', required=False, default='') parser.add_argument('--createRelease', '-C', help='Shall the release be created here', required=False, action='store_true', default=False) parser.add_argument('--releaseTag', '-t', help='Tag of the release in the repo', required=False, default='') parser.add_argument('--releaseTagTarget', '-T', help='Commit or branch the tag should point to', required=False, default='') parser.add_argument('--createReleaseTag', '-c', help='Shall the release tag be created here', required=False, action='store_true', default=False) parser.add_argument('--description', '-d', help='Description of the release', required=False, default='') parser.add_argument('--releaseInfoFile', '-F', help='File containing JSON object with release info ' '(release tag, create release tag, description', required=False, default='') parser.add_argument('--instanceUrl', '-I', help='URL of your gitlab instance', required=False, default='https://gitlab.com') parser.add_argument('--caBundle', '-B', help='File with the CA certificates to trust', required=False, default='/etc/ssl/certs/ca-certificates.crt') parser.add_argument('--insecure', help='insecure ssl connect', required=False, action='store_true', default=False) parser.add_argument('--verbose', '-v', help='verbose output', required=False, action='store_true', default=False) args = parser.parse_args() privateToken = args.privateToken projectId = args.projectId projectUrl = args.projectUrl filesToUpload = args.file releaseName = args.releaseName createRelease = args.createRelease releaseTag = args.releaseTag releaseTagTarget = args.releaseTagTarget releaseDescription = args.description instanceUrl = args.instanceUrl createReleaseTag = args.createReleaseTag releaseInfoFilename = args.releaseInfoFile caBundle = args.caBundle verbose = args.verbose insecure = args.insecure releaseInfo = {} if (releaseInfoFilename): with open(releaseInfoFilename, 'r') as releaseInfoFile: releaseInfo = releaseInfoFile.read() releaseInfo = json.loads(releaseInfo) if 'releaseName' in releaseInfo: releaseName = releaseInfo['releaseName'] if 'createRelease' in releaseInfo: createRelease = (releaseInfo['createRelease'] in ('true', 'True')) if 'releaseTag' in releaseInfo: releaseTag = releaseInfo['releaseTag'] if 'releaseTagTarget' in releaseInfo: releaseTagTarget = releaseInfo['releaseTagTarget'] if 'createReleaseTag' in releaseInfo: createReleaseTag = (releaseInfo['createReleaseTag'] in ('true', 'True')) if 'description' in releaseInfo: releaseDescription = releaseInfo['description'] if releaseName == '': raise Exception('No release name given') if (releaseTag == '') and createRelease: raise Exception('No release tag given but creation of release requested') if (releaseTagTarget == '') and createReleaseTag: raise Exception('No release tag target given but creation of tag requested') if (releaseDescription == '') and createRelease: raise Exception('No release description given but creation of release requested') if insecure: caBundle = (False) def checkAndShowResult(result, expectedCode, errorMessage): global verbose if result.status_code != expectedCode: print(result) print(result.text) raise Exception(errorMessage) if verbose: print(result) print(result.text) # --- upload the file assets = [] # is required later, must be defined if filesToUpload: url = "%s/api/v4/projects/%s/uploads" % (instanceUrl, projectId) headers = {"PRIVATE-TOKEN": privateToken} for filename in filesToUpload: with open(filename, 'rb') as filehandle: files = {"file": filehandle} if verbose: print("POST to {}".format(url)) result = requests.post(url, files=files, headers=headers, verify=caBundle) checkAndShowResult(result, 201, 'Unable to upload file to Gitlab') assetUrl = projectUrl + json.loads(result.text)['url'] assets.append({'name': filename, 'url': assetUrl, 'id': "new-link-{}".format(len(assets))}) print('File {} successfully uploaded, url is {}'.format(filename, assetUrl)) # --- create release tag if createReleaseTag: url = ("%s/api/v4//projects/%s/repository/tags" % (instanceUrl, projectId)) headers = {"PRIVATE-TOKEN": privateToken, "Content-Type": "application/json"} payload = { "tag_name": releaseTag, "id": projectId, "ref": releaseTagTarget, "message": "Tag for release %s" % releaseName } if verbose: print("POST to {}".format(url)) result = requests.post(url, headers=headers, data=json.dumps(payload), verify=caBundle) checkAndShowResult(result, 201, 'Unable to create release tag') print('Tag successfully created') # --- create release if createRelease: url = "%s/api/v4/projects/%s/releases" % (instanceUrl, projectId) headers = {"PRIVATE-TOKEN": privateToken, "Content-Type": "application/json"} payload = { "name": releaseName, "tag_name": releaseTag, "description": releaseDescription } if verbose: print("POST to {}".format(url)) result = requests.post(url, headers=headers, data=json.dumps(payload), verify=caBundle) checkAndShowResult(result, 201, 'Unable to create release') print('Release successfully created') # --- update release in case of additional description if not createRelease and releaseDescription: # --- get release to fetch existing description url = "%s/api/v4/projects/%s/releases/%s" % (instanceUrl, projectId, releaseName) headers = {"PRIVATE-TOKEN": privateToken} if verbose: print("GET to {}".format(url)) result = requests.get(url, headers=headers, verify=caBundle) checkAndShowResult(result, 200, 'Unable to get release') existingDescription = json.loads(result.text)['description'] print("Existing description is {}".format(existingDescription)) releaseDescription += "\n\n---------------------------------------------\n\n" releaseDescription += existingDescription # --- update release url = "%s/api/v4/projects/%s/releases/%s" % (instanceUrl, projectId, releaseName) headers = {"PRIVATE-TOKEN": privateToken, "Content-Type": "application/json"} payload = { "name": releaseName, "tag_name": releaseTag, "description": releaseDescription } if verbose: print("PUT to {}".format(url)) result = requests.put(url, headers=headers, data=json.dumps(payload), verify=caBundle) checkAndShowResult(result, 200, 'Unable to update release') print('Release successfully update') # --- add assets # get existing assets headers = {"PRIVATE-TOKEN": privateToken} url = "%s/api/v4/projects/%s/releases/%s" % (instanceUrl, projectId, releaseName) if verbose: print("GET to {}".format(url)) result = requests.get(url, headers=headers, verify=caBundle) checkAndShowResult(result, 200, 'Unable to get release information') # add existing assets to list of assets links = json.loads(result.text)['assets']['links'] assets.extend(links) # delete existing assets headers = {"PRIVATE-TOKEN": privateToken} for link in links: assetId = link['id'] url = "%s/api/v4/projects/%s/releases/%s/assets/links/%s" % (instanceUrl, projectId, releaseName, assetId) if verbose: print("DELETE to {}".format(url)) result = requests.delete(url, headers=headers, verify=caBundle) checkAndShowResult(result, 200, 'Unable to delete asset') print("Asset {} successfully deleted".format(assetId)) # create all assets ("existing" and new) headers = {"PRIVATE-TOKEN": privateToken, "Content-Type": "application/json"} url = "%s/api/v4/projects/%s/releases/%s/assets/links" % (instanceUrl, projectId, releaseName) for asset in assets: if verbose: print("POST to {}".format(url)) result = requests.post(url, headers=headers, data=json.dumps(asset), verify=caBundle) checkAndShowResult(result, 201, "Unable to create asset") print("Asset {} successfully created".format(asset))