diff --git a/bench/commands/__init__.py b/bench/commands/__init__.py index 069ab644..da0bb257 100755 --- a/bench/commands/__init__.py +++ b/bench/commands/__init__.py @@ -47,7 +47,7 @@ bench_command.add_command(switch_to_develop) from bench.commands.utils import (start, restart, set_nginx_port, set_ssl_certificate, set_ssl_certificate_key, set_url_root, set_mariadb_host, set_default_site, download_translations, shell, backup_site, backup_all_sites, release, renew_lets_encrypt, - disable_production, bench_src) + disable_production, bench_src, prepare_beta_release) bench_command.add_command(start) bench_command.add_command(restart) bench_command.add_command(set_nginx_port) @@ -64,6 +64,7 @@ bench_command.add_command(release) bench_command.add_command(renew_lets_encrypt) bench_command.add_command(disable_production) bench_command.add_command(bench_src) +bench_command.add_command(prepare_beta_release) from bench.commands.setup import setup bench_command.add_command(setup) diff --git a/bench/commands/utils.py b/bench/commands/utils.py index 7c592458..50266824 100644 --- a/bench/commands/utils.py +++ b/bench/commands/utils.py @@ -135,6 +135,16 @@ def release(app, bump_type, from_branch, to_branch, owner, repo_name, remote): release(bench_path='.', app=app, bump_type=bump_type, from_branch=from_branch, to_branch=to_branch, remote=remote, owner=owner, repo_name=repo_name) + +@click.command('prepare-beta-release') +@click.argument('app') +@click.option('--owner', default='frappe') +def prepare_beta_release(app, owner): + """Prepare major beta release from develop branch""" + from bench.prepare_beta_release import prepare_beta_release + prepare_beta_release(bench_path='.', app=app, owner=owner) + + @click.command('disable-production') def disable_production(): """Disables production environment for the bench.""" diff --git a/bench/prepare_beta_release.py b/bench/prepare_beta_release.py new file mode 100644 index 00000000..09809c78 --- /dev/null +++ b/bench/prepare_beta_release.py @@ -0,0 +1,124 @@ +#! env python +import os +import git +import click +from .config.common_site_config import get_config +import semantic_version + +github_username = None +github_password = None + +def prepare_beta_release(bench_path, app, owner='frappe', remote='upstream'): + from .release import get_release_message + + beta_hotfix = '' + beta_master = click.prompt('Branch name for beta release', type=str) + + if click.confirm("Do you want to setup hotfix for beta ?"): + beta_hotfix = click.prompt('Branch name for beta hotfix ({}_hotifx)'.format(beta_master), type=str) + + + print(beta_master, beta_hotfix) + + validate(bench_path) + repo_path = os.path.join(bench_path, 'apps', app) + version = get_bummped_version(repo_path) + + update_branch(repo_path, remote) + prepare_beta_master(repo_path, beta_master, version, remote) + + if beta_hotfix: + prepare_beta_hotfix(repo_path, beta_hotfix, remote) + + tag_name = merge_beta_release_to_develop(repo_path, beta_master, remote, version) + push_branches(repo_path, beta_master, beta_hotfix, remote) + create_github_release(repo_path, tag_name, '', owner, remote) + +def validate(bench_path): + from .release import validate + + config = get_config(bench_path) + validate(bench_path, config) + +def get_bummped_version(repo_path): + from .release import get_current_version + current_version = get_current_version(repo_path, 'master') + + v = semantic_version.Version(current_version) + + if v.major: + v.major += 1 + v.minor = 0 + v.patch = 0 + v.prerelease = None + + return str(v) + + +def update_branch(repo_path, remote): + from .release import update_branch + update_branch(repo_path, 'develop', remote) + +def prepare_beta_master(repo_path, beta_master, version, remote): + g = git.Repo(repo_path).git + g.checkout(b=beta_master) + + set_beta_version(repo_path, version) + +def set_beta_version(repo_path, version): + from .release import set_filename_version + set_filename_version(os.path.join(repo_path, os.path.basename(repo_path),'hooks.py'), version, 'beta_version') + + repo = git.Repo(repo_path) + app_name = os.path.basename(repo_path) + repo.index.add([os.path.join(app_name, 'hooks.py')]) + repo.index.commit('bumped to version {}'.format(version)) + + +def prepare_beta_hotfix(repo_path, beta_hotfix, remote): + g = git.Repo(repo_path).git + g.checkout(b=beta_hotfix) + + +def merge_beta_release_to_develop(repo_path, beta_master, remote, version): + from .release import handle_merge_error + + repo = git.Repo(repo_path) + g = repo.git + + tag_name = 'v' + version + repo.create_tag(tag_name, message='Release {}'.format(version)) + + g.checkout('develop') + + try: + print(beta_master) + g.merge(beta_master) + except git.exc.GitCommandError as e: + print("here") + handle_merge_error(e, source=beta_master, target='develop') + + return tag_name + +def push_branches(repo_path, beta_master, beta_hotfix, remote): + repo = git.Repo(repo_path) + g = repo.git + + args = [ + 'develop:develop', + '{beta_master}:{beta_master}'.format(beta_master=beta_master), + ] + + if beta_hotfix: + args.append('{beta_hotfix}:{beta_hotfix}'.format(beta_hotfix=beta_hotfix)) + + args.append('--tags') + + print("Pushing new branches") + print(g.push(remote, *args)) + +def create_github_release(repo_path, tag_name, message, owner, remote): + from .release import create_github_release + + create_github_release(repo_path, tag_name, message, remote=remote, owner=owner, + repo_name=None, gh_username=github_username, gh_password=github_password) \ No newline at end of file diff --git a/bench/release.py b/bench/release.py index fec67a03..32e75be8 100755 --- a/bench/release.py +++ b/bench/release.py @@ -31,7 +31,9 @@ def release(bench_path, app, bump_type, from_branch='develop', to_branch='master print('bench not configured to release') sys.exit(1) - branches_to_update.update(config.get('branches_to_update', branches_to_update)) + + if config.get('branches_to_update'): + branches_to_update.update(config.get('branches_to_update')) validate(bench_path, config) @@ -82,7 +84,7 @@ def bump(bench_path, app, bump_type, from_branch, to_branch, remote, owner, repo click.confirm('Do you want to continue?', abort=True) new_version = bump_repo(repo_path, bump_type, from_branch=from_branch, to_branch=to_branch) - commit_changes(repo_path, new_version) + commit_changes(repo_path, new_version, to_branch) tag_name = create_release(repo_path, new_version, from_branch=from_branch, to_branch=to_branch) push_release(repo_path, from_branch=from_branch, to_branch=to_branch, remote=remote) create_github_release(repo_path, tag_name, message, remote=remote, owner=owner, repo_name=repo_name) @@ -125,22 +127,34 @@ def get_release_message(repo_path, from_branch='develop', to_branch='master', re return "* " + log.replace('\n', '\n* ') def bump_repo(repo_path, bump_type, from_branch='develop', to_branch='master'): - current_version = get_current_version(repo_path) + current_version = get_current_version(repo_path, to_branch) new_version = get_bumped_version(current_version, bump_type) print('bumping version from', current_version, 'to', new_version) - set_version(repo_path, new_version) + set_version(repo_path, new_version, to_branch) return new_version -def get_current_version(repo_path): +def get_current_version(repo_path, to_branch): # TODO clean this up! - filename = os.path.join(repo_path, os.path.basename(repo_path), '__init__.py') - with open(filename) as f: - contents = f.read() - match = re.search(r"^(\s*%s\s*=\s*['\\\"])(.+?)(['\"])(?sm)" % '__version__', - contents) - return match.group(2) + + if to_branch.lower() == 'master': + filename = os.path.join(repo_path, os.path.basename(repo_path), '__init__.py') + + with open(filename) as f: + contents = f.read() + match = re.search(r"^(\s*%s\s*=\s*['\\\"])(.+?)(['\"])(?sm)" % '__version__', + contents) + return match.group(2) + + else: + filename = os.path.join(repo_path, os.path.basename(repo_path), 'hooks.py') + + with open(filename) as f: + contents = f.read() + match = re.search(r"^(\s*%s\s*=\s*['\\\"])(.+?)(['\"])(?sm)" % 'beta_version', + contents) + return match.group(2) def get_bumped_version(version, bump_type): v = semantic_version.Version(version) @@ -175,8 +189,11 @@ def get_bumped_version(version, bump_type): return str(v) -def set_version(repo_path, version): - set_filename_version(os.path.join(repo_path, os.path.basename(repo_path),'__init__.py'), version, '__version__') +def set_version(repo_path, version, to_branch): + if to_branch.lower() == 'master': + set_filename_version(os.path.join(repo_path, os.path.basename(repo_path),'__init__.py'), version, '__version__') + else: + set_filename_version(os.path.join(repo_path, os.path.basename(repo_path),'hooks.py'), version, 'beta_version') # TODO fix this # set_setuppy_version(repo_path, version) @@ -210,12 +227,17 @@ def set_filename_version(filename, version_number, pattern): with open(filename, 'w') as f: f.write(contents) -def commit_changes(repo_path, new_version): +def commit_changes(repo_path, new_version, to_branch): print('committing version change to', repo_path) repo = git.Repo(repo_path) app_name = os.path.basename(repo_path) - repo.index.add([os.path.join(app_name, '__init__.py')]) + + if to_branch.lower() == 'master': + repo.index.add([os.path.join(app_name, '__init__.py')]) + else: + repo.index.add([os.path.join(app_name, 'hooks.py')]) + repo.index.commit('bumped to version {}'.format(new_version)) def create_release(repo_path, new_version, from_branch='develop', to_branch='master'): @@ -238,7 +260,7 @@ def create_release(repo_path, new_version, from_branch='develop', to_branch='mas handle_merge_error(e, source=to_branch, target=from_branch) for branch in branches_to_update[from_branch]: - print('merging master into', branch) + print('merging {0} into'.format(to_branch), branch) g.checkout(branch) try: g.merge(to_branch)