- PHP 7.4 support
- Improved Webp images support with Cloudflare (Issue [#95](https://github.com/WordOps/WordOps/issues/95)). Nginx will not serve webp images alternative with Cloudflare IP ranges.
- Stack upgrade for adminer
- Check acme.sh installation and setup acme.sh if needed before issuing certificate
- Add `--ufw` to `wo stack status`
- Add Nginx directive `gzip_static on;` to serve precompressed assets with Cache-Enabler or WP-Rocket. (Issue [#207](https://github.com/WordOps/WordOps/issues/207))
- Previous `--php73` & `--php73=off` flags are replaced by `--php72`, `--php73`, `--php74` to switch site's php version
- phpMyAdmin updated to v4.9.2
- Adminer updated to v4.7.5
- Replace dot and dashes by underscores in database names (Issue [#206](https://github.com/WordOps/WordOps/issues/206))
- Increased database name length to 32 characters from domain name + 8 random characters
- typo error in motd-news script (Issue [#204](https://github.com/WordOps/WordOps/issues/204))
- Install Nginx before ngxblocker
- WordOps install/update script text color
- Issue with MySQL stack on Raspbian 9/10
- Typo error  (PR [#205](https://github.com/WordOps/WordOps/pull/205))
- php version in `wo debug` (PR [#209](https://github.com/WordOps/WordOps/pull/209))
- SSL certificates expiration display with shared wildcard certificates
This commit is contained in:
VirtuBox
2019-12-03 19:48:18 +01:00
committed by GitHub
parent 63d2acf7ba
commit 01ee8c0a13
72 changed files with 3222 additions and 2521 deletions

View File

@@ -16,8 +16,41 @@ class WOAcme:
wo_acme_exec = ("/etc/letsencrypt/acme.sh --config-home "
"'/etc/letsencrypt/config'")
def check_acme(self):
"""
Check if acme.sh is properly installed,
and install it if required
"""
if not os.path.exists('/etc/letsencrypt/acme.sh'):
if os.path.exists('/opt/acme.sh'):
WOFileUtils.rm(self, '/opt/acme.sh')
WOGit.clone(
self, 'https://github.com/Neilpang/acme.sh.git',
'/opt/acme.sh', branch='master')
WOFileUtils.mkdir(self, '/etc/letsencrypt/config')
WOFileUtils.mkdir(self, '/etc/letsencrypt/renewal')
WOFileUtils.mkdir(self, '/etc/letsencrypt/live')
try:
WOFileUtils.chdir(self, '/opt/acme.sh')
WOShellExec.cmd_exec(
self, './acme.sh --install --home /etc/letsencrypt'
'--config-home /etc/letsencrypt/config'
'--cert-home /etc/letsencrypt/renewal'
)
WOShellExec.cmd_exec(
self, "{0} --upgrade --auto-upgrade"
.format(WOAcme.wo_acme_exec)
)
except CommandExecutionError as e:
Log.debug(self, str(e))
Log.error(self, "acme.sh installation failed")
if not os.path.exists('/etc/letsencrypt/acme.sh'):
Log.error(self, 'acme.sh ')
def export_cert(self):
"""Export acme.sh csv certificate list"""
# check acme.sh is installed
WOAcme.check_acme(self)
if not WOShellExec.cmd_exec(
self, "{0} ".format(WOAcme.wo_acme_exec) +
"--list --listraw > /var/lib/wo/cert.csv"):
@@ -26,6 +59,9 @@ class WOAcme:
def setupletsencrypt(self, acme_domains, acmedata):
"""Issue SSL certificates with acme.sh"""
# check acme.sh is installed
WOAcme.check_acme(self)
# define variables
all_domains = '\' -d \''.join(acme_domains)
wo_acme_dns = acmedata['acme_dns']
keylenght = acmedata['keylength']
@@ -74,6 +110,8 @@ class WOAcme:
def deploycert(self, wo_domain_name):
"""Deploy Let's Encrypt certificates with acme.sh"""
# check acme.sh is installed
WOAcme.check_acme(self)
if not os.path.isfile('/etc/letsencrypt/renewal/{0}_ecc/fullchain.cer'
.format(wo_domain_name)):
Log.error(self, 'Certificate not found. Deployment canceled')
@@ -135,6 +173,8 @@ class WOAcme:
def renew(self, domain):
"""Renew letsencrypt certificate with acme.sh"""
# check acme.sh is installed
WOAcme.check_acme(self)
try:
WOShellExec.cmd_exec(
self, "{0} ".format(WOAcme.wo_acme_exec) +
@@ -158,7 +198,10 @@ class WOAcme:
response = requests.get(url, headers=headers).json()
domain_ip = response["Answer"][0]['data']
except requests.RequestException:
Log.error(self, 'Resolving domain IP failed')
Log.error(
self, 'Resolving domain IP failed.\n'
'The domain {0} do not exist or a DNS record is missing'
.format(domain))
if(not domain_ip == server_ip):
Log.warn(
self, "{0}".format(domain) +
@@ -202,6 +245,8 @@ class WOAcme:
.format(WOVar.wo_ssl_live, domain),
'/etc/letsencrypt/shared/{0}.conf'.format(domain)]
wo_domain = domain
# check acme.sh is installed
WOAcme.check_acme(self)
if WOAcme.cert_check(self, wo_domain):
Log.info(self, "Removing Acme configuration")
Log.debug(self, "Removing Acme configuration")

View File

@@ -1,6 +1,7 @@
"""WordOps package installation using apt-get module."""
import subprocess
import sys
import os
from sh import ErrorReturnCode, apt_get
@@ -222,6 +223,29 @@ class WOAptGet():
# apt_cache.close()
return False
def is_exec(self, package_name):
"""
Check if package is available by looking
for an executable or a systemd service related
to this package
"""
exec_path = ["/bin", "/usr/bin", "/usr/local/bin",
"/usr/sbin", "/usr/local/sbin"]
for path in exec_path:
if os.path.exists('{0}/{1}'.format(path, package_name)):
return True
return False
def is_selected(self, package_name, packages_list):
"""
Check if package is selected for install/removal/purge
in packages_list
"""
for package in packages_list:
if package_name == package[2]:
return True
return False
def download_only(self, package_name, repo_url=None, repo_key=None):
"""
Similar to `apt-get install --download-only PACKAGE_NAME`

View File

@@ -252,10 +252,7 @@ class WOFileUtils():
Check if file exist on given path
"""
try:
if os.path.exists(path):
return (True)
else:
return (False)
return bool(os.path.exists(path))
except OSError as e:
Log.debug(self, "{0}".format(e.strerror))
Log.error(self, "Unable to check path {0}".format(path))
@@ -369,3 +366,22 @@ class WOFileUtils():
except IOError as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to append content in {0}".format(path))
def enabledisable(self, path, enable=True):
"""Switch conf from .conf.disabled to .conf or vice-versa"""
if enable:
Log.debug(self, "Check if disabled file exist")
if os.path.exists('{0}.disabled'.format(path)):
Log.debug(self, "Moving .disabled file")
shutil.move('{0}.disabled'.format(path), path)
return True
else:
return False
else:
Log.debug(self, "Check if .conf file exist")
if os.path.exists(path):
Log.debug(self, "Moving .conf file")
shutil.move(path, '{0}.disabled'.format(path))
return True
else:
return False

View File

@@ -18,25 +18,24 @@ class WOGit:
"""
for path in paths:
global git
git = git.bake("--git-dir={0}/.git".format(path),
"--work-tree={0}".format(path))
wogit = git.bake("-C", "{0}".format(path))
if os.path.isdir(path):
if not os.path.isdir(path + "/.git"):
try:
Log.debug(self, "WOGit: git init at {0}"
.format(path))
git.init(path)
wogit.init(path)
except ErrorReturnCode as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to git init at {0}"
.format(path))
status = git.status("-s")
status = wogit.status("-s")
if len(status.splitlines()) > 0:
try:
Log.debug(self, "WOGit: git commit at {0}"
.format(path))
git.add("--all")
git.commit("-am {0}".format(msg))
wogit.add("--all")
wogit.commit("-am {0}".format(msg))
except ErrorReturnCode as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to git commit at {0} "
@@ -49,9 +48,8 @@ class WOGit:
Checks status of file, If its tracked or untracked.
"""
global git
git = git.bake("--git-dir={0}/.git".format(repo),
"--work-tree={0}".format(repo))
status = git.status("-s", "{0}".format(filepath))
wogit = git.bake("-C", "{0}".format(repo))
status = wogit.status("-s", "{0}".format(filepath))
if len(status.splitlines()) > 0:
return True
else:
@@ -64,8 +62,7 @@ class WOGit:
"""
for path in paths:
global git
git = git.bake("--git-dir={0}/.git".format(path),
"--work-tree={0}".format(path))
wogit = git.bake("-C", "{0}".format(path))
if os.path.isdir(path):
if not os.path.isdir(path + "/.git"):
Log.error(
@@ -75,8 +72,8 @@ class WOGit:
Log.debug(
self, "WOGit: git stash --include-untracked at {0}"
.format(path))
git.stash("push", "--include-untracked", "-m {0}"
.format(msg))
wogit.stash("push", "--include-untracked", "-m {0}"
.format(msg))
except ErrorReturnCode as e:
Log.debug(self, "{0}".format(e))
Log.error(self, "Unable to git reset at {0} "

19
wo/core/nginx.py Normal file
View File

@@ -0,0 +1,19 @@
"""WordOps Nginx Manager"""
import subprocess
from wo.core.logging import Log
def check_config(self):
"""Check Nginx configuration and return boolean"""
Log.debug(self, "Testing Nginx configuration ")
# Check Nginx configuration before executing command
sub = subprocess.Popen('nginx -t', stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
output, error_output = sub.communicate()
if 'emerg' in str(error_output):
Log.debug(self, "Nginx configuration check failed")
return False
else:
Log.debug(self, "Nginx configuration check was successful")
return True

View File

@@ -20,15 +20,17 @@ class WOService():
# Check Nginx configuration before executing command
sub = subprocess.Popen('nginx -t', stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
output, error_output = sub.communicate()
if 'emerg' not in str(error_output):
output = sub.communicate()
if 'emerg' not in str(output):
Log.valide(self, "Testing Nginx configuration ")
Log.wait(self, "Starting Nginx ")
Log.wait(self, "Starting Nginx")
service_cmd = ('service {0} start'.format(service_name))
retcode = subprocess.getstatusoutput(service_cmd)
if retcode[0] == 0:
Log.valide(self, "Starting Nginx ")
return True
else:
Log.failed(self, "Starting Nginx")
else:
Log.failed(self, "Testing Nginx configuration ")
return False
@@ -129,15 +131,15 @@ class WOService():
output, error_output = sub.communicate()
if 'emerg' not in str(error_output):
Log.valide(self, "Testing Nginx configuration ")
Log.wait(self, "Reloading Nginx ")
Log.wait(self, "Reloading Nginx")
service_cmd = ('service {0} reload'.format(service_name))
retcode = subprocess.getstatusoutput(service_cmd)
if retcode[0] == 0:
Log.valide(self, "Reloading Nginx ")
Log.valide(self, "Reloading Nginx")
return True
else:
Log.failed(self, "Testing Nginx configuration ")
return False
else:
Log.failed(self, "Testing Nginx configuration ")
return False
else:
service_cmd = ('service {0} reload'.format(service_name))
Log.wait(self, "Reloading {0:10}".format(
@@ -160,10 +162,11 @@ class WOService():
def get_service_status(self, service_name):
try:
is_exist = subprocess.getstatusoutput('which {0}'
is_exist = subprocess.getstatusoutput('command -v {0}'
.format(service_name))
if is_exist[0] == 0 or service_name in ['php7.2-fpm',
'php7.3-fpm']:
'php7.3-fpm',
'php7.4-fpm']:
retcode = subprocess.getstatusoutput('service {0} status'
.format(service_name))
if retcode[0] == 0:

View File

@@ -37,27 +37,6 @@ class WOShellExec():
Log.debug(self, str(e))
raise CommandExecutionError
def cmd_exist(self, command):
"""Check if a command exist with command -v"""
try:
Log.debug(self, "Testing command: {0}".format(command))
testing_command = ("command -v {0}".format(command))
with subprocess.Popen([testing_command], stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True) as proc:
(cmd_stdout_bytes, cmd_stderr_bytes) = proc.communicate()
(cmd_stdout, cmd_stderr) = (
cmd_stdout_bytes.decode('utf-8', "replace"),
cmd_stderr_bytes.decode('utf-8', "replace"))
Log.debug(self, "Command Output: {0}, \nCommand Error: {1}"
.format(cmd_stdout, cmd_stderr))
return bool(proc.returncode == 0)
except OSError as e:
Log.debug(self, str(e))
raise CommandExecutionError
except Exception as e:
Log.debug(self, str(e))
raise CommandExecutionError
def invoke_editor(self, filepath, errormsg=''):
"""
Open files using sensible editor

View File

@@ -6,26 +6,39 @@ from wo.core.fileutils import WOFileUtils
from wo.core.logging import Log
from wo.core.shellexec import WOShellExec
from wo.core.variables import WOVar
from wo.core.acme import WOAcme
class SSL:
def getexpirationdays(self, domain, returnonerror=False):
# check if exist
if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem'
if not os.path.exists('/etc/letsencrypt/live/{0}/cert.pem'
.format(domain)):
Log.error(self, 'File Not Found: '
'/etc/letsencrypt/live/{0}/cert.pem'
.format(domain), False)
if returnonerror:
return -1
Log.error(self, "Check the WordOps log for more details "
"`tail /var/log/wo/wordops.log` and please try again...")
Log.debug(self, "cert not found for {0}".format(domain))
split_domain = domain.split('.')
root_domain = ('.').join(split_domain[1:])
Log.debug(self, "trying with {0}".format(root_domain))
if os.path.exists('/etc/letsencrypt/live/{0}/cert.pem'
.format(root_domain)):
domain = root_domain
else:
Log.error(self, 'File Not Found: '
'/etc/letsencrypt/live/{0}/cert.pem'
.format(domain), False)
Log.error(
self, "Check the WordOps log for more details "
"`tail /var/log/wo/wordops.log` "
"and please try again...")
Log.debug(
self,
"Getting expiration of /etc/letsencrypt/live/{0}/cert.pem"
.format(domain))
current_date = WOShellExec.cmd_exec_stdout(self, "date -d \"now\" +%s")
expiration_date = WOShellExec.cmd_exec_stdout(
self, "date -d \""
"$(openssl x509 -in /etc/letsencrypt/live/"
self, "date -d \"$(openssl x509 -in /etc/letsencrypt/live/"
"{0}/cert.pem -text -noout | grep \"Not After\" "
"| cut -c 25-)\" +%s"
.format(domain))
@@ -39,23 +52,27 @@ class SSL:
def getexpirationdate(self, domain):
# check if exist
if os.path.islink('/var/www/{0}/conf/nginx/ssl.conf'):
split_domain = domain.split('.')
domain = ('.').join(split_domain[1:])
if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem'
.format(domain)):
Log.error(self, 'File Not Found: /etc/letsencrypt/'
'live/{0}/cert.pem'
.format(domain), False)
Log.error(self, "Check the WordOps log for more details "
"`tail /var/log/wo/wordops.log` and please try again...")
if os.path.exists('/var/www/{0}/conf/nginx/ssl.conf'):
split_domain = domain.split('.')
check_domain = ('.').join(split_domain[1:])
else:
Log.error(
self, 'File Not Found: /etc/letsencrypt/'
'live/{0}/cert.pem'
.format(domain), False)
Log.error(
self, "Check the WordOps log for more details "
"`tail /var/log/wo/wordops.log` and please try again...")
else:
check_domain = domain
expiration_date = WOShellExec.cmd_exec_stdout(
return WOShellExec.cmd_exec_stdout(
self, "date -d \"$(/usr/bin/openssl x509 -in "
"/etc/letsencrypt/live/{0}/cert.pem -text -noout | grep "
"\"Not After\" | cut -c 25-)\" "
.format(domain))
return expiration_date
.format(check_domain))
def siteurlhttps(self, domain):
wo_site_webroot = ('/var/www/{0}'.format(domain))
@@ -93,8 +110,8 @@ class SSL:
Log.valide(self, "Updating site url with https")
# check if a wildcard exist to secure a new subdomain
def checkwildcardexist(self, wo_domain_name):
"""Check if a wildcard certificate exist for a domain"""
wo_acme_exec = ("/etc/letsencrypt/acme.sh --config-home "
"'/etc/letsencrypt/config'")
@@ -110,31 +127,44 @@ class SSL:
reader = csv.reader(certfile, 'acmeconf')
wo_wildcard_domain = ("*.{0}".format(wo_domain_name))
for row in reader:
if wo_wildcard_domain in row[2]:
if not row[2] == "":
iswildcard = True
break
else:
iswildcard = False
if wo_wildcard_domain == row[2]:
if not row[3] == "":
return True
certfile.close()
return False
return iswildcard
def setuphsts(self, wo_domain_name, enable=True):
"""Enable or disable htsts for a site"""
if enable:
if WOFileUtils.enabledisable(
self, '/var/www/{0}/conf/nginx/hsts.conf'
):
return 0
else:
Log.info(
self, "Adding /var/www/{0}/conf/nginx/hsts.conf"
.format(wo_domain_name))
def setuphsts(self, wo_domain_name):
Log.info(
self, "Adding /var/www/{0}/conf/nginx/hsts.conf"
.format(wo_domain_name))
hstsconf = open("/var/www/{0}/conf/nginx/hsts.conf"
.format(wo_domain_name),
encoding='utf-8', mode='w')
hstsconf.write("more_set_headers "
"\"Strict-Transport-Security: "
"max-age=31536000; "
"includeSubDomains; "
"preload\";")
hstsconf.close()
return 0
hstsconf = open("/var/www/{0}/conf/nginx/hsts.conf"
.format(wo_domain_name),
encoding='utf-8', mode='w')
hstsconf.write("more_set_headers "
"\"Strict-Transport-Security: "
"max-age=31536000; "
"includeSubDomains; "
"preload\";")
hstsconf.close()
return 0
else:
if WOFileUtils.enabledisable(
self, '/var/www/{0}/conf/nginx/hsts.conf',
enable=False
):
Log.info(self, "HSTS disabled")
return 0
else:
Log.info(self, "HSTS is not enabled")
return 0
def selfsignedcert(self, proftpd=False, backend=False):
"""issue a self-signed certificate"""
@@ -197,3 +227,100 @@ class SSL:
"/etc/proftpd/ssl/proftpd.crt")
# remove self-signed tmp directory
WOFileUtils.rm(self, selfs_tmp)
def httpsredirect(self, wo_domain_name, acme_domains, redirect=True):
"""Create Nginx redirection from http to https"""
wo_acme_domains = ' '.join(acme_domains)
if redirect:
Log.wait(self, "Adding HTTPS redirection")
if WOFileUtils.enabledisable(
self, '/etc/nginx/conf.d/force-ssl-{0}.conf'
.format(wo_domain_name), enable=True):
Log.valide(self, "Adding HTTPS redirection")
return 0
else:
try:
sslconf = open(
"/etc/nginx/conf.d/force-ssl-{0}.conf"
.format(wo_domain_name),
encoding='utf-8', mode='w')
sslconf.write(
"server {\n"
"\tlisten 80;\n" +
"\tlisten [::]:80;\n" +
"\tserver_name {0};\n"
.format(wo_acme_domains) +
"\treturn 301 https://$host"
"$request_uri;\n}")
sslconf.close()
except IOError as e:
Log.debug(self, str(e))
Log.debug(
self, "Error occured while generating "
"/etc/nginx/conf.d/force-ssl-{0}.conf"
.format(wo_domain_name))
return 1
Log.valide(self, "Adding HTTPS redirection")
return 0
else:
if WOFileUtils.enabledisable(
self, "/etc/nginx/conf.d/force-ssl-{0}.conf"
.format(wo_domain_name), enable=False):
Log.info(
self, "Disabled HTTPS Force Redirection for site "
"{0}".format(wo_domain_name))
else:
Log.info(
self, "HTTPS redirection already disabled for site"
"{0}".format(wo_domain_name)
)
return 0
def archivedcertificatehandle(self, domain, acme_domains):
Log.warn(
self, "You already have an existing certificate "
"for the domain requested.\n"
"(ref: {0}/"
"{1}_ecc/{1}.conf)".format(WOVar.wo_ssl_archive, domain) +
"\nPlease select an option from below?"
"\n\t1: Reinstall existing certificate"
"\n\t2: Issue a new certificate to replace "
"the current one (limit ~5 per 7 days)"
"")
check_prompt = input(
"\nType the appropriate number [1-2] or any other key to cancel: ")
if not os.path.isfile("{0}/{1}/fullchain.pem"
.format(WOVar.wo_ssl_live, domain)):
Log.debug(
self, "{0}/{1}/fullchain.pem file is missing."
.format(WOVar.wo_ssl_live, domain))
check_prompt = "2"
if check_prompt == "1":
Log.info(self, "Reinstalling SSL cert with acme.sh")
ssl = WOAcme.deploycert(self, domain)
if ssl:
SSL.httpsredirect(self, domain, acme_domains)
elif (check_prompt == "2"):
Log.info(self, "Issuing new SSL cert with acme.sh")
ssl = WOShellExec.cmd_exec(
self, "/etc/letsencrypt/acme.sh "
"--config-home '/etc/letsencrypt/config' "
"--renew -d {0} --ecc --force"
.format(domain))
if ssl:
WOAcme.deploycert(self, domain)
else:
Log.error(self, "Operation cancelled by user.")
if os.path.isfile("{0}/conf/nginx/ssl.conf"
.format(domain)):
Log.info(self, "Existing ssl.conf . Backing it up ..")
WOFileUtils.mvfile(self, "/var/www/{0}/conf/nginx/ssl.conf"
.format(domain),
'/var/www/{0}/conf/nginx/ssl.conf.bak'
.format(domain))
return ssl

50
wo/core/stackconf.py Normal file
View File

@@ -0,0 +1,50 @@
import os
from wo.core.logging import Log
from wo.core.template import WOTemplate
from wo.core.variables import WOVar
class WOConf():
"""wo stack configuration utilities"""
def __init__():
pass
def nginxcommon(self):
"""nginx common configuration deployment"""
wo_php_version = ["php72", "php73", "php74"]
ngxcom = '/etc/nginx/common'
for wo_php in wo_php_version:
if not os.path.exists(ngxcom):
os.mkdir(ngxcom)
Log.debug(self, 'deploying templates for {0}'.format(wo_php))
data = dict(upstream="{0}".format(wo_php),
release=WOVar.wo_version)
WOTemplate.deploy(self,
'{0}/{1}.conf'
.format(ngxcom, wo_php),
'php.mustache', data)
WOTemplate.deploy(
self, '{0}/redis-{1}.conf'.format(ngxcom, wo_php),
'redis.mustache', data)
WOTemplate.deploy(
self, '{0}/wpcommon-{1}.conf'.format(ngxcom, wo_php),
'wpcommon.mustache', data)
WOTemplate.deploy(
self, '{0}/wpfc-{1}.conf'.format(ngxcom, wo_php),
'wpfc.mustache', data)
WOTemplate.deploy(
self, '{0}/wpsc-{1}.conf'.format(ngxcom, wo_php),
'wpsc.mustache', data)
WOTemplate.deploy(
self, '{0}/wprocket-{1}.conf'.format(ngxcom, wo_php),
'wprocket.mustache', data)
WOTemplate.deploy(
self, '{0}/wpce-{1}.conf'.format(ngxcom, wo_php),
'wpce.mustache', data)

View File

@@ -14,11 +14,11 @@ class WOVar():
"""Intialization of core variables"""
# WordOps version
wo_version = "3.10.3"
wo_version = "3.11.0"
# WordOps packages versions
wo_wp_cli = "2.3.0"
wo_adminer = "4.7.3"
wo_phpmyadmin = "4.9.1"
wo_wp_cli = "2.4.0"
wo_adminer = "4.7.5"
wo_phpmyadmin = "4.9.2"
wo_extplorer = "2.1.13"
wo_dashboard = "1.2"
@@ -133,16 +133,23 @@ class WOVar():
wo_nginx = ["nginx-custom", "nginx-wo"]
wo_nginx_key = '188C9FB063F0247A'
wo_php = ["php7.2-fpm", "php7.2-curl", "php7.2-gd", "php7.2-imap",
"php7.2-readline", "php7.2-common", "php7.2-recode",
"php7.2-cli", "php7.2-mbstring", "php7.2-intl",
"php7.2-bcmath", "php7.2-mysql", "php7.2-opcache",
"php7.2-zip", "php7.2-xml", "php7.2-soap"]
wo_php73 = ["php7.3-fpm", "php7.3-curl", "php7.3-gd", "php7.3-imap",
"php7.3-readline", "php7.3-common", "php7.3-recode",
"php7.3-cli", "php7.3-mbstring", "php7.3-intl",
"php7.3-bcmath", "php7.3-mysql", "php7.3-opcache",
"php7.3-zip", "php7.3-xml", "php7.3-soap"]
wo_module = ["fpm", "curl", "gd", "imap",
"readline", "common",
"cli", "mbstring", "intl",
"bcmath", "mysql", "opcache",
"zip", "xml", "soap"]
wo_php72 = []
for module in wo_module:
wo_php72 = wo_php72 + ["php7.2-{0}".format(module),
"php7.2-recode"]
wo_php73 = []
for module in wo_module:
wo_php73 = wo_php73 + ["php7.3-{0}".format(module),
"php7.3-recode"]
wo_php74 = []
for module in wo_module:
wo_php74 = wo_php74 + ["php7.4-{0}".format(module)]
wo_php_extra = ["php-memcached", "php-imagick",
"graphviz", "php-xdebug", "php-msgpack", "php-redis"]

17
wo/core/wpcli.py Normal file
View File

@@ -0,0 +1,17 @@
"""WordPress utilities for WordOps"""
from wo.core.logging import Log
from wo.core.shellexec import WOShellExec
from wo.core.variables import WOVar
class WOWp:
"""WordPress utilities for WordOps"""
def wpcli(self, command):
"""WP-CLI wrapper"""
try:
WOShellExec.cmd_exec(
self, '{0} --allow-root '.format(WOVar.wo_wpcli_path) +
'{0}'.format(command))
except Exception:
Log.error(self, "WP-CLI command failed")