diff --git a/bob/devtools/release.py b/bob/devtools/release.py index a26b04a6e5ffe2f84121159377dc2ff2703f79b4..a1cc6cdd6cc50127d0c13530f676bfeec9f722ba 100644 --- a/bob/devtools/release.py +++ b/bob/devtools/release.py @@ -228,8 +228,62 @@ def update_tag_comments(gitpkg, tag_name, tag_comments_list, dry_run=False): return tag -def commit_files(gitpkg, files_dict, message='Updated files', dry_run=False): - """Commit files of a given GitLab package. +def update_files_with_mr(gitpkg, files_dict, message, branch, automerge, + dry_run): + """Update (via a commit) files of a given gitlab package, through an MR + + This function can update a file in a gitlab package, but will do this + through a formal merge request. + + Args: + + gitpkg: gitlab package object + files_dict: Dictionary of file names and their contents (as text) + message: Commit message + branch: The branch name to use for the merge request + automerge: If we should set the "merge if build suceeds" flag on the + created MR + dry_run: If True, nothing will be pushed to gitlab + + """ + + data = { + 'branch': branch, + 'start_branch': 'master', + 'commit_message': message, + 'actions': [] + } + + # add files to update + for filename in files_dict.keys(): + update_action = dict(action='update', file_path=filename) + update_action['content'] = files_dict[filename] + data['actions'].append(update_action) + + logger.debug("Committing changes in files (%s) to new branch '%s'", + ', '.join(files_dict.keys()), branch) + if not dry_run: + commit = gitpkg.commits.create(data) + + logger.debug("Creating merge request %s -> master", branch) + logger.debug("Set merge-when-pipeline-succeeds = %s", automerge) + if not dry_run: + mr = project.mergerequests.create({ + 'source_branch': branch, + 'target_branch': 'master', + 'title': message, + }) + accept = { + 'merge_when_pipeline_succeeds': 'true' if automerge else 'false', + 'should_remove_source_branch': 'true', + } + mr.merge(accept) + + + +def update_files_at_master(gitpkg, files_dict, message, dry_run): + """Update (via a commit) files of a given gitlab package, directly on the + master branch. Args: @@ -398,7 +452,7 @@ def release_package(gitpkg, tag_name, tag_comments_list, dry_run=False): readme_content = readme_file.decode().decode() readme_content = _update_readme(readme_content, version_number) # commit and push changes - commit_files(gitpkg, + update_files_at_master(gitpkg, { 'README.rst': readme_content, 'version.txt': version_number @@ -427,7 +481,7 @@ def release_package(gitpkg, tag_name, tag_comments_list, dry_run=False): major, minor, patch = version_number.split('.') version_number = '{}.{}.{}b0'.format(major, minor, int(patch)+1) # commit and push changes - commit_files(gitpkg, { + update_files_at_master(gitpkg, { 'README.rst': readme_content, 'version.txt': version_number, }, diff --git a/bob/devtools/scripts/commitfile.py b/bob/devtools/scripts/commitfile.py new file mode 100644 index 0000000000000000000000000000000000000000..c1e9cae335dbc5f1d23473057e82d419a3fb79cf --- /dev/null +++ b/bob/devtools/scripts/commitfile.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +import os +import logging +logger = logging.getLogger(__name__) + +import click + +from . import bdt +from ..log import verbosity_option +from ..release import get_gitlab_instance, update_files_with_mr + + +@click.command(epilog=''' +Examples: + + 1. Replaces the README.rst file on the package bob/bob.extension, through a merge-request, using the contents of the local file with the same name: + + $ bdt commitfile -vv bob/bob.extension README.rst + + + 2. Replaces the README.rst file on the package beat/beat.core, specifying a commit/merge-request message: + +\b + $ bdt commitfile -vv --message="[readme] Update [ci skip]" beat/beat.core README.rst + + + 3. Replaces the file conda/meta.yaml on the package bob/bob.blitz through a merge request, specifying a commit/merge-request message, using the contents of the local file new.yaml, set merge-when-pipeline-succeeds, and the name of the branch to be creatd: + +\b + $ bdt commitfile -vv bob/bob.blitz --path=conda/meta.yaml --branch=conda-changes --auto-merge new.yaml + +''') +@click.argument('package') +@click.argument('file', type=click.Path(file_okay=True, dir_okay=False, + exists=True)) +@click.option('-m', '--message', + help='Message to set for this commit',) +@click.option('-p', '--path', + help='Which path to replace on the remote package',) +@click.option('-b', '--branch', + help='Name of the branch to create for this MR',) +@click.option('-a', '--auto-merge/--no-auto-merge', default=False, + help='If set, then the created merge request will be merged when ' \ + 'a potentially associated pipeline succeeds') +@click.option('-d', '--dry-run/--no-dry-run', default=False, + help='Only goes through the actions, but does not execute them ' \ + '(combine with the verbosity flags - e.g. ``-vvv``) to enable ' \ + 'printing to help you understand what will be done') +@verbosity_option() +@bdt.raise_on_error +def commitfile(package, message, file, path, branch, auto_merge, dry_run): + """Changes a file on a given package, directly on the master branch + """ + + if '/' not in package: + raise RuntimeError('PACKAGE should be specified as "group/name"') + + gl = get_gitlab_instance() + + # we lookup the gitlab package once + use_package = gl.projects.get(package) + logger.info('Found gitlab project %s (id=%d)', + use_package.attributes['path_with_namespace'], use_package.id) + + # if we are in a dry-run mode, let's let it be known + if dry_run: + logger.warn('!!!! DRY RUN MODE !!!!') + logger.warn('Nothing is being committed to Gitlab') + + path = path or file + + # load file contents + with open(file, 'rt') as f: + contents = f.read() + + components = os.path.splitext(path)[0].split(os.sep) + branch = 'update-%s' % components[-1].lower() + message = message or ("[%s] update" % \ + ''.join(['[%s]' % k for k in components])) + + # commit and push changes + update_files_with_mr(use_package, {path: contents}, message, branch, + auto_merge, dry_run) diff --git a/conda/meta.yaml b/conda/meta.yaml index b99a1a9b5095aabf63a3cb2fcd355f482dfdf3e7..09cd327e9f57649e6bb1aac48c8571f9b6202b02 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -59,6 +59,7 @@ test: - bdt --help - bdt lasttag --help #- bdt lasttag -vv bob/bob.devtools + - bdt commitfile --help - bdt changelog --help #- bdt changelog -vv bob/bob.devtools changelog.md - bdt release --help diff --git a/setup.py b/setup.py index 3b9dca20482d7cfb712aca91f45e39c8b2a0f596..d1e0f1abbe471654a8b0f7d2f6111d7fed83764e 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,7 @@ setup( 'bdt.cli': [ 'release = bob.devtools.scripts.release:release', 'new = bob.devtools.scripts.new:new', + 'commitfile = bob.devtools.scripts.commitfile:commitfile', 'changelog = bob.devtools.scripts.changelog:changelog', 'lasttag = bob.devtools.scripts.lasttag:lasttag', 'visibility = bob.devtools.scripts.visibility:visibility',