diff --git a/rattail_fabric2/apache.py b/rattail_fabric2/apache.py
index edf847e..1d6073b 100644
--- a/rattail_fabric2/apache.py
+++ b/rattail_fabric2/apache.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2018 Lance Edgar
+# Copyright © 2010-2019 Lance Edgar
#
# This file is part of Rattail.
#
@@ -57,6 +57,16 @@ def enable_mod(c, *names):
c.sudo('a2enmod {}'.format(name))
+def enable_port(c, port):
+ """
+ Tell Apache to listen on the given port.
+ """
+ if not isinstance(port, int) and not port.isdigit():
+ raise ValueError("port must be an integer")
+ if c.run("grep '^Listen {}' /etc/apache2/ports.conf".format(port), warn=True).failed:
+ c.sudo("""bash -c 'echo "Listen {}" >> /etc/apache2/ports.conf'""".format(port))
+
+
def enable_site(c, *names):
"""
Enable the given Apache site(s)
diff --git a/rattail_fabric2/composer.py b/rattail_fabric2/composer.py
new file mode 100644
index 0000000..98d12f6
--- /dev/null
+++ b/rattail_fabric2/composer.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8; -*-
+################################################################################
+#
+# Rattail -- Retail Software Framework
+# Copyright © 2010-2019 Lance Edgar
+#
+# This file is part of Rattail.
+#
+# Rattail is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# Rattail. If not, see .
+#
+################################################################################
+"""
+Fabric lib for Composer (PHP dependency manager)
+"""
+
+from __future__ import unicode_literals, absolute_import
+
+from rattail_fabric2 import make_deploy, exists
+
+
+deploy = make_deploy(__file__)
+
+
+def install_globally(c):
+ """
+ Install `composer.phar` in global location
+ """
+ if not exists(c, '/usr/local/bin/composer.phar'):
+ deploy(c, 'composer/install-composer.sh', '/tmp/install-composer.sh', mode='0700', use_sudo=True)
+ c.sudo("bash -c 'cd /usr/local/bin; /tmp/install-composer.sh'")
+ c.sudo('rm /tmp/install-composer.sh')
diff --git a/rattail_fabric2/core.py b/rattail_fabric2/core.py
index 0d9f7a1..cd98a1f 100644
--- a/rattail_fabric2/core.py
+++ b/rattail_fabric2/core.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2018 Lance Edgar
+# Copyright © 2010-2019 Lance Edgar
#
# This file is part of Rattail.
#
@@ -214,6 +214,26 @@ class Deployer(object):
from rattail_fabric2.backup import deploy_backup_app
deploy_backup_app(c, self, envname, *args, **kwargs)
+ def certbot_account(self, c, uuid, localdir='certbot/account'):
+ """
+ Deploy files to establish a certbot account on target server
+ """
+ from .util import exists
+
+ localdir = localdir.rstrip('/')
+ paths = [
+ '/etc/letsencrypt/accounts',
+ '/etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org',
+ '/etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org/directory',
+ ]
+ final_path = '{}/{}'.format(paths[-1], uuid)
+ paths.append(final_path)
+ if not exists(c, final_path, use_sudo=True):
+ mkdir(c, paths, mode='0700', use_sudo=True)
+ self.deploy(c, '{}/private_key.json'.format(localdir), '{}/private_key.json'.format(final_path), mode='0600', use_sudo=True)
+ self.deploy(c, '{}/meta.json'.format(localdir), '{}/meta.json'.format(final_path), use_sudo=True)
+ self.deploy(c, '{}/regr.json'.format(localdir), '{}/regr.json'.format(final_path), use_sudo=True)
+
def make_deploy(deploy_path, last_segment='deploy'):
"""
diff --git a/rattail_fabric2/corepos.py b/rattail_fabric2/corepos.py
new file mode 100644
index 0000000..a7834b2
--- /dev/null
+++ b/rattail_fabric2/corepos.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8; -*-
+################################################################################
+#
+# Rattail -- Retail Software Framework
+# Copyright © 2010-2019 Lance Edgar
+#
+# This file is part of Rattail.
+#
+# Rattail is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# Rattail. If not, see .
+#
+################################################################################
+"""
+Fabric library for CORE-POS (IS4C)
+"""
+
+from __future__ import unicode_literals, absolute_import
+
+import os
+
+from rattail_fabric2 import mysql, exists, mkdir
+
+
+def install_fannie(c, rootdir, user='www-data', branch='version-2.10',
+ mysql_user='is4c', mysql_pass='is4c'):
+ """
+ Install the Fannie app to the given location.
+
+ Please note, this assumes composer is already installed and available.
+ """
+ mkdir(c, rootdir, owner=user, use_sudo=True)
+
+ # fannie source
+ is4c = os.path.join(rootdir, 'IS4C')
+ if not exists(c, is4c):
+ c.sudo('git clone https://github.com/CORE-POS/IS4C.git {}'.format(is4c), user=user)
+ c.sudo("bash -c 'cd {}; git checkout {}'".format(is4c, branch), user=user)
+ c.sudo("bash -c 'cd {}; git pull'".format(is4c), user=user)
+
+ # fannie dependencies
+ mkdir(c, [os.path.join(is4c, 'vendor'),
+ os.path.join(is4c, 'fannie/src/javascript/composer-components')],
+ owner=user, use_sudo=True)
+ c.sudo("bash -c 'cd {}; composer.phar install'".format(is4c), user=user)
+
+ # shadowread
+ # TODO: check first; only 'make' if necessary
+ c.sudo("bash -c 'cd {}/fannie/auth/shadowread; make'".format(is4c), user=user)
+ c.sudo("bash -c 'cd {}/fannie/auth/shadowread; make install'".format(is4c)) # as root!
+
+ # fannie logging
+ c.sudo("bash -c 'cd {}/fannie/logs; touch fannie.log debug_fannie.log queries.log php-errors.log dayend.log'".format(is4c), user=user)
+
+ # fannie databases
+ mysql.create_user(c, mysql_user, host='%', password=mysql_pass)
+ mysql.create_db(c, 'core_op', user="{}@'%'".format(mysql_user))
+ mysql.create_db(c, 'core_trans', user="{}@'%'".format(mysql_user))
+ mysql.create_db(c, 'trans_archive', user="{}@'%'".format(mysql_user))
diff --git a/rattail_fabric2/deploy/composer/install-composer.sh b/rattail_fabric2/deploy/composer/install-composer.sh
new file mode 100755
index 0000000..6f01e38
--- /dev/null
+++ b/rattail_fabric2/deploy/composer/install-composer.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+EXPECTED_SIGNATURE=$(wget -q -O - https://composer.github.io/installer.sig)
+php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
+ACTUAL_SIGNATURE=$(php -r "echo hash_file('SHA384', 'composer-setup.php');")
+
+if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
+then
+ >&2 echo 'ERROR: Invalid installer signature'
+ rm composer-setup.php
+ exit 1
+fi
+
+php composer-setup.php --quiet
+RESULT=$?
+rm composer-setup.php
+exit $RESULT