2
0
mirror of https://github.com/frappe/bench.git synced 2025-01-10 09:02:10 +00:00

Merge branch 'staging' into v5.x

This commit is contained in:
Gavin D'souza 2022-06-14 17:46:34 +05:30
commit 2a3cd9dcd2
23 changed files with 2 additions and 1125 deletions

View File

@ -13,8 +13,6 @@ from datetime import date
from urllib.parse import urlparse from urllib.parse import urlparse
import os import os
from markupsafe import soft_str
# imports - third party imports # imports - third party imports
import click import click
from git import Repo from git import Repo
@ -285,31 +283,6 @@ def make_resolution_plan(app: App, bench: "Bench"):
return resolution return resolution
def add_to_appstxt(app, bench_path="."):
from bench.bench import Bench
apps = Bench(bench_path).apps
if app not in apps:
apps.append(app)
return write_appstxt(apps, bench_path=bench_path)
def remove_from_appstxt(app, bench_path="."):
from bench.bench import Bench
apps = Bench(bench_path).apps
if app in apps:
apps.remove(app)
return write_appstxt(apps, bench_path=bench_path)
def write_appstxt(apps, bench_path="."):
with open(os.path.join(bench_path, "sites", "apps.txt"), "w") as f:
return f.write("\n".join(apps))
def get_excluded_apps(bench_path="."): def get_excluded_apps(bench_path="."):
try: try:
with open(os.path.join(bench_path, "sites", "excluded_apps.txt")) as f: with open(os.path.join(bench_path, "sites", "excluded_apps.txt")) as f:

View File

@ -74,8 +74,6 @@ from bench.commands.utils import (
find_benches, find_benches,
generate_command_cache, generate_command_cache,
migrate_env, migrate_env,
prepare_beta_release,
release,
renew_lets_encrypt, renew_lets_encrypt,
restart, restart,
set_mariadb_host, set_mariadb_host,
@ -102,11 +100,9 @@ bench_command.add_command(set_redis_socketio_host)
bench_command.add_command(download_translations) bench_command.add_command(download_translations)
bench_command.add_command(backup_site) bench_command.add_command(backup_site)
bench_command.add_command(backup_all_sites) bench_command.add_command(backup_all_sites)
bench_command.add_command(release)
bench_command.add_command(renew_lets_encrypt) bench_command.add_command(renew_lets_encrypt)
bench_command.add_command(disable_production) bench_command.add_command(disable_production)
bench_command.add_command(bench_src) bench_command.add_command(bench_src)
bench_command.add_command(prepare_beta_release)
bench_command.add_command(find_benches) bench_command.add_command(find_benches)
bench_command.add_command(migrate_env) bench_command.add_command(migrate_env)
bench_command.add_command(generate_command_cache) bench_command.add_command(generate_command_cache)

View File

@ -128,29 +128,6 @@ def backup_all_sites():
backup_all_sites(bench_path='.') backup_all_sites(bench_path='.')
@click.command('release', help="Release a Frappe app (internal to the Frappe team)")
@click.argument('app')
@click.argument('bump-type', type=click.Choice(['major', 'minor', 'patch', 'stable', 'prerelease']))
@click.option('--from-branch', default='develop')
@click.option('--to-branch', default='master')
@click.option('--remote', default='upstream')
@click.option('--owner', default='frappe')
@click.option('--repo-name')
@click.option('--dont-frontport', is_flag=True, default=False, help='Front port fixes to new branches, example merging hotfix(v10) into staging-fixes(v11)')
def release(app, bump_type, from_branch, to_branch, owner, repo_name, remote, dont_frontport):
from bench.release import release
frontport = not dont_frontport
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, frontport=frontport)
@click.command('prepare-beta-release', help="Prepare major beta release from develop branch")
@click.argument('app')
@click.option('--owner', default='frappe')
def prepare_beta_release(app, owner):
from bench.prepare_beta_release import prepare_beta_release
prepare_beta_release(bench_path='.', app=app, owner=owner)
@click.command('disable-production', help="Disables production environment for the bench.") @click.command('disable-production', help="Disables production environment for the bench.")
def disable_production(): def disable_production():
from bench.config.production_setup import disable_production from bench.config.production_setup import disable_production

View File

@ -1,3 +1,3 @@
--- ---
node_version: 12 node_version: 14
... ...

View File

