Skip to content

Instantly share code, notes, and snippets.

@aryomuzakki
Last active June 21, 2023 08:12
Show Gist options
  • Save aryomuzakki/c5d19685b4ff1f7b287bcc9f20924262 to your computer and use it in GitHub Desktop.
Save aryomuzakki/c5d19685b4ff1f7b287bcc9f20924262 to your computer and use it in GitHub Desktop.
Bash command in ubuntu to create a bare git repo and post commit hooks to start node js with pm2
#!/bin/bash
# USAGE:
# command example.com [OPTIONS]
#
# args desc
# example.com the domain name
#
# options args desc
# --app-name short-app-name required if --pm2-start is yes
# --domain-name domain-name
# --npm-install yes|no include npm install script in post hooks or not
# --npm-build yes|no include npm run build in post hooks or not
# --pm2-start yes|no include pm2 start script in post hooks or not
# -p --port 3000 required if --pm2-start option is yes
# -? show this help
# -h --help show this help
# example:
# > command example.com --npm-install yes --pm2-start no
#
# or
# > command example.com --pm2-start=yes --app-name=something --port 3000
#
# or
# > command --domain-name=example.com --npm-install no
# ## VARIABLES
domainName=
appName=
isUseNPM=
isUseNPMBuild=
isUsePM2=
port=
gitDir=
gitWorkTreeDir=
postHooksFile=
postHooksScript=
pm2ConfigFile=
pm2ConfigScript=
# exit and show error
function Die() {
echo "$1\n" >&2
printf "$s\n" "$1" >&2
exit 1
}
# show usage
function ShowHelp() {
cat <<-EOF
Usage for $0:
command example.com [OPTIONS]
args desc
example.com the domain name
options args desc
--app-name short-app-name required if --pm2-start is yes
--domain-name domain-name
--npm-install yes|no include npm install script in post hooks or not
--npm-build yes|no include npm run build in post hooks or not
--pm2-start yes|no include pm2 start script in post hooks or not
-p --port 3000 required if --pm2-start option is yes
-? show this help
-h --help show this help
example:
> command example.com --npm-install yes --npm-build yes --pm2-start no
or
> command example.com --pm2-start=yes --app-name=something --port 3000
or
> command --domain-name=example.com --npm-install no
EOF
}
# ## first argument
if [ "$1" ] && [[ "$1" != -* ]]; then # if first arg exist and not starts with -, assign domain name
domainName=$1
shift
fi
# ## option argument
if [ $# -gt 0 ]; then
while [ "$1" ]; do
case $1 in
--app-name)
# set app name
# if argument exist, set app name, else ask user input
if [ "$2" ]; then
if [[ "$2" == -* ]]; then
Die "Error: argument cannot start with \"-\" for option \"$1\""
else
appName=$2
shift 2
fi
else
Die "Error: argument required for \"$1\" option"
fi
;;
--app-name=?*)
# get argument after "=" sign
appName=${1#*=}
shift
;;
--app-name=)
# empty args
Die "Error: argument required for \"$1\" option"
;;
--domain-name)
# set domain name
# if domain name is not null
if [ "$domainName" ]; then
echo "Warning: redeclared domain name using \"$1\" option"
fi
if [ "$2" ]; then
if [[ "$2" == -* ]]; then
Die "Error: argument cannot start with \"-\" for option \"$1\""
else
domainName=$2
shift 2
fi
else
Die "Error: argument required for \"$1\" option"
fi
;;
--domain-name=?*)
# get argument after "=" sign
domainName=${1#*=}
shift
;;
--domain-name=)
# empty args
Die "Error: argument required for \"$1\" option"
;;
--npm-install)
# include or exclude npm install
echo "npm install case"
[[ "$2" == -* ]] && echo "ok"
if [ "$2" ]; then
if [[ "$2" == -* ]]; then
Die "Error: argument cannot start with \"-\" for option \"$1\""
else
if [ "$2" = "yes" ]; then
isUseNPM="include npm"
elif [ "$2" = "no" ]; then
isUseNPM="exclude npm"
fi
shift 2
fi
else
Die "Error: argument required for \"$1\" option"
fi
;;
--npm-install=?*)
# get argument after "=" sign
if [ "${1#*=}" = "yes" ]; then
isUseNPM="include npm"
elif [ "${1#*=}" = "no" ]; then
isUseNPM="exclude npm"
fi
shift
;;
--npm-install=)
# empty args
Die "Error: argument required for \"$1\" option"
;;
--npm-build)
# include or exclude npm run build
echo "npm run build case"
[[ "$2" == -* ]] && echo "ok"
if [ "$2" ]; then
if [[ "$2" == -* ]]; then
Die "Error: argument cannot start with \"-\" for option \"$1\""
else
if [ "$2" = "yes" ]; then
isUseNPMBuild="include npm run build"
elif [ "$2" = "no" ]; then
isUseNPMBuild="exclude npm run build"
fi
shift 2
fi
else
Die "Error: argument required for \"$1\" option"
fi
;;
--npm-build=?*)
# get argument after "=" sign
if [ "${1#*=}" = "yes" ]; then
isUseNPMBuild="include npm run build"
elif [ "${1#*=}" = "no" ]; then
isUseNPMBuild="exclude npm run build"
fi
shift
;;
--npm-build=)
# empty args
Die "Error: argument required for \"$1\" option"
;;
--pm2-start)
# include or exclude pm2 start script
if [ "$2" ]; then
if [[ "$2" == -* ]]; then
Die "Error: argument cannot start with \"-\" for option \"$1\""
else
if [ "$2" = "yes" ]; then
isUsePM2="include pm2"
elif [ "$2" = "no" ]; then
isUsePM2="exclude pm2"
fi
shift 2
fi
else
Die "Error: argument required for \"$1\" option"
fi
;;
--pm2-start=?*)
# get argument after "=" sign
if [ "${1#*=}" = "yes" ]; then
isUsePM2="include pm2"
elif [ "${1#*=}" = "no" ]; then
isUsePM2="exclude pm2"
fi
shift
;;
--pm2-start=)
# empty args
Die "Error: argument required for \"$1\" option"
;;
--port)
# set port in pm2 config
if [ "$2" ]; then
if [[ "$2" == -* ]]; then
Die "Error: argument cannot start with \"-\" for option \"$1\""
else
port=$2
shift 2
fi
else
Die "Error: argument required for \"$1\" option"
fi
;;
--port=?*)
# get argument after "=" sign
port=${1#*=}
shift
;;
--port=)
# empty args
Die "Error: argument required for \"$1\" option"
;;
-h | -\? | --help)
# display usage
ShowHelp
shift
exit
;;
*)
# unrecognized argument
Die "Error: \"$1\" is not recognized as an argument or option"
;;
esac
done
fi
# ## check required user input when argument not specified
# ## and set variables
while [ ! "$domainName" ]; do
read -p "*Provide domain name: (domain.com) " domainName
done
if [ "$domainName" ]; then
gitDir=${HOME}/repo/node/${domainName}.git
gitWorkTreeDir=${HOME}/source/node/${domainName}
postHooksFile=${gitDir}/hooks/post-receive
postHooksScript=$(
cat <<-EOF
#!/bin/sh
git --work-tree=$gitWorkTreeDir --git-dir=$gitDir checkout -f main
EOF
)
fi
while [ ! "$isUseNPM" ]; do
read -p "*Include npm install? (yes/no) " isUseNPM
if [ "$isUseNPM" = "yes" ]; then
isUseNPM="include npm"
elif [ "$isUseNPM" = "no" ]; then
isUseNPM="exclude npm"
else
isUseNPM=
fi
done
if [ "$isUseNPM" = "include npm" ]; then
# append npm script to post receive hook
postHooksScript+=$(
cat <<-EOF
echo "export nvs path"
export PATH=\$PATH:${HOME}/.nvs/default/bin
echo "changing directory"
cd ${gitWorkTreeDir}
echo "installing dependencies"
npm install
EOF
)
fi
while [ ! "$isUseNPMBuild" ]; do
read -p "*Include npm run build? (yes/no) " isUseNPMBuild
if [ "$isUseNPMBuild" = "yes" ]; then
isUseNPMBuild="include npm run build"
elif [ "$isUseNPMBuild" = "no" ]; then
isUseNPMBuild="exclude npm run build"
else
isUseNPMBuild=
fi
done
if [ "$isUseNPMBuild" = "include npm run build" ]; then
# append npm run build script to post receive hook
postHooksScript+=$(
cat <<-EOF
echo "building project"
npm run build
EOF
)
fi
while [ ! "$isUsePM2" ]; do
read -p "*Include pm2 start script? (yes/no) " isUsePM2
if [ "$isUsePM2" = "yes" ]; then
isUsePM2="include pm2"
while [ ! "$appName" ]; do
read -p "*Provide app name: (app-name) " appName
done
while [ ! "$port" ]; do
read -p "*Provide port number: (5000-5999) " port
done
elif [ "$isUsePM2" = "no" ]; then
isUsePM2="exclude pm2"
else
isUsePM2=
fi
done
if [ "$isUsePM2" = "include pm2" ]; then
# add pm2 config
pm2ConfigFile=${HOME}/source/node/ecosystem.config.js
pm2ConfigScript=$(
cat <<-EOF
{\
\n name: "${appName}",\
\n script: "npm",\
\n args: "run start",\
\n cwd: "./${domainName}",\
\n watch: true,\
\n ignore_watch: ["node_modules"],\
\n restart_delay: 1000,\
\n env: {\
\n PORT: $port,\
\n },\
\n },
EOF
)
# check if file exist and is not empty
if [ -f "$pm2ConfigFile" ] && [ -s "$pm2ConfigFile" ]; then
# get line position
ln=$(($(cat $pm2ConfigFile | wc -l) - 1))
else
ln=3
cat <<-EOF >$pm2ConfigFile
module.exports = {
apps: [
],
}
EOF
fi
# insert pm2ConfigScript object string into array string in pm2ConfigFile
# in the correct line position
pm2ConfigScript=$(sed "${ln}i\\$pm2ConfigScript" $pm2ConfigFile)
# append pm2 script to post receive hook
postHooksScript+=$(
cat <<-EOF
echo "set up and start from pm2 config file"
cd ${HOME}/source/node/
pm2 stop ${appName} || : && pm2 delete ${appName} || : && pm2 start ecosystem.config.js --only ${appName}
pm2 save
echo "completed"
EOF
)
fi
# main script
# create dirs
mkdir -p $gitDir $gitWorkTreeDir
# init git bare repo
if cd $gitDir; then
git init --bare --quiet
else
echo "failed changing directory to: $gitDir" >&2
echo "exit before running \"git init --bare\" and the rest of process" >&2
exit 1
fi
# create post-receive hooks and set executable
echo "$postHooksScript" >$postHooksFile
chmod +x $postHooksFile
# create copy of previous pm2 config
cp $pm2ConfigFile $pm2ConfigFile.former 2>/dev/null
# create pm2 config app if set
[ "$isUsePM2" = "include pm2" ] && echo "$pm2ConfigScript" >$pm2ConfigFile
# get public ip address
publicIP=$(curl https://ipinfo.io/ip)
# finish, ready to pushed to repo
# output repo link
echo "ssh://${USER}@$publicIP$gitDir"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment