File: //usr/local/bin/dhwp/dhwp/controllers/stage.py
import os
import re
import sys
import shutil
import fileinput
from cement import App, Controller, ex
from cement import shell
from dhwp.core.git import Git
from dhwp.core.wordpress import Wordpress
class Stage(Controller):
class Meta:
label = 'stage'
description = 'staging commands'
stacked_on = 'base'
stacked_type = 'nested'
arguments = [
(['-p', '--path'],
{'help': 'path to live wordpress install',
'action': 'store',
'dest': 'path', }),
(['-d', '--destination'],
{'help': 'path to staging',
'action': 'store',
'dest': 'destination', }),
]
@ex(
help='create a new staging environment',
arguments=[
(['--url'],
{'help': 'staging site url',
'action': 'store',
'dest': 'url'}),
(['--dbname'],
{'help': 'staging database name',
'action': 'store',
'dest': 'dbname'}),
]
)
def create(self):
if not self.app.pargs.destination:
self.app.log.fatal("destination is required")
sys.exit(1)
destination = self.app.pargs.destination
live_wp = Wordpress(self.app.pargs.path)
live_url = live_wp.site_url()
live_git = Git(self.app.pargs.path)
self.delete()
if not os.path.isdir(destination):
os.makedirs(destination, 0o755)
if os.listdir(destination):
self.app.log.fatal("destination is not empty")
sys.exit(1)
self.app.log.info("Initalize live repository at %s" %
self.app.pargs.path)
live_git.cmd('init')
self.app.log.info("Commiting all changes in %s" % self.app.pargs.path)
live_git.commit('staging create', exclude_media=True, exclude_live_archives=True)
self.app.log.info("Creating new git worktree at %s" % destination)
live_git.cmd('worktree', 'add -b staging ' + destination + ' master -f')
self.app.log.info("Copying over wp-config from %s to %s" % (self.app.pargs.path, self.app.pargs.destination))
r = re.compile(r"https?://(www\.)?")
domain = r.sub('', live_url).strip().strip('/')
with open(os.path.join(self.app.pargs.path, 'wp-config.php'), 'r') as conf, \
open(os.path.join(self.app.pargs.destination, 'wp-config.php'), 'w') as stage_conf:
for line in conf:
wp_home = all(w in line for w in ('WP_HOME', domain))
wp_siteurl = all(w in line for w in ('WP_SITEURL', domain))
if wp_home or wp_siteurl:
self.app.log.info(
"Generating file %s: removed line %s" % (
os.path.join(self.app.pargs.destination, 'wp-config.php'), line))
else:
stage_conf.write(line)
stage_wp = Wordpress(destination)
stage_r = re.compile(r"^www\.")
stage_domain = stage_r.sub('', self.app.pargs.url)
stage_url = 'https://' + stage_domain
stage_db_file = destination + '/db.sql'
self.app.log.info("Creating Live backup")
live_wp.db_export(stage_db_file)
self.app.log.info("Importing live DB backup to stage")
stage_wp.cli('config set DB_NAME ' + self.app.pargs.dbname)
stage_wp.db_import(stage_db_file)
os.unlink(stage_db_file)
self.app.log.info("Setting Jetpack to staging mode")
stage_wp.cli('config set JETPACK_STAGING_MODE true --add --type=constant --raw')
self.app.log.info("Setting WordPress to staging mode")
stage_wp.cli('config set WP_ENVIRONMENT_TYPE staging --add --type=constant')
self.app.log.info("Setting Redis plugin to use own separate DB")
stage_wp.cli('config set WP_REDIS_DATABASE 1 --add --type=constant --raw')
self.app.log.info("Setting WordPress to not auto-update")
stage_wp.cli('config set AUTOMATIC_UPDATER_DISABLED true --add --type=constant')
self.app.log.info("Renaming WP site from %s to %s" %
(live_url, stage_url))
stage_wp.rename(live_url, stage_url)
self.app.log.info("Regenerating the .htaccess file")
stage_wp.cli('rewrite flush --hard')
@ex(
help='commit changes to environment',
arguments=[
(['--all'],
{'help': 'commit db and files',
'action': 'store_true',
'dest': 'all_changes', }),
(['--files'],
{'help': 'commit files only',
'action': 'store_true',
'dest': 'file_changes', }),
(['--db'],
{'help': 'commit database changes',
'action': 'store_true',
'dest': 'db_changes', }),
]
)
def commit(self):
stage_wp = Wordpress(self.app.pargs.destination)
live_wp = Wordpress(self.app.pargs.path)
live_git = Git(self.app.pargs.path)
stage_git = Git(self.app.pargs.destination)
live_url = live_wp.site_url()
stage_url = stage_wp.site_url()
if self.app.pargs.file_changes or self.app.pargs.all_changes:
if stage_git.has_changes():
self.app.log.info("Commiting all changes to staging")
stage_git.commit('staging changes')
self.app.log.info("Merging staging changes")
# TODO do we prefer changes to live or staging.. hrmm
live_git.git_ignore(exclude_media=True, exclude_live_archives=True)
shell.cmd("cd %s && for i in $(git ls-files . --exclude-standard --others); do rm -rf \"$i\"; done" % self.app.pargs.path)
live_git.cmd('reset', '--hard')
live_git.cmd('merge', '-X theirs staging')
else:
self.app.log.info("No file changes were found to commit")
if self.app.pargs.db_changes or self.app.pargs.all_changes:
stage_sql_file = stage_wp.db_export()
live_wp.db_import(stage_sql_file)
os.unlink(stage_sql_file)
live_wp.rename(stage_url, live_url)
live_wp.cli('core update-db')
live_wp.cli('cache flush')
@ex(
help='delete a staging environment',
arguments=[
]
)
def delete(self):
destination = self.app.pargs.destination
live_git = Git(self.app.pargs.path)
stage_git = Git(self.app.pargs.destination)
live_git.wipe()
stage_git.wipe()
if os.path.isdir(destination):
self.app.log.info("Clearing destination %s" % destination)
for root, dirs, files in os.walk(destination):
for f in files:
os.unlink(os.path.join(root, f))
for d in dirs:
if os.path.islink(os.path.join(root, d)):
os.unlink(os.path.join(root, d))
else:
shutil.rmtree(os.path.join(root, d))