Skip to content

Instantly share code, notes, and snippets.

@d10r
Last active February 14, 2025 09:55
Show Gist options
  • Save d10r/523c315db414b128a085223ddd3f840b to your computer and use it in GitHub Desktop.
Save d10r/523c315db414b128a085223ddd3f840b to your computer and use it in GitHub Desktop.
#!/bin/bash
# Script for deploying nodejs applications to the SF cloud ™.
# Requirements:
# - ssh access
# - application name in package.json or provided as env var APP_NAME
# - git url in package.json or provided as env var GIT_URL
# - package-lock.json
# - npm scripts "build" and "start"
# Optional:
# - .nvmrc file signalling the wanted node version
# - .env.prod (or file specified in env var ENV_FILE) with env vars to be used in production
# - custom install command(s) in env var INSTALL_CMD (default: npm ci && npm run build)
#
# When running for an existing app, it will:
# - update the .env file based on the local .env.prod
# - update the repo
# - update dependencies (npm ci)
# - build the app (npm run build)
# - restart the service
#
# When running for a new app, it will additionally first:
# - clone the repo
# - create a user systemd unit
set -eu
[email protected]
APP_NAME=${APP_NAME:-$(cat package.json | jq -r '.name')}
GIT_URL=${GIT_URL:-$(cat package.json | jq -r '.repository.url')}
ENV_FILE=${ENV_FILE:-.env.prod}
INSTALL_CMD=${INSTALL_CMD:-"node --version && npm ci && npm run build"}
SERVICE_FILE=$APP_NAME.service
echo "APP_NAME: $APP_NAME"
if [ -z "$APP_NAME" ] || [[ "$APP_NAME" =~ ' ' ]]; then
echo "Invalid app name"
exit 1
fi
echo "GIT_URL: $GIT_URL"
if [ -z "$GIT_URL" ]; then
echo "Invalid git url"
exit 1
fi
echo "ENV_FILE: $ENV_FILE"
if [ ! -f "$ENV_FILE" ]; then
echo "env file not found"
exit 1
fi
echo "INSTALL_CMD: $INSTALL_CMD"
# if there's a local env file (as specified), copy it to the server with the name .env.$APP_NAME
if [ -f "$ENV_FILE" ]; then
echo "Using $ENV_FILE for the production environment"
scp $ENV_FILE $SSH_HOST:~/.env.$APP_NAME
fi
# if any of the commands fails, don't continue
ssh -q -T $SSH_HOST "/bin/bash --noprofile --norc" <<EOF
set -e
set -u
. ./.profile
. .nvm/nvm.sh
# (conditional) first time setup
# if directory with app name doesn't exist, clone the repo
if [ ! -d "$APP_NAME" ]; then
echo "Cloning repo"
git clone $GIT_URL $APP_NAME
fi
# if service file doesn't exist, copy it from the template
if [ ! -f "services/$SERVICE_FILE" ]; then
echo "Copying service file"
cp template.service services/$SERVICE_FILE
sed -i "s|Description=|Description=$APP_NAME|" services/$SERVICE_FILE
sed -i "s|WorkingDirectory=|WorkingDirectory=/home/dapps/$APP_NAME|" services/$SERVICE_FILE
systemctl --user daemon-reload
sleep 1
systemctl --user enable $SERVICE_FILE
fi
# regular update
# if there's an .env.$APP_NAME, use it
if [ -f ".env.$APP_NAME" ]; then
echo "Using .env.$APP_NAME for the production environment"
cp .env.$APP_NAME $APP_NAME/.env
rm .env.$APP_NAME
else
echo "No .env.$APP_NAME found"
fi
cd $APP_NAME
git pull
if [ -f .nvmrc ]; then
nvm use
else
echo "no .nvmrc provided, using default node version"
fi
$INSTALL_CMD
cd
systemctl --user restart $SERVICE_FILE
systemctl --user -n 50 status $SERVICE_FILE
EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment