Skip to content
Snippets Groups Projects
Commit 6fe3e6f1 authored by Pavel KORSHUNOV's avatar Pavel KORSHUNOV
Browse files

added options to resume release, single package release, and dry-run

parent 4bd13dcd
No related branches found
No related tags found
1 merge request!75The release script for bob that uses changelog as a guidance for release
#!/usr/bin/env python #!/usr/bin/env python
""" """
By using changelog file as an input (can be generated with 'generate_changelog.py' script), this script goes through all packages in changelog file (in order listed), tags them correctly as per the file, an releases them one by one. This script uses python-gitlab package for accessing GitLab's API. By using changelog file as an input (can be generated with 'generate_changelog.py' script),
this script goes through all packages in changelog file (in order listed), tags them correctly
as per the file, and releases them one by one. A script can also be used to release a single package.
This script uses python-gitlab package for accessing GitLab's API.
Usage: Usage:
{0} [-v...] [options] [--] <private_token> {0} [-v...] [options] [--] <private_token>
{0} -h | --help {0} -h | --help
{0} --version {0} --version
Arguments: Arguments:
<private_token> Private token used to access GitLab. <private_token> Private token used to access GitLab.
Options: Options:
-h --help Show this screen. -h --help Show this screen.
--version Show version. --version Show version.
-c, --changelog-file STR A changelog file with all packages to release with their tags, listed in order. -c, --changelog-file STR A changelog file with all packages to release with their tags, listed in order.
[default: changelog_since_last_release.rst]. [default: changelog_since_last_release.rst].
-g, --group-name STR Group name where we are assuming that all packages are located. -g, --group-name STR Group name where we are assuming that all packages are located.
[default: bob]. [default: bob].
-p, --package STR If the name of a package is provided, then this package will be found
in the changelog file and the release will resume from it (if option --resume is set)
or only this package will be released.
-r, --resume The overall release will resume from the provided package name.
-q, --dry-run Only print the actions, but do not execute them.
""" """
import sys import sys
...@@ -97,16 +106,17 @@ def get_parsed_tag(gitpkg, tag): ...@@ -97,16 +106,17 @@ def get_parsed_tag(gitpkg, tag):
raise ValueError('Cannot parse changelog tag {0} of the package {1}'.format(tag, gitpkg.name)) raise ValueError('Cannot parse changelog tag {0} of the package {1}'.format(tag, gitpkg.name))
def update_tag_comments(gitpkg, tag_name, tag_comments_list): def update_tag_comments(gitpkg, tag_name, tag_comments_list, dry_run=False):
# get tag and update its description # get tag and update its description
tag = gitpkg.tags.get(tag_name) tag = gitpkg.tags.get(tag_name)
print('package {0}, tag {1}, updating comments with:'.format(gitpkg.name, tag.name)) print('Found tag {1}, updating its comments with:'.format(gitpkg.name, tag.name))
print(tag_comments_list) print(tag_comments_list)
tag.set_release_description('\n'.join(tag_comments_list)) if not dry_run:
tag.set_release_description('\n'.join(tag_comments_list))
return tag return tag
def commit_files(gitpkg, files_list, message='Updated files'): def commit_files(gitpkg, files_list, message='Updated files', dry_run=False):
data = { data = {
'branch': 'master', # v4 'branch': 'master', # v4
'commit_message': message, 'commit_message': message,
...@@ -120,11 +130,12 @@ def commit_files(gitpkg, files_list, message='Updated files'): ...@@ -120,11 +130,12 @@ def commit_files(gitpkg, files_list, message='Updated files'):
update_action['content'] = files_list[filename] update_action['content'] = files_list[filename]
data['actions'].append(update_action) data['actions'].append(update_action)
print("Committing changes") print("Committing changes in files: {0}".format(str(files_list.keys())))
gitpkg.commits.create(data) if not dry_run:
gitpkg.commits.create(data)
def just_build_package(gitpkg): def just_build_package(gitpkg, dry_run=False):
# we assume the last pipeline is with commit [skip ci] # we assume the last pipeline is with commit [skip ci]
# so, we take the pipeline that can be re-built, which the previous to the last one # so, we take the pipeline that can be re-built, which the previous to the last one
last_pipeline = gitpkg.pipelines.list(per_page=2, page=1)[1] last_pipeline = gitpkg.pipelines.list(per_page=2, page=1)[1]
...@@ -141,11 +152,11 @@ def just_build_package(gitpkg): ...@@ -141,11 +152,11 @@ def just_build_package(gitpkg):
'is "{1}" instead of the expected "sucess"'.format(last_pipeline.id, last_pipeline.status)) 'is "{1}" instead of the expected "sucess"'.format(last_pipeline.id, last_pipeline.status))
print("Retrying pipeline {0}".format(last_pipeline.id)) print("Retrying pipeline {0}".format(last_pipeline.id))
print(last_pipeline) if not dry_run:
last_pipeline.retry() last_pipeline.retry()
def wait_for_pipeline_to_finish(gitpkg, tag): def wait_for_pipeline_to_finish(gitpkg, tag, dry_run=False):
sleep_step = 30 sleep_step = 30
max_sleep = 60 * 60 # one hour max_sleep = 60 * 60 # one hour
if tag == 'none': if tag == 'none':
...@@ -155,8 +166,14 @@ def wait_for_pipeline_to_finish(gitpkg, tag): ...@@ -155,8 +166,14 @@ def wait_for_pipeline_to_finish(gitpkg, tag):
# otherwise just take the last pipeline # otherwise just take the last pipeline
pipeline = gitpkg.pipelines.list(per_page=1, page=1)[0] pipeline = gitpkg.pipelines.list(per_page=1, page=1)[0]
# probe and wait for the pipeline to finish
pipeline_id = pipeline.id pipeline_id = pipeline.id
print('Waiting for the pipeline {0} of package {1} to finish. Do not interrupt.'.format(pipeline_id, gitpkg.name))
if dry_run:
return
# probe and wait for the pipeline to finish
slept_so_far = 0 slept_so_far = 0
while pipeline.status == 'running' or pipeline.status == 'pending': while pipeline.status == 'running' or pipeline.status == 'pending':
time.sleep(sleep_step) time.sleep(sleep_step)
...@@ -172,8 +189,10 @@ def wait_for_pipeline_to_finish(gitpkg, tag): ...@@ -172,8 +189,10 @@ def wait_for_pipeline_to_finish(gitpkg, tag):
raise ValueError('Pipeline {0} of project {1} exited with undesired status "{2}". ' raise ValueError('Pipeline {0} of project {1} exited with undesired status "{2}". '
'Release is not possible.'.format(pipeline_id, gitpkg.name, pipeline.status)) 'Release is not possible.'.format(pipeline_id, gitpkg.name, pipeline.status))
print('Pipeline {0} of package {1} succeeded. Continue processing.'.format(pipeline_id, gitpkg.name))
def release_package(gitpkg, tag_name, tag_comments_list): def release_package(gitpkg, tag_name, tag_comments_list, dry_run=False):
# if there is nothing to release, just rebuild the package # if there is nothing to release, just rebuild the package
if tag_name == 'none': if tag_name == 'none':
print("Since the tag is 'none', we just re-build the last pipeline") print("Since the tag is 'none', we just re-build the last pipeline")
...@@ -186,25 +205,26 @@ def release_package(gitpkg, tag_name, tag_comments_list): ...@@ -186,25 +205,26 @@ def release_package(gitpkg, tag_name, tag_comments_list):
readme_content = _update_readme(readme_content, version=version_number) readme_content = _update_readme(readme_content, version=version_number)
# commit and push changes # commit and push changes
commit_files(gitpkg, {'README.rst': readme_content, 'version.txt': version_number}, commit_files(gitpkg, {'README.rst': readme_content, 'version.txt': version_number},
'Increased stable version to %s' % version_number) 'Increased stable version to %s' % version_number, dry_run)
# 2. Tag package with new tag and push # 2. Tag package with new tag and push
print("creating tag {}".format(tag_name)) print("Creating tag {}".format(tag_name))
print('package {0}, tag {1}, updating comments with:'.format(gitpkg.name, tag_name)) print("updating tag's comments with:".format(gitpkg.name, tag_name))
print(tag_comments_list) print(tag_comments_list)
tag = gitpkg.tags.create({'tag_name': tag_name, 'ref': 'master'}) if not dry_run:
# update tag with comments tag = gitpkg.tags.create({'tag_name': tag_name, 'ref': 'master'})
tag.set_release_description('\n'.join(tag_comments_list)) # update tag with comments
tag.set_release_description('\n'.join(tag_comments_list))
# 3. Replace branch tag in Readme to master, change version file to beta version tag. Git add, commit, and push. # 3. Replace branch tag in Readme to master, change version file to beta version tag. Git add, commit, and push.
readme_content = _update_readme(readme_content) readme_content = _update_readme(readme_content)
version_number += 'b0' version_number += 'b0'
# commit and push changes # commit and push changes
commit_files(gitpkg, {'README.rst': readme_content, 'version.txt': version_number}, commit_files(gitpkg, {'README.rst': readme_content, 'version.txt': version_number},
'Increased latest version to %s [skip ci]' % version_number) 'Increased latest version to %s [skip ci]' % version_number, dry_run)
def parse_and_process_package_changelog(gl, bob_group, pkg_name, package_changelog): def parse_and_process_package_changelog(gl, bob_group, pkg_name, package_changelog, dry_run=False):
cur_tag = None cur_tag = None
cur_tag_comments = [] cur_tag_comments = []
...@@ -218,7 +238,7 @@ def parse_and_process_package_changelog(gl, bob_group, pkg_name, package_changel ...@@ -218,7 +238,7 @@ def parse_and_process_package_changelog(gl, bob_group, pkg_name, package_changel
if ' *' == line[:3]: # a tag level if ' *' == line[:3]: # a tag level
# write the comments collected for the previous tag # write the comments collected for the previous tag
if cur_tag: if cur_tag:
update_tag_comments(gitpkg, cur_tag, cur_tag_comments) update_tag_comments(gitpkg, cur_tag, cur_tag_comments, dry_run)
cur_tag_comments = [] # reset comments cur_tag_comments = [] # reset comments
# parse the current tag name # parse the current tag name
...@@ -231,7 +251,7 @@ def parse_and_process_package_changelog(gl, bob_group, pkg_name, package_changel ...@@ -231,7 +251,7 @@ def parse_and_process_package_changelog(gl, bob_group, pkg_name, package_changel
return gitpkg, cur_tag, cur_tag_comments return gitpkg, cur_tag, cur_tag_comments
def main(private_token, group_name='bob', changelog_file='changelog.rst'): def main(private_token, group_name='bob', changelog_file='changelog.rst', dry_run=False, package=None, resume=False):
gl = gitlab.Gitlab('https://gitlab.idiap.ch', private_token=private_token, api_version=4) gl = gitlab.Gitlab('https://gitlab.idiap.ch', private_token=private_token, api_version=4)
bob_group = gl.groups.list(search=group_name)[0] bob_group = gl.groups.list(search=group_name)[0]
...@@ -244,19 +264,35 @@ def main(private_token, group_name='bob', changelog_file='changelog.rst'): ...@@ -244,19 +264,35 @@ def main(private_token, group_name='bob', changelog_file='changelog.rst'):
# find the starts of each package's description in the changelog # find the starts of each package's description in the changelog
pkgs = numpy.asarray([i for i, line in enumerate(changelog) if line[0] == '*']) pkgs = numpy.asarray([i for i, line in enumerate(changelog) if line[0] == '*'])
pkgs = numpy.concatenate([pkgs, [len(changelog)]]) pkgs = numpy.concatenate([pkgs, [len(changelog)]])
for i in range(pkgs.shape[0] - 1): start_idx = 0
print('Processing package {0}'.format(changelog[pkgs[i]])) if package:
# print(changelog[pkgs[i] + 1: pkgs[i + 1]]) # get the index where the package first appears in the list
gitpkg, tag, tag_comments = parse_and_process_package_changelog(gl, bob_group, changelog[pkgs[i]][1:].strip(), start_idx = [i for i, line in enumerate(changelog) if line[1:].strip() == package]
changelog[pkgs[i] + 1: pkgs[i + 1]]) if not start_idx:
print('Package {0} was not found in the changelog'.format(package))
return
start_idx = start_idx[0]
for i in range(start_idx, pkgs.shape[0] - 1):
cur_package_name = changelog[pkgs[i]][1:].strip()
print('\nProcessing package {0}'.format(changelog[pkgs[i]]))
gitpkg, tag, tag_comments = parse_and_process_package_changelog(gl, bob_group, cur_package_name,
changelog[pkgs[i] + 1: pkgs[i + 1]], dry_run)
# release the package with the found tag and its comments # release the package with the found tag and its comments
if gitpkg: if gitpkg:
release_package(gitpkg, tag, tag_comments) release_package(gitpkg, tag, tag_comments, dry_run)
# now, wait for the pipeline to finish, before we can release the next package # now, wait for the pipeline to finish, before we can release the next package
wait_for_pipeline_to_finish(gitpkg, tag) wait_for_pipeline_to_finish(gitpkg, tag, dry_run)
# if package name is provided and resume is not set, process only this package
if package == cur_package_name and not resume:
break
print('\nFinished processing changelog')
if __name__ == '__main__': if __name__ == '__main__':
arguments = docopt(__doc__.format(sys.argv[0]), version='Changelog 0.0.1') arguments = docopt(__doc__.format(sys.argv[0]), version='Changelog 0.0.1')
main(arguments['<private_token>'], group_name=arguments['--group-name'], main(arguments['<private_token>'], group_name=arguments['--group-name'],
changelog_file=arguments['--changelog-file']) changelog_file=arguments['--changelog-file'], dry_run=arguments['--dry-run'],
package=arguments['--package'], resume=arguments['--resume'])
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment