add pre-commit hooks
This commit is contained in:
parent
e32cda0fba
commit
8e2950e101
189
git_redmine.py
189
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]
|
||||
|
|
Loading…
Reference in New Issue