Commit ff15b066 authored by Anton Melser's avatar Anton Melser 💬

Use local, debug-enhanced version of gitlab_release

parent 9e03cc00
image: alpine
variables:
LOG_LEVEL: DEBUG
stages:
- test
- release
......@@ -14,9 +17,10 @@ release-tag:
image: python:3.6-slim
script:
- apt update && apt -y install jq zip
- pip install gitlab_release
- cd src
- pip install requests
- pushd src
- zip -r -FS "../$(cat manifest.json | jq -j '.applications.gecko.id').xpi" *
- python -m gitlab_release "$PRIVATE_TOKEN" *.xpi
- popd
- python gitlab_release.py "$PRIVATE_TOKEN" *.xpi
only:
- tags
#!/usr/bin/env python3
import os
import io
import json
import codecs
import zipfile
import argparse
import itertools
import mimetypes
import urllib.error
from glob import glob
from urllib.request import Request, urlopen
from urllib.parse import urljoin, urlencode, quote
import requests
import logging
logging.basicConfig(level=os.getenv('LOG_LEVEL') or logging.WARNING)
class MultiPartForm(object):
"""Accumulate the data to be used when posting a form."""
def __init__(self):
self.form_fields = []
self.files = []
# self.boundary = mimetools.choose_boundary()
self.boundary = '----------lImIt_of_THE_fIle_eW_$'
return
def get_content_type(self):
return 'multipart/form-data; boundary=%s' % self.boundary
def add_field(self, name, value):
"""Add a simple field to the form data."""
self.form_fields.append((name, value))
return
def add_file(self, fieldname, filename, fileHandle, mimetype=None):
"""Add a file to be uploaded."""
body = fileHandle.read()
if mimetype is None:
mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
self.files.append((fieldname, filename, mimetype, body))
return
def get_binary(self):
"""Return a binary buffer containing the form data, including attached files."""
def to_bytes(s):
return s.encode('ascii') if isinstance(s, str) else s
part_boundary = '--' + self.boundary
binary = io.BytesIO()
needsCLRF = False
# Add the form fields
for name, value in self.form_fields:
if needsCLRF:
binary.write('\r\n')
needsCLRF = True
block = [part_boundary,
'Content-Disposition: form-data; name="%s"' % name,
'',
value
]
binary.write('\r\n'.join(block))
# Add the files to upload
for field_name, filename, content_type, body in self.files:
if needsCLRF:
binary.write('\r\n')
needsCLRF = True
block = [part_boundary,
str('Content-Disposition: file; name="%s"; filename="%s"' % \
(field_name, filename)),
'Content-Type: %s' % content_type,
''
]
binary.write(b'\r\n'.join([to_bytes(s) for s in block]))
binary.write(b'\r\n')
binary.write(to_bytes(body))
# add closing boundary marker,
binary.write(to_bytes('\r\n--' + self.boundary + '--\r\n'))
return binary
def main():
parser = argparse.ArgumentParser(description='Upload files to gitlab tag (release)')
parser.add_argument('--server', default=None, help='url of gitlab server or $CI_PROJECT_URL')
parser.add_argument('--project_id', default=None, help='Unique id of project, available in '
'Project Settings/General or $CI_PROJECT_ID')
parser.add_argument('--release_tag', default=None, help='Tag to upload files against or $CI_BUILD_TAG')
parser.add_argument('--timeout', type=int, default=120, help='Timeout for http requests')
parser.add_argument('--ignore_cert', action="store_true", help='Ignore ssl certificate failures')
parser.add_argument('--zip', help='Add all globbed files to provided zip name and upload that')
parser.add_argument('--description', default='', help='Extra text to add to the release description')
parser.add_argument('private_token', help='login token with permissions to commit to repo')
parser.add_argument('files', nargs="+", help='glob/s of files to upload')
args = parser.parse_args()
server = args.server or os.environ['CI_PROJECT_URL']
if not server:
print("Must provide --server if not running from CI")
exit(1)
logging.debug(f'Using {server} for value "server"')
project_id = args.project_id or os.environ['CI_PROJECT_ID']
if not project_id:
print("Must provide --project_id if not running from CI")
exit(1)
project_id = quote(project_id, safe='')
logging.debug(f'Using {project_id} for value "project_id"')
release_tag = args.release_tag or os.environ['CI_BUILD_TAG']
if not release_tag:
print("Must provide --release_tag if not running from CI")
exit(1)
logging.debug(f'Using {release_tag} for value "release_tag"')
verify = not args.ignore_cert
auth = {'PRIVATE-TOKEN': args.private_token}
logging.info(f"Uploading to {server} (id: {project_id}) @ {release_tag}")
if not server.endswith('/'):
server += '/'
api_url = urljoin(server, f"/api/v4/projects/{project_id}/")
all_files = list(itertools.chain(*[glob(f) for f in args.files]))
if args.zip:
with zipfile.ZipFile(args.zip, "w", zipfile.ZIP_DEFLATED) as zf:
def zipdir(path, ziph):
# ziph is zipfile handle
for root, dirs, files in os.walk(path):
for file in files:
ziph.write(os.path.join(root, file))
for fname in all_files:
print (fname)
if fname == args.zip:
continue
if os.path.isdir(fname):
zipdir(fname, zf)
else:
zf.write(fname)
all_files = [os.path.abspath(args.zip)]
logging.info(f"Uploading {all_files}")
uploads = []
if args.description:
uploads.append(args.description)
for fname in all_files:
with codecs.open(fname, 'rb') as filehandle:
upload_url = urljoin(api_url, 'uploads')
logging.debug(f"Uploading {fname} to {api_url}")
rsp = requests.post(upload_url, files={'file': filehandle}, headers=auth, verify=verify)
try:
rsp.raise_for_status()
except Exception as ex:
# print("Upload of {f} failed: {ex}".format(f=fname, ex=ex))
logging.exception(f"Upload of {fname} failed:")
raise
else:
uploads.append(rsp.json()['markdown'])
description = ' \n'.join(uploads)
logging.debug(f"Will update to description: {description}")
# Now we've got the uploaded file info, attach that to the tag
url = urljoin(api_url, f"repository/tags/{quote(release_tag, safe='')}")
logging.debug(f"Updating tag {release_tag} with uploaded files info at url {url}")
tag_details = requests.get(url, headers=auth, verify=verify).json()
method = requests.post
if 'release' in tag_details and tag_details['release'] is not None:
logging.debug(f"Release description found, adding to description: {tag_details['release']['description']}")
description = ' \n'.join((tag_details['release']['description'], description))
method = requests.put
rsp = method(url + '/release', data={'description': description}, headers=auth, verify=verify)
try:
rsp.raise_for_status()
except Exception as ex:
logging.exception(f"Setting tag description failed for description: {description}")
tagname = rsp.json()['tag_name']
endpoint = urljoin(server, f"tags/{quote(tagname)}")
logging.info(f"Uploaded {all_files} to tag {tagname}: {endpoint}")
if __name__ == '__main__':
main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment