2022-01-18 21:13:19 +00:00
#!/usr/bin/env bash
2022-08-17 16:02:25 +00:00
# Usage: ./develop.sh [--agpl]
#
# If the --agpl parameter is specified, builds only the AGPL-licensed code (no
# Coder enterprise features).
2022-09-07 16:40:17 +00:00
SCRIPT_DIR = $( dirname " ${ BASH_SOURCE [0] } " )
2022-11-10 18:47:42 +00:00
# shellcheck source=scripts/lib.sh
2022-09-07 16:40:17 +00:00
source " ${ SCRIPT_DIR } /lib.sh "
2022-06-22 13:02:31 +00:00
# Allow toggling verbose output
2022-09-07 16:40:17 +00:00
[ [ -n ${ VERBOSE :- } ] ] && set -x
2022-01-18 21:13:19 +00:00
set -euo pipefail
2023-08-21 17:23:07 +00:00
CODER_DEV_ACCESS_URL = " ${ CODER_DEV_ACCESS_URL :- http : //127.0.0.1 : 3000 } "
2023-03-08 21:50:55 +00:00
DEFAULT_PASSWORD = "SomeSecurePassword!"
password = " ${ CODER_DEV_ADMIN_PASSWORD :- ${ DEFAULT_PASSWORD } } "
2023-04-20 14:48:47 +00:00
use_proxy = 0
2022-09-07 16:40:17 +00:00
2023-08-21 17:23:07 +00:00
args = " $( getopt -o "" -l access-url:,use-proxy,agpl,password: -- " $@ " ) "
2022-08-17 16:02:25 +00:00
eval set -- " $args "
while true; do
case " $1 " in
2023-08-21 17:23:07 +00:00
--access-url)
CODER_DEV_ACCESS_URL = " $2 "
shift 2
; ;
2022-08-17 16:02:25 +00:00
--agpl)
2022-09-07 16:40:17 +00:00
export CODER_BUILD_AGPL = 1
2022-08-17 16:02:25 +00:00
shift
; ;
2022-09-07 16:40:17 +00:00
--password)
password = " $2 "
shift 2
; ;
2023-04-20 14:48:47 +00:00
--use-proxy)
use_proxy = 1
shift
; ;
2022-08-17 16:02:25 +00:00
--)
shift
break
; ;
*)
error " Unrecognized option: $1 "
; ;
esac
done
2023-04-20 14:48:47 +00:00
if [ " ${ CODER_BUILD_AGPL :- 0 } " -gt "0" ] && [ " ${ use_proxy } " -gt "0" ] ; then
echo '== ERROR: cannot use both external proxies and APGL build.' && exit 1
fi
2022-06-22 13:02:31 +00:00
# Preflight checks: ensure we have our required dependencies, and make sure nothing is listening on port 3000 or 8080
2023-08-02 18:28:16 +00:00
dependencies curl git go make pnpm
2022-06-22 18:04:12 +00:00
curl --fail http://127.0.0.1:3000 >/dev/null 2>& 1 && echo '== ERROR: something is listening on port 3000. Kill it and re-run this script.' && exit 1
curl --fail http://127.0.0.1:8080 >/dev/null 2>& 1 && echo '== ERROR: something is listening on port 8080. Kill it and re-run this script.' && exit 1
2022-01-18 21:13:19 +00:00
2022-09-07 16:40:17 +00:00
# Compile the CLI binary. This should also compile the frontend and refresh
# node_modules if necessary.
GOOS = " $( go env GOOS) "
GOARCH = " $( go env GOARCH) "
2022-09-08 05:22:08 +00:00
make -j " build/coder_ ${ GOOS } _ ${ GOARCH } "
2022-05-12 17:37:51 +00:00
2022-07-01 10:09:19 +00:00
# Use the coder dev shim so we don't overwrite the user's existing Coder config.
CODER_DEV_SHIM = " ${ PROJECT_ROOT } /scripts/coder-dev.sh "
2022-11-29 13:45:14 +00:00
# Stores the pid of the subshell that runs our main routine.
ppid = 0
# Tracks pids of commands we've started.
2022-11-10 18:47:42 +00:00
pids = ( )
exit_cleanup( ) {
set +e
# Set empty interrupt handler so cleanup isn't interrupted.
2022-11-29 13:45:14 +00:00
trap '' INT TERM
2022-11-10 18:47:42 +00:00
# Remove exit trap to avoid infinite loop.
trap - EXIT
2022-11-29 13:45:14 +00:00
# Send interrupts to the processes we started. Note that we do not
# (yet) want to send a kill signal to the entire process group as
# this can halt processes started by graceful shutdown.
2022-11-10 18:47:42 +00:00
kill -INT " ${ pids [@] } " >/dev/null 2>& 1
# Use the hammer if things take too long.
2022-11-29 13:45:14 +00:00
{ sleep 5 && kill -TERM " ${ pids [@] } " >/dev/null 2>& 1; } &
2022-11-10 18:47:42 +00:00
# Wait for all children to exit (this can be aborted by hammer).
wait_cmds
2022-11-29 13:45:14 +00:00
# Just in case, send termination to the entire process group
# in case the children left something behind.
kill -TERM -" ${ ppid } " >/dev/null 2>& 1
2022-11-10 18:47:42 +00:00
exit 1
}
start_cmd( ) {
2022-11-29 13:45:14 +00:00
name = $1
prefix = $2
shift 2
2022-11-10 18:47:42 +00:00
echo " == CMD: $* " >& 2
2022-11-29 13:45:14 +00:00
FORCE_COLOR = 1 " $@ " > >(
# Ignore interrupt, read will keep reading until stdin is gone.
trap '' INT
while read -r line; do
if [ [ $prefix = = date ] ] ; then
echo " [ $name ] $( date '+%Y-%m-%d %H:%M:%S' ) $line "
else
echo " [ $name ] $line "
fi
done
echo " == CMD EXIT: $* " >& 2
# Let parent know the command exited.
kill -INT $ppid >/dev/null 2>& 1
) 2>& 1 &
2022-11-10 18:47:42 +00:00
pids += ( " $! " )
}
wait_cmds( ) {
wait " ${ pids [@] } " >/dev/null 2>& 1
}
fatal( ) {
echo " == FAIL: $* " >& 2
2022-11-29 13:45:14 +00:00
kill -INT $ppid >/dev/null 2>& 1
2022-11-10 18:47:42 +00:00
}
2022-01-18 21:13:19 +00:00
# This is a way to run multiple processes in parallel, and have Ctrl-C work correctly
# to kill both at the same time. For more details, see:
# https://stackoverflow.com/questions/3004811/how-do-you-run-multiple-programs-in-parallel-from-a-bash-script
2022-02-02 02:02:59 +00:00
(
2022-11-29 13:45:14 +00:00
ppid = $BASHPID
2022-06-27 08:59:08 +00:00
# If something goes wrong, just bail and tear everything down
# rather than leaving things in an inconsistent state.
2022-11-29 13:45:14 +00:00
trap 'exit_cleanup' INT TERM EXIT
2022-11-10 18:47:42 +00:00
trap 'fatal "Script encountered an error"' ERR
2022-06-23 14:16:27 +00:00
cdroot
2023-08-21 17:23:07 +00:00
start_cmd API "" " ${ CODER_DEV_SHIM } " server --http-address 0.0.0.0:3000 --swagger-enable --access-url " ${ CODER_DEV_ACCESS_URL } " --dangerous-allow-cors-requests= true " $@ "
2022-05-27 17:15:19 +00:00
2022-06-22 13:02:31 +00:00
echo '== Waiting for Coder to become ready'
2022-11-29 13:45:14 +00:00
# Start the timeout in the background so interrupting this script
# doesn't hang for 60s.
2022-11-10 19:49:11 +00:00
timeout 60s bash -c 'until curl -s --fail http://localhost:3000/healthz > /dev/null 2>&1; do sleep 0.5; done' ||
2022-11-29 13:45:14 +00:00
fatal 'Coder did not become ready in time' &
wait $!
2022-11-10 18:47:42 +00:00
# Check if credentials are already set up to avoid setting up again.
" ${ CODER_DEV_SHIM } " list >/dev/null 2>& 1 && touch " ${ PROJECT_ROOT } /.coderv2/developsh-did-first-setup "
2022-06-15 21:02:18 +00:00
2022-09-13 17:31:33 +00:00
if [ ! -f " ${ PROJECT_ROOT } /.coderv2/developsh-did-first-setup " ] ; then
# Try to create the initial admin user.
2022-12-01 15:12:16 +00:00
if " ${ CODER_DEV_SHIM } " login http://127.0.0.1:3000 --first-user-username= admin --first-user-email= admin@coder.com --first-user-password= " ${ password } " --first-user-trial= true; then
2022-11-10 18:47:42 +00:00
# Only create this file if an admin user was successfully
# created, otherwise we won't retry on a later attempt.
touch " ${ PROJECT_ROOT } /.coderv2/developsh-did-first-setup "
else
2022-09-13 17:31:33 +00:00
echo 'Failed to create admin user. To troubleshoot, try running this command manually.'
2022-11-10 18:47:42 +00:00
fi
2022-06-15 21:02:18 +00:00
2022-09-13 17:31:33 +00:00
# Try to create a regular user.
" ${ CODER_DEV_SHIM } " users create --email= member@coder.com --username= member --password= " ${ password } " ||
echo 'Failed to create regular user. To troubleshoot, try running this command manually.'
fi
2022-06-27 08:59:08 +00:00
2022-09-07 16:40:17 +00:00
# If we have docker available and the "docker" template doesn't already
# exist, then let's try to create a template!
template_name = "docker"
2022-09-08 05:22:08 +00:00
if docker info >/dev/null 2>& 1 && ! " ${ CODER_DEV_SHIM } " templates versions list " ${ template_name } " >/dev/null 2>& 1; then
# sometimes terraform isn't installed yet when we go to create the
# template
sleep 5
2022-09-07 16:40:17 +00:00
temp_template_dir = " $( mktemp -d) "
2023-04-13 14:02:49 +00:00
" ${ CODER_DEV_SHIM } " templates init --id " ${ template_name } " " ${ temp_template_dir } "
2022-09-07 16:40:17 +00:00
DOCKER_HOST = " $( docker context inspect --format '{{ .Endpoints.docker.Host }}' ) "
printf 'docker_arch: "%s"\ndocker_host: "%s"\n' " ${ GOARCH } " " ${ DOCKER_HOST } " >" ${ temp_template_dir } /params.yaml "
2022-07-26 13:09:09 +00:00
(
2024-01-05 21:04:14 +00:00
" ${ CODER_DEV_SHIM } " templates push " ${ template_name } " --directory " ${ temp_template_dir } " --variables-file " ${ temp_template_dir } /params.yaml " --yes
2022-09-07 16:40:17 +00:00
rm -rfv " ${ temp_template_dir } " # Only delete template dir if template creation succeeds
2022-07-26 13:09:09 +00:00
) || echo " Failed to create a template. The template files are in ${ temp_template_dir } "
2022-06-27 08:59:08 +00:00
fi
2023-04-20 14:48:47 +00:00
if [ " ${ use_proxy } " -gt "0" ] ; then
log "Using external workspace proxy"
(
# Attempt to delete the proxy first, in case it already exists.
2023-08-01 15:50:43 +00:00
" ${ CODER_DEV_SHIM } " wsproxy delete local-proxy --yes || true
2023-04-20 14:48:47 +00:00
# Create the proxy
2023-05-05 18:09:04 +00:00
proxy_session_token = $( " ${ CODER_DEV_SHIM } " wsproxy create --name= local-proxy --display-name= "Local Proxy" --icon= "/emojis/1f4bb.png" --only-token)
2023-04-20 14:48:47 +00:00
# Start the proxy
2023-05-22 18:02:39 +00:00
start_cmd PROXY "" " ${ CODER_DEV_SHIM } " wsproxy server --dangerous-allow-cors-requests= true --http-address= localhost:3010 --proxy-session-token= " ${ proxy_session_token } " --primary-access-url= http://localhost:3000
2023-04-20 14:48:47 +00:00
) || echo "Failed to create workspace proxy. No workspace proxy created."
fi
2022-07-26 13:09:09 +00:00
# Start the frontend once we have a template up and running
2023-08-03 14:15:45 +00:00
CODER_HOST = http://127.0.0.1:3000 start_cmd SITE date pnpm --dir ./site dev --host
2022-11-10 18:47:42 +00:00
interfaces = ( localhost)
2023-01-17 18:45:27 +00:00
if command -v ip >/dev/null; then
2022-11-10 18:47:42 +00:00
# shellcheck disable=SC2207
interfaces += ( $( ip a | awk '/inet / {print $2}' | cut -d/ -f1) )
2023-01-17 18:45:27 +00:00
elif command -v ifconfig >/dev/null; then
2022-11-10 18:47:42 +00:00
# shellcheck disable=SC2207
interfaces += ( $( ifconfig | awk '/inet / {print $2}' ) )
fi
2022-09-07 16:40:17 +00:00
2022-11-10 18:47:42 +00:00
# Space padding used after the URLs to align "==".
space_padding = 26
2022-06-27 08:59:08 +00:00
log
2022-09-07 16:40:17 +00:00
log "===================================================================="
log "== =="
log "== Coder is now running in development mode. =="
2022-11-10 18:47:42 +00:00
for iface in " ${ interfaces [@] } " ; do
log " $( printf " == API: http://%s:3000% $(( space_padding - ${# iface } )) s== " " $iface " "" ) "
done
for iface in " ${ interfaces [@] } " ; do
log " $( printf " == Web UI: http://%s:8080% $(( space_padding - ${# iface } )) s== " " $iface " "" ) "
done
2023-04-20 14:48:47 +00:00
if [ " ${ use_proxy } " -gt "0" ] ; then
for iface in " ${ interfaces [@] } " ; do
log " $( printf " == Proxy: http://%s:3010% $(( space_padding - ${# iface } )) s== " " $iface " "" ) "
done
fi
2022-09-07 16:40:17 +00:00
log "== =="
log "== Use ./scripts/coder-dev.sh to talk to this instance! =="
2023-02-06 05:02:34 +00:00
log " $( printf " == alias cdr=%s/scripts/coder-dev.sh% $(( space_padding - ${# PWD } )) s== " " $PWD " "" ) "
2022-09-07 16:40:17 +00:00
log "===================================================================="
2022-06-27 08:59:08 +00:00
log
2022-09-07 16:40:17 +00:00
2022-06-27 08:59:08 +00:00
# Wait for both frontend and backend to exit.
2022-11-10 18:47:42 +00:00
wait_cmds
2022-02-02 02:02:59 +00:00
)