Commit 2e9bddbf authored by Filippos Giannakos's avatar Filippos Giannakos
Browse files

Heavily modify flow code.

Add argument parser for input.
Add interactive functions for cleanup and changelog editing
Add automatic version bumping.
parent 8049444d
import os
import re
import logging
logging.basicConfig()
from optparse import OptionParser
#from optparse import OptionParser
from argparse import ArgumentParser
os.environ["GIT_PYTHON_TRACE"] = "full"
from devflow import utils, versioning
from devflow.version import __version__
from devflow.autopkg import call
from devflow.ui import query_action
from devflow.ui import query_action, query_user, query_yes_no
from functools import wraps, partial
from contextlib import contextmanager
from git.exc import GitCommandError
from sh import mktemp
def create_temp_file(suffix):
create_dir_cmd = mktemp("/tmp/" + suffix + "-XXXXX")
return create_dir_cmd.stdout.strip()
def cleanup(func):
......@@ -38,11 +46,43 @@ def conflicts():
yield
except GitCommandError as e:
if e.status != 128:
print "An error occured. Resolve it and type 'exit'"
call("bash")
print "An error occured. Resolve it and type 'exit 0'"
tmpbashrc=create_temp_file("bashrc")
f = open(tmpbashrc, 'w')
f.write("source $HOME/.bashrc ; export PS1=(Conflict)\"$PS1\"")
f.close()
call('bash --rcfile %s' % tmpbashrc)
os.unlink(tmpbashrc)
else:
raise
def get_release_version(develop_version):
version = develop_version.rstrip('next')
parts = version.split('.')
major_version = int(parts[0])
minor_version = int(parts[1])
#return str(major_version) + '.' + str(minor_version+1) + 'rc1'
return str(major_version) + '.' + str(minor_version+1)
def get_develop_version_from_release(release_version):
#version = re.sub('rc[0-9]+$', '', release_version)
version = release_version
parts = version.split('.')
major_version = int(parts[0])
minor_version = int(parts[1])
return str(major_version) + '.' + str(minor_version+1) + 'next'
def get_hotfix_version(version):
parts = version.split('.')
major_version = int(parts[0])
minor_version = int(parts[1])
if (len(parts) > 2):
hotfix_version = int(parts[2])
else:
hotfix_version = 0
return str(major_version) + '.' + str(minor_version) + '.'\
+ str(hotfix_version+1)
class GitManager(object):
def __init__(self):
......@@ -54,7 +94,7 @@ class GitManager(object):
self.log.info("Repository: %s. HEAD: %s", self.repo, self.start_hex)
self.new_branches = []
self.new_tags = []
self.repo.git.pull("origin")
#self.repo.git.pull("origin")
def get_branch(self, mode, version):
if mode not in ["release", "hotfix"]:
......@@ -66,84 +106,249 @@ class GitManager(object):
raise ValueError("Unknown mode: %s" % mode)
return "debian-%s-%s" % (mode, version)
@cleanup
def start_release(self, version):
self.start_common("release", version)
def doit(self, action_yes=None, action_no=None, question="Do it", args=None,
default=False):
if not args.defaults:
ret = query_yes_no(question, default = "yes" if default else "no")
else:
ret = default
@cleanup
def start_hotfix(self, version):
self.start_common("hotfix", version)
if ret and action_yes:
action_yes()
elif not ret and action_no:
action_no()
def __print_cleanup(self, branches):
print "To remove obsolete branches run:"
for b in branches:
print "git branch -D %s" % b
def __cleanup_branches(self, branches):
repo = self.repo
for b in branches:
repo.git.branch("-D", b)
def cleanup_branches(self, branches, args, default=False):
if args.cleanup is not None:
if args.cleanup:
self.__cleanup_branches(branches)
else:
self.__print_cleanup(branches)
return
question="Remove branches %s" % branches
action_yes = partial(self.__cleanup_branches, branches)
action_no = partial(self.__print_cleanup, branches)
self.doit(action_yes=action_yes, action_no=action_no,
question=question, args=args, default=default)
def check_edit_changelog(self, edit_action, args, default=True):
if args.edit_changelog is not None:
if args.edit_changelog:
edit_action()
return
question = "Edit changelog ?"
self.doit(action_yes=edit_action, question=question, args=args,
default=default)
def _merge_branches(self, branch_to, branch_from):
repo = self.repo
cur_branch = repo.active_branch.name
repo.git.checkout(branch_to)
with conflicts():
repo.git.merge("--no-ff", branch_from)
repo.git.checkout(cur_branch)
def merge_branches(self, branch_to, branch_from, args, default=True):
action = partial(self._merge_branches, branch_to, branch_from)
question = "Merge branch %s to %s ?" % (branch_from, branch_to)
self.doit(action_yes=action, question=question, args=args,
default=default)
def edit_changelog(self, branch, base_branch=None):
repo = self.repo
if not branch in repo.branches:
raise ValueError("Branch %s does not exist." % branch)
if base_branch and not base_branch in repo.branches:
raise ValueError("Branch %s does not exist." % base_branch)
repo.git.checkout(branch)
topdir = repo.working_dir
changelog = os.path.join(topdir, "Changelog")
lines = []
lines.append("#Changelog for %s\n" % branch)
if base_branch:
commits = repo.git.rev_list("%s..%s" % (base_branch, branch)).split("\n")
for c in commits:
commit = repo.commit(c)
lines.append(commit.message)
lines.append("\n")
f = open(changelog, 'rw+')
lines.extend(f.readlines())
f.seek(0)
f.truncate(0)
f.writelines(lines)
f.close()
editor = os.getenv('EDITOR')
if not editor:
editor = 'vim'
call("%s %s" % (editor, changelog))
repo.git.add(changelog)
repo.git.commit(m="Update changelog")
print "Updated changelog on branch %s" % branch
@cleanup
def end_release(self, version):
def start_release(self, args):
repo = self.repo
master = "master"
debian_master = "debian"
upstream = "develop"
debian = "debian-develop"
repo.git.checkout(upstream)
vcs = utils.get_vcs_info()
develop_version = versioning.get_base_version(vcs)
if not args.version:
version = get_release_version(develop_version)
if not args.defaults:
version = query_user("Release version", default=version)
else:
#validate version?
pass
rc_version = "%src1" % version
new_develop_version = "%snext" % version
upstream_branch = self.get_branch("release", version)
debian_branch = self.get_debian_branch("release", version)
repo.git.checkout(upstream)
with conflicts():
repo.git.merge("--no-ff", upstream_branch)
#create release branch
repo.git.branch(upstream_branch, upstream)
self.new_branches.append(upstream_branch)
repo.git.checkout(upstream_branch)
versioning.bump_version(rc_version)
#create debian release branch
repo.git.checkout(debian)
with conflicts():
repo.git.merge("--no-ff", debian_branch)
repo.git.branch(debian_branch, debian)
self.new_branches.append(debian_branch)
repo.git.checkout(master)
with conflicts():
repo.git.merge("--no-ff", upstream_branch)
repo.git.checkout(debian_master)
with conflicts():
repo.git.merge("--no-ff", debian_branch)
repo.git.checkout(upstream_branch)
repo.git.checkout(debian)
#bump develop version
repo.git.checkout(upstream)
print "To remove obsolete branches run:"
print "git branch -d %s" % upstream_branch
print "git branch -d %s" % debian_branch
versioning.bump_version(new_develop_version)
repo.git.checkout(upstream_branch)
@cleanup
def end_hotfix(self, version):
def start_hotfix(self, args):
repo = self.repo
upstream = "master"
debian = "debian"
upstream_branch = self.get_branch("hotfix", version)
debian_branch = self.get_debian_branch("hotfix", version)
repo.git.checkout(upstream)
with conflicts():
repo.git.merge("--no-ff", upstream_branch)
repo.git.checkout(debian)
with conflicts():
repo.git.merge("--no-ff", debian_branch)
#maybe provide major.minor version, find the latest release/hotfix and
#branch from there ?
vcs = utils.get_vcs_info()
version = versioning.get_base_version(vcs)
if not args.version:
version = get_hotfix_version(version)
if not args.defaults:
version = query_user("Hotfix version", default=version)
else:
#validate version?
pass
repo.git.checkout(upstream)
print "To remove obsolete branches run:"
print "git branch -d %s" % upstream_branch
print "git branch -d %s" % debian_branch
rc_version = "%src1" % version
new_develop_version = "%snext" % version
def start_common(self, mode, version):
if mode not in ["release", "hotfix"]:
raise ValueError("Unknown mode: %s" % mode)
repo = self.repo
upstream = "develop" if mode == "release" else "master"
debian = "debian-develop" if mode == "release" else "debian"
upstream_branch = "%s-%s" % (mode, version)
debian_branch = "debian-%s-%s" % (mode, version)
repo.git.checkout(upstream)
upstream_branch = self.get_branch("hotfix", version)
debian_branch = self.get_debian_branch("hotfix", version)
#create hotfix branch
repo.git.branch(upstream_branch, upstream)
self.new_branches.append(upstream_branch)
versioning.bump_version("%snext" % version)
repo.git.checkout(upstream_branch)
versioning.bump_version("%src1" % version)
versioning.bump_version(rc_version)
#create debian hotfix branch
repo.git.checkout(debian)
repo.git.branch(debian_branch, debian)
self.new_branches.append(debian_branch)
repo.git.checkout(upstream_branch)
repo.git.checkout(debian)
#bump develop version. Ask first or verify we have the same
#major.minornext?
#repo.git.checkout(upstream)
#versioning.bump_version(new_develop_version)
repo.git.checkout(upstream_branch)
@cleanup
def end_release(self, args):
version = args.version
repo = self.repo
master = "master"
debian_master = "debian"
upstream = "develop"
debian = "debian-develop"
upstream_branch = self.get_branch("release", version)
debian_branch = self.get_debian_branch("release", version)
tag = upstream_branch
debial_tag = "debian/" + tag
edit_action = partial(self.edit_changelog, upstream_branch, "develop")
self.check_edit_changelog(edit_action, args, default=True)
#merge to master
self._merge_branches(master, upstream_branch)
self._merge_branches(debian_master, debian_branch)
#create tags
repo.git.checkout(master)
repo.git.tag("%s" % tag)
repo.git.checkout(debian)
repo.git.tag("%s" % debian)
#merge release changes to upstream
self.merge_branches(upstream, upstream_branch, args, default=True)
self.merge_branches(debian, debian_branch, args, default=True)
repo.git.checkout(upstream)
branches = [upstream_branch, debian_branch]
self.cleanup_branches(branches, args, default=True)
@cleanup
def end_hotfix(self, args):
version = args.version
repo = self.repo
upstream = "master"
debian = "debian"
upstream_branch = self.get_branch("hotfix", version)
debian_branch = self.get_debian_branch("hotfix", version)
#create tags?
self._merge_branches(upstream, upstream_branch)
self._merge_branches(debian, debian_branch)
repo.git.checkout(upstream)
branches = [upstream_branch, debian_branch]
self.cleanup_branches(branches, args, default=True)
@cleanup
def start_feature(self, feature_name):
def start_feature(self, args):
feature_name = args.feature_name
repo = self.repo
feature_upstream = "feature-%s" % feature_name
feature_debian = "debian-%s" % feature_upstream
......@@ -153,88 +358,128 @@ class GitManager(object):
self.new_branches.append(feature_debian)
@cleanup
def end_feature(self, feature_name):
def end_feature(self, args):
feature_name = args.feature_name
repo = self.repo
feature_upstream = "feature-%s" % feature_name
if not feature_upstream in repo.branches:
raise ValueError("Branch %s does not exist." % feature_upstream)
feature_debian = "debian-%s" % feature_upstream
action = partial(self.edit_changelog, feature_upstream, "develop")
query_action("Edit changelog", action = action)
# self.edit_changelog(feature_upstream)
repo.git.checkout("develop")
with conflicts():
repo.git.merge(feature_upstream)
repo.git.checkout("debian-develop")
edit_action = partial(self.edit_changelog, feature_upstream, "develop")
self.check_edit_changelog(edit_action, args, default=True)
#merge to develop
self._merge_branches("develop", feature_upstream)
if feature_debian in repo.branches:
with conflicts():
repo.git.merge(feature_debian)
self._merge_branches("debian-develop", feature_debian)
repo.git.checkout("develop")
print "To remove obsolete branches run:"
print "git branch -D %s" % feature_upstream
branches = [feature_upstream]
if feature_debian in repo.branches:
print "git branch -D %s" % feature_debian
branches.append(feature_debian)
self.cleanup_branches(branches, args, default=True)
def edit_changelog(self, branch, base_branch=None):
repo = self.repo
if not branch in repo.branches:
raise ValueError("Branch %s does not exist." % branch)
if base_branch and not base_branch in repo.branches:
raise ValueError("Branch %s does not exist." % base_branch)
repo.git.checkout(branch)
topdir = repo.working_dir
changelog = os.path.join(topdir, "Changelog")
def refhead(repo):
return repo.head.log[-1].newhexsha
lines = []
lines.append("#Changelog for %s\n" % branch)
if base_branch:
commits = repo.git.rev_list("%s..%s" % (base_branch, branch)).split("\n")
for c in commits:
commit = repo.commit(c)
lines.append(commit.message)
lines.append("\n")
f = open(changelog, 'rw+')
lines.extend(f.readlines())
f.seek(0)
f.truncate(0)
f.writelines(lines)
f.close()
def main():
parser = ArgumentParser(description="Devflow tool")
parser.add_argument('-V', '--version', action='version',
version='devflow-flow %s' % __version__)
parser.add_argument('-d', '--defaults', action='store_true', default=False,
help="Assume default on every choice, unless a value is provided")
editor = os.getenv('EDITOR')
if not editor:
editor = 'vim'
call("%s %s" % (editor, changelog))
repo.git.add(changelog)
repo.git.commit(m="Update changelog")
print "Updated changelog on branch %s" % branch
subparsers = parser.add_subparsers()
def end_common(self, mode, version):
pass
init_parser = subparsers.add_parser('init',
help="Initialize a new devflow repo")
init_parser.add_argument('-m', '--master', type=str, nargs='?',
help="Master branch")
init_parser.add_argument('-d', '--develop', type=str, nargs='?',
help="Develop branch")
init_parser.set_defaults(func='init_repo')
def refhead(repo):
return repo.head.log[-1].newhexsha
feature_parser = subparsers.add_parser('feature', help="Feature options")
feature_subparsers = feature_parser.add_subparsers()
feature_start_parser = feature_subparsers.add_parser('start',
help="Start a new feature")
feature_start_parser.set_defaults(func='start_feature')
feature_start_parser.add_argument('feature_name', type=str,
help="Name of the feature")
feature_finish_parser = feature_subparsers.add_parser('finish',
help="Finish a feature")
feature_finish_parser.set_defaults(func='end_feature')
feature_finish_parser.add_argument('feature_name', type=str,
help="Name of the feature")
feature_finish_parser.add_argument('--no-edit-changelog',
action='store_const', const=False, dest='edit_changelog',
help="Do not edit the changelog")
feature_finish_parser.add_argument('--no-cleanup', action='store_const',
const=True, dest='cleanup', help="Do not cleanup branches")
release_parser = subparsers.add_parser('release', help="release options")
release_subparsers = release_parser.add_subparsers()
release_start_parser = release_subparsers.add_parser('start',
help="Start a new release")
release_start_parser.add_argument('--version', type=str,
help="Version of the release")
release_start_parser.add_argument('--develop-version', type=str,
help="New develop version")
release_start_parser.set_defaults(func='start_release')
release_finish_parser = release_subparsers.add_parser('finish',
help="Finish a release")
release_finish_parser.add_argument('version', type=str,
help="Version of the release")
release_finish_parser.add_argument('--no-edit-changelog',
action='store_const', const=False, dest='edit_changelog',
help="Do not edit the changelog")
release_finish_parser.add_argument('--no-cleanup', action='store_const',
const=True, dest='cleanup', help="Do not cleanup branches")
release_finish_parser.set_defaults(func='end_release')
hotfix_parser = subparsers.add_parser('hotfix', help="hotfix options")
hotfix_subparsers = hotfix_parser.add_subparsers()
hotfix_start_parser = hotfix_subparsers.add_parser('start',
help="Start a new hotfix")
hotfix_start_parser.add_argument('--version', type=str,
help="Version of the hotfix")
hotfix_start_parser.add_argument('--develop-version', type=str,
help="New develop version")
hotfix_start_parser.set_defaults(func='start_hotfix')
hotfix_finish_parser = hotfix_subparsers.add_parser('finish',
help="Finish a hotfix")
hotfix_finish_parser.add_argument('version', type=str,
help="Version of the hotfix")
hotfix_finish_parser.add_argument('--no-edit-changelog',
action='store_const', const=False, dest='edit_changelog',
help="Do not edit the changelog")
hotfix_finish_parser.add_argument('--no-cleanup', action='store_const',
const=True, dest='cleanup', help="Do not cleanup branches")
hotfix_finish_parser.set_defaults(func='end_hotfix')
args = parser.parse_args()
def main():
HELP_MSG = "usage: %prog mode=[release|hotfix|feature]"\
" [version|name]"
parser = OptionParser(usage=HELP_MSG,
version="devflow-flow %s" % __version__,
add_help_option=True)
options, args = parser.parse_args()
if len(args) != 3:
parser.error("Invalid number of arguments.")
mode, action, version = args
gm = GitManager()
func = "%s_%s" % (action, mode)
try:
getattr(gm, func)(version)
except AttributeError:
parser.error("Invalid arguments.")
getattr(gm, args.func)(args)
if __name__ == "__main__":
main()
......@@ -39,3 +39,11 @@ def query_action(question, default="yes", action=None):
answer = query_yes_no(question, default)
if answer and action is not None:
action()
def query_user(question, default=""):
prompt = "[" + default + "]"
sys.stdout.write(question + prompt)
answer = raw_input()
if answer == "":
return default
return answer
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment