diff --git a/git_redmine.py b/git_redmine.py index bbc273a..c11b2e5 100644 --- a/git_redmine.py +++ b/git_redmine.py @@ -1,24 +1,22 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # pip install --user python-redmine click -from __future__ import print_function -from datetime import datetime, timedelta +import configparser +import glob import os import re -import git -import configparser -import unidecode -import tempfile -import glob import subprocess +import tempfile +from datetime import datetime, timedelta -from requests.adapters import HTTPAdapter -from redminelib import Redmine import click +import git +import unidecode +from redminelib import Redmine +from requests.adapters import HTTPAdapter MARKER = '# Everything below is ignored\n' @@ -75,11 +73,11 @@ def get_redmine_api(): redmine.engine.session.mount('http://', HTTPAdapter(max_retries=3)) redmine.engine.session.mount('https://', HTTPAdapter(max_retries=3)) redmine.statuses = {status.name: status for status in redmine.issue_status.all()} - redmine.nouveau = redmine.statuses[u'Nouveau'] - redmine.solution = redmine.statuses[u'Solution proposée'] - redmine.resolu_a_deployer = redmine.statuses[u'Résolu (à déployer)'] - redmine.developpement = [tr for tr in redmine.tracker.all() if tr.name == u'Développement'][0] - redmine.rustine = [cf for cf in redmine.custom_field.all() if cf.name == u'Rustine proposée'][0] + redmine.nouveau = redmine.statuses['Nouveau'] + redmine.solution = redmine.statuses['Solution proposée'] + redmine.resolu_a_deployer = redmine.statuses['Résolu (à déployer)'] + redmine.developpement = [tr for tr in redmine.tracker.all() if tr.name == 'Développement'][0] + redmine.rustine = [cf for cf in redmine.custom_field.all() if cf.name == 'Rustine proposée'][0] return redmine @@ -101,8 +99,7 @@ def get_issue(issue_number=None): try: issue = api.issue.get(issue_number) except Exception: - raise click.UsageError( - 'Cannot find issue %s' % issue_number) + raise click.UsageError('Cannot find issue %s' % issue_number) return issue @@ -114,8 +111,7 @@ def get_current_issue(): try: issue_number = int(issue_number) except Exception: - raise click.UsageError( - 'Cannot find an issue number in current branch name %s' % branch_name) + raise click.UsageError('Cannot find an issue number in current branch name %s' % branch_name) return issue_number @@ -142,14 +138,15 @@ def get_patches(number_of_commits=0, ref=None): 'path': path, 'filename': os.path.basename(path), } + return list(helper()) @click.group() def redmine(): - '''Integrate git branch with redmine, you must configure your .config/git/config file - with a [redmine] section and keys: url, key or username/password. - ''' + """Integrate git branch with redmine, you must configure your .config/git/config file + with a [redmine] section and keys: url, key or username/password. + """ pass @@ -161,6 +158,7 @@ def main_branch(): @redmine.command() def shell(): import IPython + api = get_redmine_api() repo = get_repo() IPython.embed() @@ -187,7 +185,9 @@ def apply_attachments(repo, issue): for i, attachment in enumerate(attachments): print(i, attachment.created_on, '%6d bytes' % attachment.filesize, attachment.filename) while True: - indexes = click.prompt('Which patch would you like to apply (id separated by spaces) ?', type=str, default='') + indexes = click.prompt( + 'Which patch would you like to apply (id separated by spaces) ?', type=str, default='' + ) try: indexes = indexes.strip() if not indexes: @@ -230,8 +230,12 @@ def take(issue_number, reference): else: new = True default_branch_name = 'wip/%s-%s' % (issue_number, slugify(issue.subject)[:32]) - click.confirm('Do you want to create a branch tracking %s ?' % (reference or 'origin/%s' % get_main_branch_name()), - default=True, abort=True) + click.confirm( + 'Do you want to create a branch tracking %s ?' + % (reference or 'origin/%s' % get_main_branch_name()), + default=True, + abort=True, + ) branch_name = click.prompt('Branch name', default=default_branch_name) branch = repo.create_head(branch_name, commit=reference or 'origin/%s' % get_main_branch_name()) set_branch_option(repo, branch, 'merge', 'refs/heads/%s' % get_main_branch_name()) @@ -242,9 +246,10 @@ def take(issue_number, reference): branch.checkout() click.echo('Moved to branch %s' % branch_name) current_user = api.user.get('current') - if ((not hasattr(issue, 'assigned_to') or issue.assigned_to.id != current_user.id) - and click.confirm('Do you want to assign the issue to yourself ?', - default=not hasattr(issue, 'assigned_to') and issue.status == api.nouveau)): + if (not hasattr(issue, 'assigned_to') or issue.assigned_to.id != current_user.id) and click.confirm( + 'Do you want to assign the issue to yourself ?', + default=not hasattr(issue, 'assigned_to') and issue.status == api.nouveau, + ): issue.assigned_to_id = current_user.id issue.save() if new: @@ -303,8 +308,13 @@ def submit(ctx, issue, no_rebase, number_of_commits): origin = repo.remote() for pi in origin.push(f=True): if pi.flags & pi.ERROR: - click.echo(click.style(u'Push from « %s » to « %s » failed.' % ( - pi.local_ref.name, pi.remote_ref.name, pi.summary), fg='red')) + click.echo( + click.style( + 'Push from « %s » to « %s » failed.' + % (pi.local_ref.name, pi.remote_ref.name, pi.summary), + fg='red', + ) + ) if click.confirm('Propose this patch as a solution ?', default=True): current_user = api.user.get('current') @@ -312,15 +322,21 @@ def submit(ctx, issue, no_rebase, number_of_commits): issue.assigned_to_id = current_user.id issue.save() elif issue.assigned_to.id != current_user.id: - if click.confirm('Issue is currently assigned to %s, do you want ' - 'to assign the issue to yourself ?' % issue.assigned_to.name): + if click.confirm( + 'Issue is currently assigned to %s, do you want ' + 'to assign the issue to yourself ?' % issue.assigned_to.name + ): issue.assigned_to_id = current_user.id issue.save() kwargs['tracker_id'] = api.developpement.id kwargs['status_id'] = api.solution.id - api.issue.update(issue.id, notes=message, uploads=patches, - custom_fields=[{'id': api.rustine.id, 'value': u'1'}], - **kwargs) + api.issue.update( + issue.id, + notes=message, + uploads=patches, + custom_fields=[{'id': api.rustine.id, 'value': '1'}], + **kwargs, + ) @issue.command() @@ -360,7 +376,8 @@ def new(ctx, reference): subject=subject, tracker_id=api.developpement.id, description=description, - assigned_to_id=current_user.id) + assigned_to_id=current_user.id, + ) click.echo('Created issue %s' % issue.url) ctx.invoke(take, issue_number=issue.id, reference=reference) @@ -374,16 +391,18 @@ def link(issue): try: target_issue = api.issue.get(issue) except Exception: - raise click.UsageError( - 'Cannot find issue %s' % issue) + raise click.UsageError('Cannot find issue %s' % issue) if len(api.issue_relation.filter(issue_id=current_issue.id, issue_to_id=target_issue.id)): click.echo(click.style('Already linked.', fg='green')) return - if click.confirm(f'Link issue {current_issue.id } - « {current_issue.subject} » to {target_issue.id} - « {target_issue.subject} »', default=False, abort=True): + if click.confirm( + f'Link issue {current_issue.id } - « {current_issue.subject} » to {target_issue.id} - « {target_issue.subject} »', + default=False, + abort=True, + ): api.issue_relation.create( - issue_id=current_issue.id, - issue_to_id=target_issue.id, - relation_type='relates') + issue_id=current_issue.id, issue_to_id=target_issue.id, relation_type='relates' + ) click.echo(click.style('Linked.', fg='green')) @@ -393,8 +412,7 @@ class MyProgressPrinter(git.RemoteProgress): def get_commits(repo, ref): - for commit in git.Commit.iter_items(repo, '%s..' % ref): - yield commit + yield from git.Commit.iter_items(repo, '%s..' % ref) @redmine.command(name='merge-and-push') @@ -426,7 +444,7 @@ def merge_and_push(issue, validate_msg, target_branch): if validate_msg: for commit in get_commits(repo, target_branch): - if (u'#%s' % get_current_issue()) not in commit.message: + if ('#%s' % get_current_issue()) not in commit.message: click.echo(click.style('Missing commit number in commit message', fg='red')) click.echo() click.echo(commit.message) @@ -440,26 +458,30 @@ def merge_and_push(issue, validate_msg, target_branch): click.confirm('Continue ?', abort=True) try: - click.echo(u'Checking-out branch « %s » ... ' % target_branch, nl=False) + click.echo('Checking-out branch « %s » ... ' % target_branch, nl=False) repo.branches[target_branch].checkout() click.echo(click.style('Done.', fg='green')) - click.echo(u'Pull-rebasing from remote « %s » onto branch « %s » ... ' % (origin.name, target_branch), nl=False) + click.echo( + 'Pull-rebasing from remote « %s » onto branch « %s » ... ' % (origin.name, target_branch), + nl=False, + ) failure = False for pi in origin.pull(rebase=True): if pi.flags & pi.ERROR: failure = True - click.echo(click.style(u'Pull-rebase from « %s » failed: %s.' % ( - pi.ref.name, pi.note), fg='red')) + click.echo( + click.style('Pull-rebase from « %s » failed: %s.' % (pi.ref.name, pi.note), fg='red') + ) click.echo(click.style('Done.', fg='green')) if failure: raise click.ClickException('Pull rebase failed.') finally: - click.echo(u'Checking-out branch « %s »... ' % current_head, nl=False) + click.echo('Checking-out branch « %s »... ' % current_head, nl=False) repo.branches[current_head].checkout() click.echo(click.style('Done.', fg='green')) try: - click.echo(u'Rebasing branch « %s » onto branch « %s » ... ' % (current_head, target_branch), nl=False) + click.echo('Rebasing branch « %s » onto branch « %s » ... ' % (current_head, target_branch), nl=False) repo.git.rebase(target_branch) except git.GitCommandError as e: click.echo(click.style('command %r failed, aborting.' % e.command, fg='red')) @@ -473,7 +495,7 @@ def merge_and_push(issue, validate_msg, target_branch): click.echo('Checking-out to %s... ' % target_branch, nl=False) repo.branches[target_branch].checkout() click.echo(click.style('Done.', fg='green')) - click.echo(u'Merging branch « %s » into « %s » ... ' % (current_head, target_branch), nl=False) + click.echo('Merging branch « %s » into « %s » ... ' % (current_head, target_branch), nl=False) try: repo.git.merge(current_head, ff=True) except git.GitCommandError as e: @@ -490,24 +512,39 @@ def merge_and_push(issue, validate_msg, target_branch): except IndexError: pass else: - if click.confirm(u'Do you want to delete feature branch « %s » on remote « %s » ?' % ( - current_head, origin.name), default=True): + if click.confirm( + 'Do you want to delete feature branch « %s » on remote « %s » ?' + % (current_head, origin.name), + default=True, + ): for pi in origin.push(refspec=':%s' % current_head): if pi.flags & pi.ERROR: - click.echo(click.style(u'Push from « %s » to « %s » failed.' % ( - pi.local_ref.name, pi.remote_ref.name, pi.summary), fg='red')) - if click.confirm(u'Do you want to push « %s » on remote « %s » ?' % (target_branch, repo.remote().name), - default=True): + click.echo( + click.style( + 'Push from « %s » to « %s » failed.' + % (pi.local_ref.name, pi.remote_ref.name, pi.summary), + fg='red', + ) + ) + if click.confirm( + 'Do you want to push « %s » on remote « %s » ?' % (target_branch, repo.remote().name), + default=True, + ): for pi in origin.push(): if pi.flags & pi.ERROR: - click.echo(click.style(u'Push from « %s » to « %s » failed: %s.' % ( - pi.local_ref.name, pi.remote_ref.name, pi.summary), fg='red')) - if click.confirm(u'Do you want to delete feature branch « %s » ?' % current_head, default=True): + click.echo( + click.style( + 'Push from « %s » to « %s » failed: %s.' + % (pi.local_ref.name, pi.remote_ref.name, pi.summary), + fg='red', + ) + ) + if click.confirm('Do you want to delete feature branch « %s » ?' % current_head, default=True): repo.delete_head(repo.branches[current_head]) else: repo.branches[current_head].checkout() except Exception: - click.echo(click.style(u'\nFailure going back to branch « %s ».' % current_head, fg='red')) + click.echo(click.style('\nFailure going back to branch « %s ».' % current_head, fg='red')) repo.branches[current_head].checkout() raise if click.confirm('Set issue status to solved ?', default=True): @@ -518,8 +555,10 @@ def merge_and_push(issue, validate_msg, target_branch): issue.assigned_to_id = current_user.id issue.save() elif issue.assigned_to.id != current_user.id: - if click.confirm('Issue is currently assigned to %s, do you want ' - 'to assign the issue to yourself ?' % issue.assigned_to.name): + if click.confirm( + 'Issue is currently assigned to %s, do you want ' + 'to assign the issue to yourself ?' % issue.assigned_to.name + ): issue.assigned_to_id = current_user.id issue.save() kwargs['status_id'] = api.resolu_a_deployer.id @@ -541,7 +580,7 @@ def rebase(target_branch): current_head = repo.head.ref.name if current_head == target_branch: - raise click.UsageError(u'Your cannot rebase on « %s » as your are already on it.' % target_branch) + raise click.UsageError('Your cannot rebase on « %s » as your are already on it.' % target_branch) try: repo.branches[target_branch] @@ -549,26 +588,30 @@ def rebase(target_branch): raise click.UsageError('%r is not a local branch.' % target_branch) try: - click.echo(u'Checking-out branch « %s » ... ' % target_branch, nl=False) + click.echo('Checking-out branch « %s » ... ' % target_branch, nl=False) repo.branches[target_branch].checkout() click.echo(click.style('Done.', fg='green')) - click.echo(u'Pull-rebasing from remote « %s » onto branch « %s » ... ' % (origin.name, target_branch), nl=False) + click.echo( + 'Pull-rebasing from remote « %s » onto branch « %s » ... ' % (origin.name, target_branch), + nl=False, + ) failure = False for pi in origin.pull(rebase=True): if pi.flags & pi.ERROR: failure = True - click.echo(click.style(u'Pull-rebase from « %s » failed: %s.' % ( - pi.ref.name, pi.note), fg='red')) + click.echo( + click.style('Pull-rebase from « %s » failed: %s.' % (pi.ref.name, pi.note), fg='red') + ) click.echo(click.style('Done.', fg='green')) if failure: raise click.ClickException('Pull rebase failed.') finally: - click.echo(u'Checking-out branch « %s »... ' % current_head, nl=False) + click.echo('Checking-out branch « %s »... ' % current_head, nl=False) repo.branches[current_head].checkout() click.echo(click.style('Done.', fg='green')) try: - click.echo(u'Rebasing branch « %s » onto branch « %s » ... ' % (current_head, target_branch), nl=False) + click.echo('Rebasing branch « %s » onto branch « %s » ... ' % (current_head, target_branch), nl=False) repo.git.rebase(target_branch) except git.GitCommandError as e: click.echo(click.style('command %r failed, aborting.' % e.command, fg='red')) @@ -626,9 +669,7 @@ def clean(): temp_issues_to_branch = list(issues_to_branch) while temp_issues_to_branch: issue_ids = ','.join(temp_issues_to_branch[:30]) - issues = api.issue.filter(issue_id=issue_ids, - status_id='closed', - include=['journals']) + issues = api.issue.filter(issue_id=issue_ids, status_id='closed', include=['journals']) for issue in issues: journals = list(issue.journals) last_journal = len(journals) and journals[len(journals) - 1] diff --git a/setup.py b/setup.py index 424d313..bf8ed2a 100755 --- a/setup.py +++ b/setup.py @@ -23,5 +23,5 @@ setup( ], entry_points={ 'console_scripts': ['git-redmine=git_redmine:redmine'], - } + }, )