diff --git a/bin/release b/bin/release index 46ead83..d74e0bd 100755 --- a/bin/release +++ b/bin/release @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # This file is part of Galette Stripe plugin (https://galette-plugins.github.io/plugin-stripe). @@ -7,7 +7,7 @@ # import os, sys, argparse, re, git, http.client, subprocess, glob -import urlgrabber.progress, tarfile, shutil, gitdb, time, fnmatch +import tarfile, shutil, gitdb, time, fnmatch from datetime import datetime from termcolor import colored from urllib.parse import urlparse @@ -42,6 +42,38 @@ def print_err(msg): print(colored(msg, 'red', attrs=['bold'])) +def run(cmd, cwd=None): + """ + Run a command, and stop the build if it failed. + + Every subprocess here used to be started and waited for without its exit + code ever being looked at, so a failed composer install produced an archive + without vendor/ and the script still exited 0, announcing a release. + """ + p1 = subprocess.Popen(cmd, shell=True, cwd=cwd) + p1.communicate() + if p1.returncode != 0: + print_err('Command failed with exit code %d: %s' % (p1.returncode, cmd)) + exit(1) + + +def remote_exists(url): + """ + Check whether a URL exists, with a HEAD request. + + The scheme is honoured. The download repository is served over HTTPS, and + asking for it on port 80 answers a 301 rather than a 200, so this check used + to report that nothing existed whatever the version being built. + """ + parsed = urlparse(url) + if parsed.scheme == 'https': + connection = http.client.HTTPSConnection(parsed.netloc) + else: + connection = http.client.HTTPConnection(parsed.netloc) + connection.request('HEAD', parsed.path) + return connection.getresponse().status == 200 + + def get_numeric_version(ver): """ Returns all numeric version @@ -77,15 +109,15 @@ def propose_version(): last_minor = '0' for tagref in tagrefs: - tag = tagref.tag - if valid_version(tag.tag): + # name is set on annotated and lightweight tags alike + if valid_version(tagref.name): # last minor version is always the last one :) - if tag.tag > last_minor: - last_minor = tag.tag + if tagref.name > last_minor: + last_minor = tagref.name # last major version - if len(tag.tag) == 5 and tag.tag > last_major: - last_major = tag.tag + if len(tagref.name) == 5 and tagref.name > last_major: + last_major = tagref.name if verbose: print('last minor: %s | last major %s' % (last_minor, last_major)) @@ -116,14 +148,18 @@ def get_latest_version(): last = None for tagref in tagrefs: - tag = tagref.tag - if tag is not None and valid_version(tag.tag): + # name is set on annotated and lightweight tags alike + if valid_version(tagref.name): # last minor version is always the last one :) - if last is None or tag.tag > last.tag: - last = tag + if last is None or tagref.name > last.name: + last = tagref + + if last is None: + print_err('No version tag found, cannot guess a version to build!') + sys.exit(1) - tag_commit = last.hexsha - return last.tag + tag_commit = last.commit.hexsha + return last.name def is_existing_version(ver): @@ -131,10 +167,9 @@ def is_existing_version(ver): Look specified version exists """ for tagref in tagrefs: - tag = tagref.tag - if valid_version(tag.tag): - if tag.tag == ver: - return True + # name is set on annotated and lightweight tags alike + if valid_version(tagref.name) and tagref.name == ver: + return True return False def ask_user_confirm(msg): @@ -195,6 +230,9 @@ def _do_build(ver): archive_name ) + if not os.path.exists(local_dl_repo): + os.makedirs(local_dl_repo) + if not force: # first check if a version local = False @@ -206,12 +244,7 @@ def _do_build(ver): if is_local: exists = os.path.isfile(url) else: - parsed = urlparse(url) - - connection = http.client.HTTPConnection(parsed[1], 80) - connection.request('HEAD', parsed[2]) - response = connection.getresponse() - exists = response.status == 200 + exists = remote_exists(url) if not exists: # also check from local repo @@ -222,11 +255,7 @@ def _do_build(ver): if is_local: ascexists = os.path.isfile(urlasc) else: - ascparsed = urlparse(urlasc) - connection = http.client.HTTPConnection(ascparsed[1], 80) - connection.request('HEAD', ascparsed[2]) - response = connection.getresponse() - ascexists = response.status == 200 + ascexists = remote_exists(urlasc) if not ascexists: # also check from local repo @@ -254,7 +283,7 @@ def _do_build(ver): if msg is not None: msg += ' and has been %ssigned!' % loctxt else: - msg += 'Release has been %ssigned!' % loctxt + msg = 'Release has been %ssigned!' % loctxt msg += '\n\nYou will *NOT* build another one :)' print_err(msg) @@ -296,8 +325,7 @@ def _do_build(ver): else: print('Archiving GIT tag %s' % ver) - p1 = subprocess.Popen(archive_cmd, shell=True) - p1.communicate() + run(archive_cmd) print('Adding vendor libraries') add_libs(rel_name, galette_archive) @@ -315,8 +343,7 @@ def _do_build(ver): def do_sign(archive): sign_cmd = 'gpg --detach-sign --armor %s' % archive - p1 = subprocess.Popen(sign_cmd, shell=True) - p1.communicate() + run(sign_cmd) def do_upload(galette_archive): @@ -345,8 +372,7 @@ def do_scp(archive): else: scp_cmd = 'scp -r %s* %s:%s' % (archive, ssh_host, path) print(scp_cmd) - p1 = subprocess.Popen(scp_cmd, shell=True) - p1.communicate() + run(scp_cmd) def do_cp(archive): @@ -402,8 +428,7 @@ def add_libs(rel_name, galette_archive): # install php dependencies composer_cmd = 'composer install --ignore-platform-reqs --no-dev' print(composer_dir) - p1 = subprocess.Popen(composer_cmd, shell=True, cwd=composer_dir) - p1.wait() + run(composer_cmd, cwd=composer_dir) # cleaunp files not required in releases todrop = [ @@ -556,6 +581,11 @@ def main(): help='Be more verbose', action="store_true" ) + parser.add_argument( + '--no-sign', + help='Do not sign the archive', + action='store_true' + ) parser.add_argument( '-n', '--nightly', @@ -600,11 +630,15 @@ def main(): repo = git.Repo(galette_repo) tagrefs = repo.tags + assume_yes = args.assume_yes + if args.f == True: force = ask_user_confirm( 'Are you *REALLY* sure you mean -f when you typed -f? [yes/No] ' ) - assume_yes = args.assume_yes + + if args.no_sign: + sign = False if args.local: if not args.download_url: