#!/bin/sh set -e set -a # automatically export all variables . .env.development if [ -f .env.local ]; then . .env.local fi docker__compose() { if [ ! -x "$(command -v docker)" ]; then echo "docker compose is not installed" echo "installation instructions are here: https://docs.docker.com/desktop/" exit 0 fi ENV_LOCAL= if [ -f .env.local ]; then ENV_LOCAL='--env-file .env.local' fi CURRENT_UID=$(id -u) CURRENT_GID=$(id -g) command docker compose --env-file .env.development $ENV_LOCAL "$@" } docker__exec() { if [ ! -x "$(command -v docker)" ]; then echo "docker is not installed" echo "installation instructions are here: https://docs.docker.com/desktop/" exit 0 fi DOCKER_CLI_HINTS=false command docker exec -i "$@" } docker__sn_lnd() { t=$1 if [ "$t" = "-t" ]; then shift else t="" fi docker__exec $t -u lnd sn_lnd lncli "$@" } docker__stacker_lnd() { t=$1 if [ "$t" = "-t" ]; then shift else t="" fi docker__exec $t -u lnd stacker_lnd lncli "$@" } docker__stacker_cln() { t=$1 if [ "$t" = "-t" ]; then shift else t="" fi docker__exec $t -u clightning stacker_cln lightning-cli --regtest "$@" } sndev__start() { shift if [ $# -eq 0 ]; then docker__compose up --build exit 0 fi docker__compose up "$@" } sndev__help_start() { help=" start the sndev env USAGE $ sndev start [OPTIONS] [SERVICE...] OPTIONS" echo "$help" docker__compose up --help | awk '/Options:/{y=1;next}y' } sndev__stop() { shift docker__compose down "$@" } sndev__help_stop() { help=" stop the sndev env USAGE $ sndev stop [OPTIONS] [SERVICE...] OPTIONS" echo "$help" docker__compose down --help | awk '/Options:/{y=1;next}y' } sndev__open() { shift service=$(docker__compose ps $1 --format '{{.Label "CONNECT"}}') if [ -z "$service" ]; then echo "no url found for $1" exit 1 fi service="http://$service" echo "opening $1 ... $service" if [ "$(uname)" = "Darwin" ]; then open $service elif [ "$(uname)" = "Linux" ]; then xdg-open $service elif [ "$(uname)" = "Windows_NT" ]; then start $service fi } sndev__help_open() { help=" open a container's url if it has one USAGE $ sndev open SERVICE OPTIONS no options currently exist " echo "$help" } sndev__restart() { shift docker__compose restart "$@" } sndev__help_restart() { help=" restart the sndev env USAGE $ sndev restart [OPTIONS] [SERVICE...] OPTIONS" echo "$help" docker__compose restart --help | awk '/Options:/{y=1;next}y' } sndev__logs() { shift if [ $# -eq 1 ]; then docker__compose logs -t --tail=1000 -f "$@" exit 0 fi docker__compose logs "$@" } sndev__help_logs() { help=" get logs from sndev env USAGE $ sndev logs [OPTIONS] [SERVICE...] OPTIONS" echo "$help" docker__compose logs --help | awk '/Options:/{y=1;next}y' } sndev__status() { shift if [ $# -eq 0 ]; then docker__compose ps -a --format 'table {{.Service}}\t{{.State}}\t{{.Status}}\t{{.Label "CONNECT"}}' exit 0 fi docker__compose ps "$@" } sndev__help_status() { help=" show container status of sndev env USAGE $ sndev status [OPTIONS] [SERVICE...] OPTIONS" echo "$help" docker__compose ps --help | awk '/Options:/{y=1;next}y' } sndev__delete() { printf "this deletes containers, volumes, and orphans - are you sure? [y/N] " read -r answer if [ "$answer" = "y" ]; then docker__compose down --volumes --remove-orphans else echo "delete cancelled" fi } sndev__help_delete() { help=" remove orphans and volumes from sndev env equivalent to sndev stop --volumes --remove-orphans USAGE $ sndev delete " echo "$help" } sndev__fund_user() { shift if [ -z "$1" ]; then echo "<nym> argument required" sndev__help_fund_user exit 1 fi if [ -z "$2" ]; then echo "<msats> argument required" sndev__help_fund_user exit 2 fi re='^[0-9]+$' if ! [[ $2 =~ $re ]]; then echo "<msats> is not a positive integer" sndev__help_fund_user exit 3 fi docker__exec db psql -U sn -d stackernews -q <<EOF UPDATE users set msats = $2 where name = '$1'; EOF } sndev__help_fund_user() { help=" fund a nym without using an LN invoice (local only) USAGE $ sndev fund_user <nym> <msats> <nym> - the name of the user you want to fund <msats> - the amount of millisatoshis to set the account to. Must be >= 0 " echo "$help" } sndev__fund() { shift docker__stacker_lnd -t payinvoice "$@" } sndev__help_fund() { help=" pay a bolt11 for funding USAGE $ sndev fund <bolt11> [OPTIONS] OPTIONS" echo "$help" docker__stacker_lnd payinvoice -h | awk '/OPTIONS:/{y=1;next}y' | awk '!/^[\t ]+--pay_req value/' } sndev__cln_fund() { shift docker__stacker_cln -t pay "$@" } sndev__help_cln_fund() { help=" pay a bolt11 for funding with CLN USAGE $ sndev cln_fund <bolt11>" echo "$help" } sndev__cln_withdraw() { shift label=$(date +%s) docker__stacker_cln -t invoice "$1" "$label" sndev | jq -r '.bolt11' } sndev__help_cln_withdraw() { help=" create a bolt11 for withdrawal with CLN USAGE $ sndev cln_withdraw <amount msats>" echo "$help" } sndev__withdraw() { shift docker__stacker_lnd addinvoice --amt "$@" | jq -r '.payment_request' } sndev__help_withdraw() { help=" create a bolt11 for withdrawal USAGE $ sndev withdraw <amount sats> [OPTIONS] OPTIONS" echo "$help" docker__stacker_lnd addinvoice -h | awk '/OPTIONS:/{y=1;next}y' | awk '!/^[\t ]+(--amt|--amt_msat) value/' } sndev__psql() { shift docker__exec -t db psql "$@" -U sn -d stackernews } sndev__help_psql() { help=" open psql on db USAGE $ sndev psql [OPTIONS] OPTIONS" echo "$help" docker__exec db psql --help | awk '/General options:/{y=1;next}y' | sed -n '/Connection options:/q;p' | awk '!/^([\t ]+-l, --list)|([\t ]+-d, --dbname)|([\t ]+-\?, --help)|([\t ]--help=)/' } sndev__prisma() { shift docker__exec -t -u apprunner app npx prisma "$@" } sndev__help_prisma() { help=" run prisma commands USAGE $ sndev prisma [COMMAND] COMMANDS" echo "$help" sndev__prisma --help | awk '/Commands/{y=1;next}y' | awk '!/^([\t ]+init)|([\t ]+studio)/' | sed -n '/Flags/q;p' } sndev__lint() { shift docker__exec -t -u apprunner app npm run lint } sndev__help_lint() { help=" run linters USAGE $ sndev lint " echo "$help" } sndev__compose() { shift docker__compose "$@" } sndev__help_compose() { docker__compose --help } sndev__sn_lndcli() { shift docker__sn_lnd -t "$@" } sndev__help_sn_lndcli() { docker__sn_lnd --help } sndev__stacker_lndcli() { shift docker__stacker_lnd -t "$@" } sndev__help_stacker_lndcli() { docker__stacker_lnd --help } sndev__stacker_clncli() { shift docker__stacker_cln -t "$@" } sndev__help_stacker_clncli() { docker__stacker_cln help } sndev__stacker_litcli() { shift docker__exec -t litd litcli -n regtest --rpcserver localhost:8444 "$@" } sndev__help_stacker_litcli() { docker__exec -t litd litcli -h } __sndev__pr_track() { json=$(curl -fsSH "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/stackernews/stacker.news/pulls/$1") case $(git config --get remote.origin.url) in "http"*) url=$(echo "$json" | grep -e '"clone_url"' | head -n1 | sed -e 's/^.*"clone_url":[[:space:]]*"//; s/",[[:space:]]*$//') ;; *) url=$(echo "$json" | grep -e '"ssh_url"' | head -n1 | sed -e 's/^.*"ssh_url":[[:space:]]*"//; s/",[[:space:]]*$//') ;; esac push=$(git remote -v | grep -e "$url .*push" | head -n1) || true if [ -n "$push" ]; then remote=$(echo "$push" | cut -f 1) else remote=$(echo "$json" | grep -e '"login"' | head -n1 | sed -e 's/^.*"login":[[:space:]]*"//; s/",[[:space:]]*$//') git remote remove "$remote" 1>/dev/null 2>&1 || true git remote add "$remote" "$url" fi ref=$(echo "$json" | grep -e '"ref"' | head -n1 | sed -e 's/^.*"ref":[[:space:]]*"//; s/",[[:space:]]*$//') git fetch "$remote" "$ref" git checkout -t -b "pr/$1" "$remote/$ref" git config --local "remote.$remote.push" pr/$1:$ref exit 0 } __sndev__pr_detach() { refspec="+refs/pull/$1/head:refs/remotes/pr/$1" case $(git config --get remote.origin.url) in "http"*) git fetch https://github.com/stackernews/stacker.news.git "$refspec" ;; *) git fetch git@github.com:stackernews/stacker.news.git "$refspec" ;; esac git checkout "pr/$1" exit 0 } sndev__pr() { shift case $1 in -t|--track) call "__sndev__pr_track" "$2" ;; *) call "__sndev__pr_detach" "$1" ;; esac } sndev__help_pr() { help=" fetch and checkout a pr USAGE $ sndev pr [OPTIONS] <pr number> OPTIONS -t, --track track the pr in a new branch, creating a remote if necessary defaults to checking out the pr in a detached state " echo "$help" } sndev__login() { shift if [ -z "$1" ]; then echo "<nym> argument required" sndev__help_login exit 1 fi # hardcode token for which is the hex digest of the sha256 of # "SNDEV-TOKEN3_0W_PhDRZVanbeJsZZGIEljexkKoGbL6qGIqSwTjjI" # next-auth concats the token with the secret from env and then sha256's it token="d5fce54babffcb070c39f78d947761fd9ec37647fafcecb9734a3085a78e5c5e" salt="202c90943c313b829e65e3f29164fb5dd7ea3370d7262c4159691c2f6493bb8b" # upsert user with nym and nym@sndev.team email="$1@sndev.team" docker__exec db psql -U sn -d stackernews -q <<EOF INSERT INTO users (name) VALUES ('$1') ON CONFLICT DO NOTHING; UPDATE users SET email = '$email', "emailHash" = encode(digest(LOWER('$email')||'$salt', 'sha256'), 'hex') WHERE name = '$1'; INSERT INTO verification_requests (identifier, token, expires) VALUES ('$email', '$token', NOW() + INTERVAL '1 day') ON CONFLICT (token) DO UPDATE SET identifier = '$email', expires = NOW() + INTERVAL '1 day'; EOF echo echo "open url in browser" echo "http://localhost:3000/api/auth/callback/email?token=SNDEV-TOKEN&email=$1%40sndev.team" echo } sndev__help_login() { help=" login as a nym USAGE $ sndev login <nym> " echo "$help" } sndev__help() { if [ $# -eq 2 ]; then call "sndev__$1_$2" "$@" exit 0 fi help=" 888 888 888 .d8888b 88888b. .d88888 .d88b. 888 888 88K 888 '88b d88' 888 d8P Y8b 888 888 'Y8888b. 888 888 888 888 88888888 Y88 88P X88 888 888 Y88b 888 Y8b. Y8bd8P 88888P' 888 888 'Y88888 'Y8888 Y88P manages a docker based stacker news development environment USAGE $ sndev [COMMAND] $ sndev help [COMMAND] COMMANDS help show help env: start start env stop stop env restart restart env status status of env logs logs from env delete delete env sn: login login as a nym fund_user fund a nym without using an LN invoice lnd: fund pay a bolt11 for funding withdraw create a bolt11 for withdrawal cln: cln_fund pay a bolt11 for funding with CLN cln_withdraw create a bolt11 for withdrawal with CLN db: psql open psql on db prisma run prisma commands dev: pr fetch and checkout a pr lint run linters open open container url in browser other: compose docker compose passthrough sn_lndcli lncli passthrough on sn_lnd stacker_lndcli lncli passthrough on stacker_lnd stacker_clncli lightning-cli passthrough on stacker_cln stacker_litcli litcli passthrough on litd " echo "$help" } call() { func=$1 if type "$func" 1>/dev/null 2>&1; then # if it's sndev COMMAND help, then call help for that command case $3 in -h|--help|help) call "sndev__help_$2" exit 0 ;; esac shift # remove func from args "$func" "$@" # invoke our named function w/ all remaining arguments else # if it's sndev -h COMMAND, then call help for that command case $2 in -h|--help) call "sndev__help_$3" exit 0 ;; esac sndev__help exit 1 fi } call "sndev__$1" "$@"