@ -1,118 +0,0 @@
#! 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(f'Branch name for beta hotfix ({beta_master}_hotifx)', type=str)
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 = ['staging']
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, 'staging_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(f'bumped to version {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=f'Release {version}')
g.checkout('develop')
try:
g.merge(beta_master)
except git.exc.GitCommandError as e:
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',
f'{beta_master}:{beta_master}',
]
if beta_hotfix:
args.append(f'{beta_hotfix}:{beta_hotfix}')
args.append('--tags')
print("Pushing 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)

View File

@ -1,367 +0,0 @@
#! env python
import json
import os
import sys
import semantic_version
import git
import getpass
import re
from time import sleep
from bench.exceptions import ValidationError
from .config.common_site_config import get_config
import click
branches_to_update = {
'develop': [],
'version-11-hotfix': [],
'version-12-hotfix': [],
}
releasable_branches = ['master']
github_username = None
github_password = None
def release(bench_path, app, bump_type, from_branch, to_branch,
remote='upstream', owner='frappe', repo_name=None, frontport=True):
confirm_testing()
config = get_config(bench_path)
if not config.get('release_bench'):
print('bench not configured to release')
sys.exit(1)
if config.get('branches_to_update'):
branches_to_update.update(config.get('branches_to_update'))
if config.get('releasable_branches'):
releasable_branches.extend(config.get('releasable_branches',[]))
validate(bench_path, config)
bump(bench_path, app, bump_type, from_branch=from_branch, to_branch=to_branch, owner=owner,
repo_name=repo_name, remote=remote, frontport=frontport)
def validate(bench_path, config):
import requests
from requests.auth import HTTPBasicAuth
global github_username, github_password
github_username = config.get('github_username')
github_password = config.get('github_password')
if not github_username:
github_username = click.prompt('Username', type=str)
if not github_password:
github_password = getpass.getpass()
r = requests.get('https://api.github.com/user', auth=HTTPBasicAuth(github_username, github_password))
r.raise_for_status()
def confirm_testing():
print('')
print('================ CAUTION ==================')
print('Never miss this, even if it is a really small release!!')
print('Manual Testing Checklisk: https://github.com/frappe/bench/wiki/Testing-Checklist')
print('')
print('')
click.confirm('Is manual testing done?', abort = True)
click.confirm('Have you added the change log?', abort = True)
def bump(bench_path, app, bump_type, from_branch, to_branch, remote, owner, repo_name=None, frontport=True):
assert bump_type in ['minor', 'major', 'patch', 'stable', 'prerelease']
repo_path = os.path.join(bench_path, 'apps', app)
push_branch_for_old_major_version(bench_path, bump_type, app, repo_path, from_branch, to_branch, remote, owner)
update_branches_and_check_for_changelog(repo_path, from_branch, to_branch, remote=remote)
message = get_release_message(repo_path, from_branch=from_branch, to_branch=to_branch, remote=remote)
if not message:
print('No commits to release')
return
print(message)
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, to_branch)
tag_name = create_release(repo_path, new_version, from_branch=from_branch, to_branch=to_branch, frontport=frontport)
push_release(repo_path, from_branch=from_branch, to_branch=to_branch, remote=remote)
prerelease = True if 'beta' in new_version else False
create_github_release(repo_path, tag_name, message, remote=remote, owner=owner, repo_name=repo_name, prerelease=prerelease)
print(f'Released {tag_name} for {repo_path}')
def update_branches_and_check_for_changelog(repo_path, from_branch, to_branch, remote='upstream'):
update_branch(repo_path, to_branch, remote=remote)
update_branch(repo_path, from_branch, remote=remote)
for branch in branches_to_update[from_branch]:
update_branch(repo_path, branch, remote=remote)
git.Repo(repo_path).git.checkout(from_branch)
check_for_unmerged_changelog(repo_path)
def update_branch(repo_path, branch, remote):
print("updating local branch of", repo_path, 'using', remote + '/' + branch)
repo = git.Repo(repo_path)
g = repo.git
g.fetch(remote)
g.checkout(branch)
g.reset('--hard', remote+'/'+branch)
def check_for_unmerged_changelog(repo_path):
current = os.path.join(repo_path, os.path.basename(repo_path), 'change_log', 'current')
if os.path.exists(current) and [f for f in os.listdir(current) if f != "readme.md"]:
raise Exception("Unmerged change log! in " + repo_path)
def get_release_message(repo_path, from_branch, to_branch, remote='upstream'):
print('getting release message for', repo_path, 'comparing', to_branch, '...', from_branch)
repo = git.Repo(repo_path)
g = repo.git
log = g.log(f'{remote}/{to_branch}..{remote}/{from_branch}', '--format=format:%s', '--no-merges')
if log:
return "* " + log.replace('\n', '\n* ')
def bump_repo(repo_path, bump_type, from_branch, to_branch):
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, to_branch)
return new_version
def get_current_version(repo_path, to_branch):
# TODO clean this up!
version_key = '__version__'
if to_branch.lower() in releasable_branches:
filename = os.path.join(repo_path, os.path.basename(repo_path), '__init__.py')
else:
filename = os.path.join(repo_path, os.path.basename(repo_path), 'hooks.py')
version_key = 'staging_version'
with open(filename) as f:
contents = f.read()
match = re.search(r"^(\s*%s\s*=\s*['\\\"])(.+?)(['\"])(?sm)" % version_key,
contents)
return match.group(2)
def get_bumped_version(version, bump_type):
v = semantic_version.Version(version)
if bump_type == 'major':
v.major += 1
v.minor = 0
v.patch = 0
v.prerelease = None
elif bump_type == 'minor':
v.minor += 1
v.patch = 0
v.prerelease = None
elif bump_type == 'patch':
if v.prerelease == ():
v.patch += 1
v.prerelease = None
elif len(v.prerelease) == 2:
v.prerelease = ()
elif bump_type == 'stable':
# remove pre-release tag
v.prerelease = None
elif bump_type == 'prerelease':
if v.prerelease == ():
v.patch += 1
v.prerelease = ('beta', '1')
elif len(v.prerelease) == 2:
v.prerelease = ('beta', str(int(v.prerelease[1]) + 1))
else:
raise ValidationError("Something wen't wrong while doing a prerelease")
else:
raise ValidationError("bump_type not amongst [major, minor, patch, prerelease]")
return str(v)
def set_version(repo_path, version, to_branch):
if to_branch.lower() in releasable_branches:
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, 'staging_version')
# TODO fix this
# set_setuppy_version(repo_path, version)
# set_versionpy_version(repo_path, version)
# set_hooks_version(repo_path, version)
# def set_setuppy_version(repo_path, version):
# set_filename_version(os.path.join(repo_path, 'setup.py'), version, 'version')
#
# def set_versionpy_version(repo_path, version):
# set_filename_version(os.path.join(repo_path, os.path.basename(repo_path),'__version__.py'), version, '__version__')
#
# def set_hooks_version(repo_path, version):
# set_filename_version(os.path.join(repo_path, os.path.basename(repo_path),'hooks.py'), version, 'app_version')
def set_filename_version(filename, version_number, pattern):
changed = []
def inject_version(match):
before, old, after = match.groups()
changed.append(True)
return before + version_number + after
with open(filename) as f:
contents = re.sub(r"^(\s*%s\s*=\s*['\\\"])(.+?)(['\"])(?sm)" % pattern,
inject_version, f.read())
if not changed:
raise Exception('Could not find %s in %s', pattern, filename)
with open(filename, 'w') as f:
f.write(contents)
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)
if to_branch.lower() in releasable_branches:
repo.index.add([os.path.join(app_name, '__init__.py')])
else:
repo.index.add([os.path.join(app_name, 'hooks.py')])
repo.index.commit(f'bumped to version {new_version}')
def create_release(repo_path, new_version, from_branch, to_branch, frontport=True):
print('creating release for version', new_version)
repo = git.Repo(repo_path)
g = repo.git
g.checkout(to_branch)
try:
g.merge(from_branch, '--no-ff')
except git.exc.GitCommandError as e:
handle_merge_error(e, source=from_branch, target=to_branch)
tag_name = 'v' + new_version
repo.create_tag(tag_name, message=f'Release {new_version}')
g.checkout(from_branch)
try:
g.merge(to_branch)
except git.exc.GitCommandError as e:
handle_merge_error(e, source=to_branch, target=from_branch)
if frontport:
for branch in branches_to_update[from_branch]:
print (f"Front porting changes to {branch}")
print(f'merging {to_branch} into', branch)
g.checkout(branch)
try:
g.merge(to_branch)
except git.exc.GitCommandError as e:
handle_merge_error(e, source=to_branch, target=branch)
return tag_name
def handle_merge_error(e, source, target):
print('-'*80)
print(f'Error when merging {source} into {target}')
print(e)
print('You can open a new terminal, try to manually resolve the conflict/error and continue')
print('-'*80)
click.confirm('Have you manually resolved the error?', abort=True)
def push_release(repo_path, from_branch, to_branch, remote='upstream'):
print('pushing branches', to_branch, from_branch, 'of', repo_path)
repo = git.Repo(repo_path)
g = repo.git
args = [
f'{to_branch}:{to_branch}',
f'{from_branch}:{from_branch}'
]
for branch in branches_to_update[from_branch]:
print(f'pushing {branch} branch of', repo_path)
args.append(f'{branch}:{branch}')
args.append('--tags')
print(g.push(remote, *args))
def create_github_release(repo_path, tag_name, message, remote='upstream', owner='frappe', repo_name=None,
gh_username=None, gh_password=None, prerelease=False):
import requests
import requests.exceptions
from requests.auth import HTTPBasicAuth
print('creating release on github')
global github_username, github_password
if not (gh_username and gh_password):
if not (github_username and github_password):
raise Exception("No credentials")
gh_username = github_username
gh_password = github_password
repo_name = repo_name or os.path.basename(repo_path)
data = {
'tag_name': tag_name,
'target_commitish': 'master',
'name': 'Release ' + tag_name,
'body': message,
'draft': False,
'prerelease': prerelease
}
for i in range(3):
try:
r = requests.post(f'https://api.github.com/repos/{owner}/{repo_name}/releases',
auth=HTTPBasicAuth(gh_username, gh_password), data=json.dumps(data))
r.raise_for_status()
break
except requests.exceptions.HTTPError:
print('request failed, retrying....')
sleep(3*i + 1)
if i !=2:
continue
else:
print(r.json())
raise
return r
def push_branch_for_old_major_version(bench_path, bump_type, app, repo_path, from_branch, to_branch, remote, owner):
if bump_type != 'major':
return
current_version = get_current_version(repo_path)
old_major_version_branch = f"v{current_version.split('.')[0]}.x.x"
click.confirm(f'Do you want to push {old_major_version_branch}?', abort=True)
update_branch(repo_path, to_branch, remote=remote)
g = git.Repo(repo_path).git
g.checkout(b=old_major_version_branch)
args = [
f'{old_major_version_branch}:{old_major_version_branch}',
]
print(f"Pushing {old_major_version_branch} ")
print(g.push(remote, *args))

View File

@ -9,7 +9,6 @@ import git
# imports - module imports # imports - module imports
from bench.utils import exec_cmd from bench.utils import exec_cmd
from bench.release import get_bumped_version
from bench.app import App from bench.app import App
from bench.tests.test_base import FRAPPE_BRANCH, TestBenchBase from bench.tests.test_base import FRAPPE_BRANCH, TestBenchBase
from bench.bench import Bench from bench.bench import Bench
@ -21,18 +20,6 @@ from bench.bench import Bench
TEST_FRAPPE_APP = "frappe_docs" TEST_FRAPPE_APP = "frappe_docs"
class TestBenchInit(TestBenchBase): class TestBenchInit(TestBenchBase):
def test_semantic_version(self):
self.assertEqual( get_bumped_version('11.0.4', 'major'), '12.0.0' )
self.assertEqual( get_bumped_version('11.0.4', 'minor'), '11.1.0' )
self.assertEqual( get_bumped_version('11.0.4', 'patch'), '11.0.5' )
self.assertEqual( get_bumped_version('11.0.4', 'prerelease'), '11.0.5-beta.1' )
self.assertEqual( get_bumped_version('11.0.5-beta.22', 'major'), '12.0.0' )
self.assertEqual( get_bumped_version('11.0.5-beta.22', 'minor'), '11.1.0' )
self.assertEqual( get_bumped_version('11.0.5-beta.22', 'patch'), '11.0.5' )
self.assertEqual( get_bumped_version('11.0.5-beta.22', 'prerelease'), '11.0.5-beta.23' )
def test_utils(self): def test_utils(self):
self.assertEqual(subprocess.call("bench"), 0) self.assertEqual(subprocess.call("bench"), 0)

View File

@ -2,7 +2,6 @@ import os
import shutil import shutil
import subprocess import subprocess
import unittest import unittest
from tabnanny import check
from bench.app import App from bench.app import App
from bench.bench import Bench from bench.bench import Bench

View File

@ -1,37 +0,0 @@
## ERPNext VM Builder
### Steps to build a VM Image
* `python build.py` builds a new Production VM, a Dev VM and a Dev Vagrant Box
### Requirements
* Bench should be installed
* Ansible should be installed
### How it works
Apart from the above the rest is handled by bench:
* Install prerequisites if not already installed
- virtualbox
- packer
* Cleanup
- Clean the required directories
* Build the VM using packer
- Packer downloads the mentioned Ubuntu iso, boots a virtual machine and preceeds the preseed.cfg file into it in order to setup a clean Ubuntu OS
- Then packer uses ssh to enter the virtual machine to execute the required commands
- `scripts/debian_family/install_ansible.sh` sets up ansible on the vm.
- Depending on the VM being built, the `vm-develop.json` or the `vm-production.json` is used
- `scripts/set_message.sh` sets welcome message (with update instructions) in the vm.
- `scripts/cleanup.sh` writes zeros to all the free space in the disk, it shrinks the disk image
* Set the correct permissions for the built Vagrant and Virtual Appliance Images
* Cleanup
- Delete the generated files from the required directories
* Restart nginx
Running the `build.py` script builds the VMs and puts them in `~/Public`. It also creates the md5 hashes for the same, and puts them in the same folder.

73
vm/Vagrantfile vendored
View File

@ -1,73 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure(2) do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://atlas.hashicorp.com/search.
config.vm.box = "erpnext"
config.ssh.username = "frappe"
config.ssh.password = "frappe"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# config.vm.network "forwarded_port", guest: 80, host: 8080
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: "192.168.33.10"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network"
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
# config.vm.synced_folder "../data", "/vagrant_data"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
# vb.memory = "1024"
# end
#
# View the documentation for the provider you are using for more
# information on available options.
# Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
# such as FTP and Heroku are also available. See the documentation at
# https://docs.vagrantup.com/v2/push/atlas.html for more information.
# config.push.define "atlas" do |push|
# push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
# end
# Enable provisioning with a shell script. Additional provisioners such as
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
# documentation for more information about their specific syntax and use.
# config.vm.provision "shell", inline: <<-SHELL
# sudo apt-get update
# sudo apt-get install -y apache2
# SHELL
end

View File

@ -1,128 +0,0 @@
"""
Builds a vm and puts it in ~/public with a latest.json that has its filename and md5sum
"""
# imports - standard imports
import os
import json
import stat
import errno
from shutil import rmtree
from distutils import spawn
from subprocess import check_output
NEW_FILES = []
BUILDS = ['Production', 'Developer']
PUBLIC_DIR = os.path.join(os.path.expanduser('~'), 'Public')
SYMLINKS = ['ERPNext-Production.ova', 'ERPNext-Dev.ova', 'ERPNext-Vagrant.box',
'ERPNext-Production.ova.md5', 'ERPNext-Dev.ova.md5', 'ERPNext-Vagrant.box.md5']
def main():
install_virtualbox()
install_packer()
cleanup()
build_vm()
generate_md5_hashes()
generate_symlinks()
delete_old_vms()
move_current_vms()
cleanup()
def install_virtualbox():
if not spawn.find_executable("virtualbox"):
check_output(['bench', 'install', 'virtualbox'])
def install_packer():
if not spawn.find_executable("packer") and not os.path.exists(os.path.join('/', 'opt', 'packer')):
check_output(['bench', 'install', 'packer'])
def silent_remove(name, is_dir=False):
'''
Method to safely remove a file or directory,
without throwing error if file doesn't exist
By default takes in file as input, for directory:
is_dir = True
'''
try:
if is_dir:
rmtree(name)
else:
os.remove(name)
except OSError as e:
if e.errno != errno.ENOENT: # errno.ENOENT = no such file or directory
raise # re-raise exception if a different error occurred
def cleanup():
silent_remove("Production Builds", is_dir=True)
silent_remove("Developer Builds", is_dir=True)
silent_remove("packer_virtualbox-iso_virtualbox-iso_md5.checksum")
def build_vm():
check_output(["packer", "build", "vm-production.json"])
check_output(["packer", "build", "vm-develop.json"])
def md5(build, file):
return check_output("md5sum '{} Builds/{}'".format(build, file), shell=True).split()[0]
def move_to_public(build, file):
NEW_FILES.append(file)
src = os.path.join('{} Builds/{}'.format(build, file))
dest = os.path.join(PUBLIC_DIR, file)
os.rename(src, dest)
# Make Public folder readable by others
st = os.stat(dest)
os.chmod(dest, st.st_mode | stat.S_IROTH)
def generate_md5_hashes():
for build in BUILDS:
for file in os.listdir('{} Builds'.format(build)):
if file.endswith(".ova") or file.endswith(".box"):
with open('{} Builds/{}.md5'.format(build, file), 'w') as f:
f.write(md5(build, file))
move_to_public(build, file)
move_to_public(build, '{}.md5'.format(file))
def generate_symlinks():
for file in NEW_FILES:
if 'md5' in file:
if 'Vagrant' in file:
silent_remove(os.path.join(PUBLIC_DIR, 'ERPNext-Vagrant.box.md5'))
os.symlink(os.path.join(PUBLIC_DIR, file),
os.path.join(PUBLIC_DIR, 'ERPNext-Vagrant.box.md5'))
elif 'Production' in file:
silent_remove(os.path.join(PUBLIC_DIR, 'ERPNext-Production.ova.md5'))
os.symlink(os.path.join(PUBLIC_DIR, file),
os.path.join(PUBLIC_DIR, 'ERPNext-Production.ova.md5'))
else: # Develop
silent_remove(os.path.join(PUBLIC_DIR, 'ERPNext-Dev.ova.md5'))
os.symlink(os.path.join(PUBLIC_DIR, file),
os.path.join(PUBLIC_DIR, 'ERPNext-Dev.ova.md5'))
else: # ova/box files
if 'Vagrant' in file:
silent_remove(os.path.join(PUBLIC_DIR, 'ERPNext-Vagrant.box'))
os.symlink(os.path.join(PUBLIC_DIR, file),
os.path.join(PUBLIC_DIR, 'ERPNext-Vagrant.box'))
elif 'Production' in file:
silent_remove(os.path.join(PUBLIC_DIR, 'ERPNext-Production.ova'))
os.symlink(os.path.join(PUBLIC_DIR, file),
os.path.join(PUBLIC_DIR, 'ERPNext-Production.ova'))
else: # Develop
silent_remove(os.path.join(PUBLIC_DIR, 'ERPNext-Dev.ova'))
os.symlink(os.path.join(PUBLIC_DIR, file),
os.path.join(PUBLIC_DIR, 'ERPNext-Dev.ova'))
def delete_old_vms():
silent_remove(os.path.join(PUBLIC_DIR, 'BACKUPS'), is_dir=True)
def move_current_vms():
os.mkdir(os.path.join(PUBLIC_DIR, 'BACKUPS'))
for file in os.listdir(PUBLIC_DIR):
if file in NEW_FILES or file in SYMLINKS or file == 'BACKUPS':
continue
src = os.path.join(PUBLIC_DIR, '{}'.format(file))
dest = os.path.join(PUBLIC_DIR, 'BACKUPS/{}'.format(file))
os.rename(src, dest)
if __name__ == "__main__":
main()

View File

@ -1,41 +0,0 @@
choose-mirror-bin mirror/http/proxy string
choose-mirror-bin mirror/http/proxy string
d-i base-installer/kernel/override-image string linux-server
d-i clock-setup/utc boolean true
d-i clock-setup/utc-auto boolean true
d-i finish-install/reboot_in_progress note
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
d-i partman-auto/disk string /dev/sda
d-i partman-auto-lvm/guided_size string max
d-i partman-auto/choose_recipe select atomic
d-i partman-auto/method string lvm
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-lvm/device_remove_lvm boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
d-i partman/confirm_write_new_label boolean true
d-i pkgsel/include string openssh-server cryptsetup build-essential libssl-dev libreadline-dev zlib1g-dev linux-source dkms nfs-common
d-i pkgsel/install-language-support boolean false
d-i pkgsel/update-policy select none
d-i pkgsel/upgrade select full-upgrade
d-i time/zone string UTC
tasksel tasksel/first multiselect standard, ubuntu-server
d-i console-setup/ask_detect boolean false
d-i keyboard-configuration/layoutcode string us
d-i keyboard-configuration/modelcode string pc105
d-i debian-installer/locale string en_US
# Create frappe user account.
d-i passwd/user-fullname string frappe
d-i passwd/username string frappe
d-i passwd/user-password password frappe
d-i passwd/user-password-again password frappe
d-i user-setup/allow-password-weak boolean true
d-i user-setup/encrypt-home boolean false
d-i passwd/user-default-groups frappe sudo
d-i passwd/user-uid string 900

View File

@ -1,13 +0,0 @@
#!/bin/bash -eux
# Apt cleanup
apt-get autoremove
apt-get clean
apt-get update
# Zero out the rest of the free space using dd, then delete the written file.
dd if=/dev/zero of=/EMPTY bs=1M
rm -f /EMPTY
# Add `sync` so Packer doesn't quit too early, before the large file is deleted.
sync

View File

@ -1,10 +0,0 @@
#!/bin/bash -eux
# Install Ansible repository.
apt -y update && apt-get -y upgrade
apt -y install software-properties-common
apt-add-repository ppa:ansible/ansible
# Install Ansible.
apt -y update
apt -y install ansible

View File

@ -1,5 +0,0 @@
#!/bin/bash -eux
# Install ERPNext
wget https://raw.githubusercontent.com/frappe/bench/develop/install.py
python install.py --develop --user frappe --mysql-root-password frappe --admin-password admin

View File

@ -1,5 +0,0 @@
#!/bin/bash -eux
# Install ERPNext
wget https://raw.githubusercontent.com/frappe/bench/develop/install.py
python install.py --production --user frappe --mysql-root-password frappe --admin-password admin

View File

@ -1,4 +0,0 @@
#!/bin/bash -eux
# Install base requirements.
apt-get install -y curl git wget vim python-dev gcc

View File

@ -1,8 +0,0 @@
#!/bin/bash -eux
# Add frappe user to sudoers.
echo "frappe ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
sed -i "s/^.*requiretty/#Defaults requiretty/" /etc/sudoers
# Disable daily apt unattended updates.
echo 'APT::Periodic::Enable "0";' >> /etc/apt/apt.conf.d/10periodic

View File

@ -1,14 +0,0 @@
#! /bin/bash
# Write out current crontab
crontab -l > current_cron
# Echo new cron into cron file
echo "@reboot sleep 20 && systemctl restart supervisor" >> current_cron
# Install new cron file
crontab current_cron
# Delete the temporary cron file
rm current_cron

View File

@ -1,18 +0,0 @@
#! /bin/bash
message="
ERPNext Evaluation VM (built on `date +\"%B %d, %Y\"`)
Please access ERPNext by going to http://localhost:8000 on the host system.
The username is \"Administrator\" and password is \"admin\"
Do consider donating at https://frappe.io/buy
To update, login as
username: frappe
password: frappe
cd frappe-bench
bench update
"
echo "$message" | sudo tee -a /etc/issue
echo "$message" | sudo tee -a /etc/motd

View File

@ -1,18 +0,0 @@
#! /bin/bash
message="
ERPNext VM (built on `date +\"%B %d, %Y\"`)
Please access ERPNext by going to http://localhost:8080 on the host system.
The username is \"Administrator\" and password is \"admin\"
Consider buying professional support from us at https://erpnext.com/support
To update, login as
username: frappe
password: frappe
cd frappe-bench
bench update
"
echo "$message" | sudo tee -a /etc/issue
echo "$message" | sudo tee -a /etc/motd

View File

@ -1,101 +0,0 @@
{
"builders": [{
"vm_name": "ERPNext-Develop-{{isotime \"20060102150405\"}}",
"output_directory": "Developer Builds",
"type": "virtualbox-iso",
"boot_command": [
"<enter><wait><f6><esc><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"/install/vmlinuz<wait>",
" auto<wait>",
" console-setup/ask_detect=false<wait>",
" console-setup/layoutcode=us<wait>",
" console-setup/modelcode=pc105<wait>",
" debconf/frontend=noninteractive<wait>",
" debian-installer=en_US<wait>",
" fb=false<wait>",
" initrd=/install/initrd.gz<wait>",
" kbd-chooser/method=us<wait>",
" keyboard-configuration/layout=USA<wait>",
" keyboard-configuration/variant=USA<wait>",
" locale=en_US<wait>",
" netcfg/get_domain=vm<wait>",
" netcfg/get_hostname=ubuntu<wait>",
" grub-installer/bootdev=/dev/sda<wait>",
" noapic<wait>",
" preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg",
" -- <wait>",
"<enter><wait>"
],
"boot_wait": "10s",
"format": "ova",
"guest_os_type": "Ubuntu_64",
"headless": true,
"iso_url": "http://releases.ubuntu.com/16.04/ubuntu-16.04.6-server-amd64.iso",
"iso_checksum": "ac8a79a86a905ebdc3ef3f5dd16b7360",
"iso_checksum_type": "md5",
"ssh_username": "frappe",
"ssh_password": "frappe",
"ssh_port": 22,
"ssh_wait_timeout": "10000s",
"http_directory": "http",
"guest_additions_mode": "disable",
"virtualbox_version_file": ".vbox_version",
"guest_additions_path": "VBoxGuestAdditions_{{.Version}}.iso",
"export_opts": [
"--vsys", "0",
"--product", "ERPNext",
"--producturl", "https://erpnext.com",
"--vendor", "Frappe Techonologies",
"--vendorurl", "https://frappe.io",
"--description", "ERPNext Evaluation VM"
],
"shutdown_command": "echo 'frappe'|sudo -S shutdown -P now",
"vboxmanage": [
[ "modifyvm", "{{.Name}}", "--memory", "1024" ],
[ "modifyvm", "{{.Name}}", "--cpus", "1" ],
[ "modifyvm", "{{.Name}}", "--audio", "none" ],
[ "modifyvm", "{{.Name}}", "--natpf1", "vm_ssh,tcp,,3022,,22" ],
[ "modifyvm", "{{.Name}}", "--natpf1", "vm_http,tcp,,8080,,80" ],
[ "modifyvm", "{{.Name}}", "--natpf1", "vm_http2,tcp,,8000,,8000" ]
]
}],
"provisioners": [{
"type": "shell",
"execute_command": "echo 'frappe' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
"script": "scripts/debian_family/install_ansible.sh"
}, {
"type": "shell",
"execute_command": "echo 'frappe' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
"script": "scripts/debian_family/setup.sh"
},{
"type": "shell",
"execute_command": "echo 'frappe' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
"script": "scripts/debian_family/install_prerequisites.sh"
}, {
"type": "shell",
"script": "scripts/debian_family/install_erpnext_develop.sh"
}, {
"type": "shell",
"execute_command": "echo 'frappe' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
"script": "scripts/debian_family/cleanup.sh"
}, {
"type": "shell",
"script": "scripts/set_message_develop.sh"
}, {
"type": "shell",
"execute_command": "echo 'frappe' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
"script": "scripts/restart_supervisor.sh"
}],
"post-processors": [{
"type": "checksum",
"checksum_types": ["md5"]
}, {
"type": "vagrant",
"keep_input_artifact": true,
"output": "Developer Builds/ERPNext-Vagrant-Develop-{{isotime \"20060102150405\"}}.box",
"vagrantfile_template": "Vagrantfile"
}]
}

View File

@ -1,95 +0,0 @@
{
"builders": [{
"vm_name": "ERPNext-Production-{{isotime \"20060102150405\"}}",
"output_directory": "Production Builds",
"type": "virtualbox-iso",
"boot_command": [
"<enter><wait><f6><esc><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"/install/vmlinuz<wait>",
" auto<wait>",
" console-setup/ask_detect=false<wait>",
" console-setup/layoutcode=us<wait>",
" console-setup/modelcode=pc105<wait>",
" debconf/frontend=noninteractive<wait>",
" debian-installer=en_US<wait>",
" fb=false<wait>",
" initrd=/install/initrd.gz<wait>",
" kbd-chooser/method=us<wait>",
" keyboard-configuration/layout=USA<wait>",
" keyboard-configuration/variant=USA<wait>",
" locale=en_US<wait>",
" netcfg/get_domain=vm<wait>",
" netcfg/get_hostname=ubuntu<wait>",
" grub-installer/bootdev=/dev/sda<wait>",
" noapic<wait>",
" preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg",
" -- <wait>",
"<enter><wait>"
],
"boot_wait": "10s",
"format": "ova",
"guest_os_type": "Ubuntu_64",
"headless": true,
"iso_url": "http://releases.ubuntu.com/16.04/ubuntu-16.04.6-server-amd64.iso",
"iso_checksum": "ac8a79a86a905ebdc3ef3f5dd16b7360",
"iso_checksum_type": "md5",
"ssh_username": "frappe",
"ssh_password": "frappe",
"ssh_port": 22,
"ssh_wait_timeout": "10000s",
"http_directory": "http",
"guest_additions_mode": "disable",
"virtualbox_version_file": ".vbox_version",
"guest_additions_path": "VBoxGuestAdditions_{{.Version}}.iso",
"export_opts": [
"--vsys", "0",
"--product", "ERPNext",
"--producturl", "https://erpnext.com",
"--vendor", "Frappe Techonologies",
"--vendorurl", "https://frappe.io",
"--description", "ERPNext Evaluation VM"
],
"shutdown_command": "echo 'frappe'|sudo -S shutdown -P now",
"vboxmanage": [
[ "modifyvm", "{{.Name}}", "--memory", "1024" ],
[ "modifyvm", "{{.Name}}", "--cpus", "1" ],
[ "modifyvm", "{{.Name}}", "--audio", "none" ],
[ "modifyvm", "{{.Name}}", "--natpf1", "vm_ssh,tcp,,3022,,22" ],
[ "modifyvm", "{{.Name}}", "--natpf1", "vm_http,tcp,,8080,,80" ]
]
}],
"provisioners": [{
"type": "shell",
"execute_command": "echo 'frappe' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
"script": "scripts/debian_family/install_ansible.sh"
}, {
"type": "shell",
"execute_command": "echo 'frappe' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
"script": "scripts/debian_family/setup.sh"
},{
"type": "shell",
"execute_command": "echo 'frappe' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
"script": "scripts/debian_family/install_prerequisites.sh"
}, {
"type": "shell",
"script": "scripts/debian_family/install_erpnext_production.sh"
}, {
"type": "shell",
"execute_command": "echo 'frappe' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
"script": "scripts/debian_family/cleanup.sh"
}, {
"type": "shell",
"script": "scripts/set_message_production.sh"
}, {
"type": "shell",
"execute_command": "echo 'frappe' | {{.Vars}} sudo -S -E bash '{{.Path}}'",
"script": "scripts/restart_supervisor.sh"
}],
"post-processors": [{
"type": "checksum",
"checksum_types": ["md5"]
}]
}