diff --git a/release/release_bob.py b/release/release_bob.py index 098b21c47581340b4cba254b630fa1ded60037bd..d17234e29e769ba7f02bc5265dfaa53fcf854af9 100755 --- a/release/release_bob.py +++ b/release/release_bob.py @@ -93,7 +93,17 @@ def _insure_correct_package(candidates, group_name, pkg_name): # adapted from the same-name function in new_version.py script of bob.extension package def _update_readme(readme, version=None): - # replace the travis badge in the README.rst with the given version + """ + Inside text of the readme, replaces parts of the links to the provided version. + If version is not provided, replace to `stable` or `master`. + Args: + readme: Text of the README.rst file from a bob package + version: Format of the version string is '#.#.#' + + Returns: New text of readme with all replaces done + + """ + # replace the badge in the readme's text with the given version DOC_IMAGE = re.compile(r'\-(stable|(v\d+\.\d+\.\d+([abc]\d+)?))\-') BRANCH_RE = re.compile(r'/(stable|master|(v\d+\.\d+\.\d+([abc]\d+)?))') @@ -116,16 +126,22 @@ def _update_readme(readme, version=None): def get_latest_tag_name(gitpkg): + """ + Find the name of the latest tag for a given package in the format '#.#.#' + Args: + gitpkg: gitlab package object + + Returns: The name of the latest tag in format '#.#.#'. None if no tags for the package were found. + + """ # get 50 latest tags as a list latest_tags = gitpkg.tags.list(all=True) if not latest_tags: return None # create list of tags' names but ignore the first 'v' character in each name tag_names = [tag.name[1:] for tag in latest_tags] - print(tag_names) # sort them correctly according to each subversion number tag_names.sort(key=StrictVersion) - print(tag_names) # take the last one, as it is the latest tag in the sorted tags latest_tag_name = tag_names[-1] return latest_tag_name @@ -171,6 +187,17 @@ def get_parsed_tag(gitpkg, tag): def update_tag_comments(gitpkg, tag_name, tag_comments_list, dry_run=False): + """ + Write annotations inside the provided tag of a given package. + Args: + gitpkg: gitlab package object + tag_name: The name of the tag to update + tag_comments_list: New annotations for this tag in a form of list + dry_run: If True, nothing will be committed or pushed to GitLab + + Returns: The gitlab object for the tag that was updated + + """ # get tag and update its description tag = gitpkg.tags.get(tag_name) print('Found tag {1}, updating its comments with:'.format(gitpkg.name, tag.name)) @@ -180,26 +207,42 @@ def update_tag_comments(gitpkg, tag_name, tag_comments_list, dry_run=False): return tag -def commit_files(gitpkg, files_list, message='Updated files', dry_run=False): +def commit_files(gitpkg, files_dict, message='Updated files', dry_run=False): + """ + Commit files of a given GitLab package. + Args: + gitpkg: gitlab package object + files_dict: Dictionary of file names and their contents (as text) + message: Commit message + dry_run: If True, nothing will be committed or pushed to GitLab + + """ data = { 'branch': 'master', # v4 'commit_message': message, 'actions': [] } # add files to update - for filename in files_list.keys(): + for filename in files_dict.keys(): update_action = dict(action='update', file_path=filename) - # with open(filename, 'r') as f: - # update_action['content'] = f.read() - update_action['content'] = files_list[filename] + update_action['content'] = files_dict[filename] data['actions'].append(update_action) - print("Committing changes in files: {0}".format(str(files_list.keys()))) + print("Committing changes in files: {0}".format(str(files_dict.keys()))) if not dry_run: gitpkg.commits.create(data) def get_last_nonskip_pipeline(gitpkg, before_last=False): + """ + Returns the last running pipeline or the one before the last. + Args: + gitpkg: gitlab package object + before_last: If True, the pipeline before the last is returned + + Returns: The gtilab object of the pipeline + + """ # sleep for 10 seconds to ensure that if a pipeline was just submitted, # we can retrieve it time.sleep(10) @@ -212,6 +255,15 @@ def get_last_nonskip_pipeline(gitpkg, before_last=False): def just_build_package(gitpkg, dry_run=False): + """ + Restrt the last runnable pipeline of the package + Args: + gitpkg: gitlab package object + dry_run: If True, the pipeline will not be actually restarted on GitLab + + Returns: + + """ # 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 last_pipeline = get_last_nonskip_pipeline(gitpkg, before_last=True) @@ -232,7 +284,15 @@ def just_build_package(gitpkg, dry_run=False): last_pipeline.retry() -def wait_for_pipeline_to_finish(gitpkg, tag, dry_run=False): +def wait_for_pipeline_to_finish(gitpkg, dry_run=False): + """ + Using sleep function, wait for the latest pipeline to finish building. + This function pauses the script until pipeline completes either successfully or with error. + Args: + gitpkg: gitlab package object + dry_run: If True, print log message and exit. There wil be no waiting. + + """ sleep_step = 30 max_sleep = 60 * 60 # one hour pipeline = get_last_nonskip_pipeline(gitpkg, before_last=True) @@ -264,12 +324,28 @@ def wait_for_pipeline_to_finish(gitpkg, tag, dry_run=False): def cancel_last_pipeline(gitpkg): + """ + Cancel the last started pipeline of a package + Args: + gitpkg: gitlab package object + + """ pipeline = get_last_nonskip_pipeline(gitpkg) print('Cancelling the last pipeline {0} of project {1}'.format(pipeline.id, gitpkg.name)) pipeline.cancel() def release_package(gitpkg, tag_name, tag_comments_list, dry_run=False): + """ + Release package. The provided tag will be annotated with a given list of comments. + README.rst and version.txt files will also be updated according to the release procedures. + Args: + gitpkg: gitlab package object + tag_name: The name of the release tag + tag_comments_list: New annotations for this tag in a form of list + dry_run: If True, nothing will be committed or pushed to GitLab + + """ # if there is nothing to release, just rebuild the package if tag_name == 'none': print("Since the tag is 'none', we just re-build the last pipeline") @@ -305,6 +381,19 @@ def release_package(gitpkg, tag_name, tag_comments_list, dry_run=False): def parse_and_process_package_changelog(gl, bob_group, pkg_name, package_changelog, dry_run=False): + """ + Process the changelog of a single package. Parse the log following specific format. + Update annotations of the provided older tags and release the package by following the last tag description. + Args: + gl: Gitlab API object + bob_group: gitlab object for the group + pkg_name: name of the package + package_changelog: the changelog corresponding to the provided package + dry_run: If True, nothing will be committed or pushed to GitLab + + Returns: gitlab handle for the package, name of the latest tag, and tag's comments + + """ cur_tag = None cur_tag_comments = [] @@ -332,6 +421,17 @@ def parse_and_process_package_changelog(gl, bob_group, pkg_name, package_changel def main(private_token, group_name='bob', changelog_file='changelog.rst', dry_run=False, package=None, resume=False): + """ + Main function that updates and releases packages according to the provided changelog file. + Args: + private_token: GitLab token with the access rights to update and release Bob's packages + group_name: Name of the group in GitLab. By default, the name of the group is 'bob' + changelog_file: The changelog file that defines which packages will be processed + dry_run: If True, nothing will be committed or pushed to GitLab + package: If provided, only this package will be processed + resume: If True, the processing of changelog will be resumed starting with the provided package + + """ gl = gitlab.Gitlab('https://gitlab.idiap.ch', private_token=private_token, api_version=4) bob_group = gl.groups.list(search=group_name)[0] @@ -362,7 +462,7 @@ def main(private_token, group_name='bob', changelog_file='changelog.rst', dry_ru if gitpkg: release_package(gitpkg, tag, tag_comments, dry_run) # now, wait for the pipeline to finish, before we can release the next package - wait_for_pipeline_to_finish(gitpkg, tag, dry_run) + wait_for_pipeline_to_finish(gitpkg, dry_run) # if package name is provided and resume is not set, process only this package if package == cur_package_name and not resume: