|
#!/usr/bin/env python3 |
|
|
|
"""Automate preparation of files for deployment to Bintray from CI. |
|
|
|
Currently works with: |
|
|
|
- Travis CI |
|
- AppVeyor |
|
""" |
|
|
|
import json |
|
import logging |
|
import os |
|
import pathlib |
|
import sys |
|
import zipfile |
|
|
|
from version_query import predict_version_str |
|
|
|
__updated__ = '2018-04-16' |
|
|
|
_LOG = logging.getLogger(__name__) |
|
|
|
BINTRAY_DESCRIPTOR_PATH = pathlib.Path('.bintray.json') |
|
BINTRAY_ARCHIVE_SUFFIX = '-bintray.zip' |
|
BINTRAY_VERSION_PATH = pathlib.Path('.bintray_version.txt') |
|
|
|
|
|
def preprocess_git_version_tag(tag: str): |
|
"""Remove a prefix from a version tag.""" |
|
# TODO: this will be in version_query |
|
if tag.startswith('ver'): |
|
return tag[3:] |
|
if tag.startswith('v'): |
|
return tag[1:] |
|
if tag and tag[0] in ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9'): |
|
return tag |
|
raise ValueError('given tag "{}" does not appear to be a version tag'.format(tag)) |
|
|
|
|
|
def query_provider(): |
|
"""Determine on which CI this script is running.""" |
|
if os.environ.get('TRAVIS', None): |
|
return "travis" |
|
if os.environ.get('APPVEYOR', None): |
|
return "appveyor" |
|
raise NotImplementedError('could not detect the provider') |
|
|
|
|
|
def query_build_metadata(provider: str): |
|
"""Gather build metadata to prepare deployment.""" |
|
provider = provider.lower() |
|
if provider == 'travis': |
|
owner, _, project = os.environ['TRAVIS_REPO_SLUG'].partition('/') |
|
return { |
|
'owner': owner, |
|
'project': project, |
|
'sha': os.environ['TRAVIS_COMMIT'], |
|
'tag': os.environ.get('TRAVIS_TAG', None)} |
|
if provider == 'appveyor': |
|
return { |
|
'owner': os.environ['APPVEYOR_ACCOUNT_NAME'], |
|
'project': os.environ['APPVEYOR_PROJECT_NAME'], |
|
'sha': os.environ['APPVEYOR_REPO_COMMIT'], |
|
'tag': os.environ.get('APPVEYOR_REPO_TAG_NAME', None)} |
|
raise NotImplementedError('provider {} is not supported'.format(provider)) |
|
|
|
|
|
def determine_deployed_version(metadata): |
|
"""Prepare exact version identifier for the deployed package.""" |
|
if metadata['tag']: |
|
try: |
|
return preprocess_git_version_tag(metadata['tag']) |
|
except ValueError: |
|
_LOG.warning('keeping non-version tag %s as-is', metadata['tag']) |
|
return metadata['tag'] |
|
default_version = predict_version_str() |
|
if '+' in default_version: |
|
return default_version |
|
return '{}+{}'.format(default_version, metadata['sha'][:8]) |
|
|
|
|
|
def prepare_for_deployment(platform: str, *artifact_patterns): |
|
"""Prepare for Bintray deployment.""" |
|
provider = query_provider() |
|
_LOG.info('executing on %s', provider) |
|
metadata = query_build_metadata(provider) |
|
_LOG.info('gathered build metadata %s', metadata) |
|
version = determine_deployed_version(metadata) |
|
if provider == 'travis': |
|
vcs_label = metadata['tag'] if metadata['tag'] else metadata['sha'] |
|
make_travis_bintray_descriptor(metadata['owner'], metadata['project'], version, platform, |
|
vcs_label, *artifact_patterns) |
|
elif provider == 'appveyor': |
|
make_appveyor_bintray_archive(metadata['project'], version, platform, *artifact_patterns) |
|
with BINTRAY_VERSION_PATH.open('w') as version_file: |
|
print(version, file=version_file) |
|
_LOG.info('prepared AppVeyor bintray version file at "%s"', BINTRAY_VERSION_PATH.resolve()) |
|
with BINTRAY_VERSION_PATH.open('r') as version_file: |
|
_LOG.debug('the version in the bintray version file:\n%s', version_file.read()) |
|
|
|
|
|
def make_travis_bintray_descriptor(subject: str, package: str, version: str, platform: str, |
|
vcs_label: str, *artifact_patterns): |
|
"""Generate the Bintray descriptor file for Travis CI deployment.""" |
|
_LOG.info('making Travis bintray descriptor') |
|
upload_path = pathlib.Path('{}-v{}'.format(package, version), platform) |
|
files = [] |
|
for artifact_pattern in artifact_patterns: |
|
for pth in pathlib.Path('.').glob(artifact_pattern): |
|
if not pth.is_file(): |
|
continue |
|
moved_pth = pth |
|
if '+' in str(pth): |
|
moved_pth = pathlib.Path(str(pth).replace('+', '_')) |
|
_LOG.warning('renaming "%s" to "%s" because of "+" in the path', pth, moved_pth) |
|
pth.rename(moved_pth) |
|
files.append({'includePattern': str(moved_pth), |
|
'uploadPattern': str(upload_path.joinpath(pth.name))}) |
|
if files: |
|
_LOG.debug('files to be uploaded: %s', files) |
|
else: |
|
_LOG.error('no files to be uploaded') |
|
data = { |
|
'package': {'subject': subject, 'repo': 'pkgs', 'name': package}, |
|
'version': { |
|
'name': version, 'desc': '', 'released': '', |
|
'vcs_tag': vcs_label, |
|
'attributes': [], 'gpgSign': False}, |
|
'files': files, |
|
'publish': True} |
|
with BINTRAY_DESCRIPTOR_PATH.open('w') as desc_file: |
|
json.dump(data, desc_file, indent='\t') |
|
_LOG.info('prepared Travis bintray descriptor at "%s"', BINTRAY_DESCRIPTOR_PATH.resolve()) |
|
with BINTRAY_DESCRIPTOR_PATH.open('r') as desc_file: |
|
_LOG.debug('the descriptor file:\n%s', desc_file.read()) |
|
|
|
|
|
def make_appveyor_bintray_archive(package: str, version: str, platform: str, *artifact_patterns): |
|
"""Generate the Bintray archive for AppVeyor deployment.""" |
|
_LOG.info('making AppVeyor bintray archive') |
|
zip_path = '{}-v{}-{}{}'.format(package, version, platform, BINTRAY_ARCHIVE_SUFFIX) |
|
archive_dir = pathlib.Path('{}-v{}'.format(package, version), platform) |
|
with zipfile.ZipFile(zip_path, 'w' if sys.version_info < (3, 5) else 'x') as zip_file: |
|
for artifact_pattern in artifact_patterns: |
|
for pth in pathlib.Path('.').glob(artifact_pattern): |
|
if pth.is_file(): |
|
archived_path = archive_dir.joinpath(pth.name) |
|
zip_file.write(str(pth), str(archived_path)) |
|
_LOG.debug('added "%s" to archive as "%s"', pth, archived_path) |
|
_LOG.info('prepared AppVeyor bintray archive at "%s"', pathlib.Path(zip_path).resolve()) |
|
|
|
|
|
def main(args=None): |
|
if args is None: |
|
args = sys.argv[1:] |
|
_LOG.info('using script args: %s', args) |
|
prepare_for_deployment(*args) |
|
|
|
|
|
if __name__ == '__main__': |
|
logging.basicConfig(level=logging.DEBUG) |
|
main() |