New way to deploy Ruby on Rails applications

If you set the `deployedExternally' option to true you can use the
nix-copy-closure command to deploy updates to the application.

See `scripts/deploy-rails-app.sh' for more details.
This commit is contained in:
Peter Jones 2019-04-05 13:22:03 -07:00
parent 7cc5c27782
commit 94d6158f04
No known key found for this signature in database
GPG key ID: 9DAFAA8D01941E49
5 changed files with 117 additions and 7 deletions

View file

@ -25,7 +25,7 @@ let
"${app.domain}" = {
forceSSL = config.phoebe.security.enable;
enableACME = config.phoebe.security.enable;
root = "${app.package}/share/${app.name}/public";
root = "${funcs.appLink app}/share/${app.name}/public";
locations = {
"/assets/" = {

View file

@ -10,6 +10,10 @@ rec {
# Where a Rails application lives:
home = name: "${base}/${name}";
##############################################################################
# Path to where the app is actually installed:
appLink = app: "${app.home}/package";
##############################################################################
# Is PostgreSQL local?
localpg = config.phoebe.services.postgresql.enable;

View file

@ -116,6 +116,21 @@ let
'';
};
deployedExternally = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
When true, system activation will not override the app link
in the home directory. However, it will be created if missing.
This is useful if you want to deploy your Rails application
using some external tool. In the Phoebe scripts directory
there is an example script for deploying with
nix-copy-closure.
'';
};
home = mkOption {
type = types.path;
description = "The directory where the application is deployed to.";

View file

@ -52,18 +52,22 @@ let
app.afterServices;
preStart = optionalString service.isMain ''
# Link the package into the application's home directory:
if [ ! -e "${funcs.appLink app}" ] || [ "${toString app.deployedExternally}" -ne 1 ]; then
ln -nfs "${app.package}" "${funcs.appLink app}"
fi
# Prepare the config directory:
rm -rf ${app.home}/config
mkdir -p ${app.home}/{config,log,tmp,db,state}
cp -rf ${app.package}/share/${app.name}/config.dist/* ${app.home}/config/
cp ${app.package}/share/${app.name}/db/schema.rb.dist ${app.home}/db/schema.rb
cp -rf ${funcs.appLink app}/share/${app.name}/config.dist/* ${app.home}/config/
cp ${funcs.appLink app}/share/${app.name}/db/schema.rb.dist ${app.home}/db/schema.rb
cp ${./database.yml} ${app.home}/config/database.yml
cp ${app.database.passwordFile} ${app.home}/state/database.password
# Additional set up for the home directory:
mkdir -p ${app.home}/home
ln -nfs ${app.package}/share/${app.name} ${app.home}/home/app
ln -nfs ${funcs.appLink app}/share/${app.name} ${app.home}/home/app
ln -nfs ${plib.attrsToShellExports "rails-${app.name}-env" (funcs.appEnv app)} ${app.home}/home/.env
cp ${./profile.sh} ${app.home}/home/.profile
chmod 0700 ${app.home}/home/.profile
@ -82,7 +86,7 @@ let
# Migrate the database:
${pkgs.sudo}/bin/sudo --user=rails-${app.name} --login \
${scripts.user}/bin/db-migrate.sh \
-r ${app.package}/share/${app.name} \
-r ${funcs.appLink app}/share/${app.name} \
-s ${app.home}/state
'';
@ -92,7 +96,7 @@ let
'';
serviceConfig = {
WorkingDirectory = "${app.package}/share/${app.name}";
WorkingDirectory = "-${funcs.appLink app}/share/${app.name}";
Restart = "on-failure";
TimeoutSec = "infinity"; # FIXME: what's a reasonable amount of time?
Type = "simple";

87
scripts/deploy-rails-app.sh Executable file
View file

@ -0,0 +1,87 @@
#!/bin/sh
################################################################################
# Example script to deploy a rails application:
set -e
set -u
################################################################################
option_base="/var/lib/rails"
option_app_name=""
option_host=""
################################################################################
usage() {
cat <<EOF
Usage: $(basename "$0") -n <name> -t <host> [options]
-h This message
-n NAME Application name
-p PATH Base path of Rails application (default: $option_base)
-t HOST SSH host to deploy to (e.g., root@example.com)
EOF
}
################################################################################
while getopts "hn:p:t:" o; do
case "${o}" in
h) usage
exit
;;
n) option_app_name=$OPTARG
;;
p) option_base=$OPTARG
;;
t) option_host=$OPTARG
;;
*) exit 1
;;
esac
done
shift $((OPTIND-1))
################################################################################
if [ ! -e default.nix ]; then
>&2 echo "ERROR: Run this from a directory that has a default.nix file"
exit 1
fi
################################################################################
if [ -z "$option_app_name" ]; then
>&2 echo "ERROR: You must use the -n option to name the application"
exit 1
fi
################################################################################
if [ -z "$option_host" ]; then
>&2 echo "ERROR: You must use the -t option to specify the host"
exit 1
fi
################################################################################
if [ $# -ne 0 ]; then
>&2 echo "ERROR: invalid options given: $*"
exit 1
fi
################################################################################
echo "==> Building application"
path=$(nix-build --quiet --no-out-link)
echo "==> Uploading application to $option_host"
nix-copy-closure --use-substitutes --to "$option_host" "$path"
# Create the GC root:
echo "==> Installing and activating application"
ssh "$option_host" \
nix-store --add-root "$option_base/$option_app_name/package" \
--indirect --realize --quiet "$path"
# Restart the application:
echo "==> Restarting application"
ssh "$option_host" systemctl restart rails-"$option_app_name"-'\*'