Skip to content

Instantly share code, notes, and snippets.

@ctalkington
Last active February 27, 2023 03:06
Show Gist options
  • Save ctalkington/c1a7dac6876c037365ee2bca7bfcec50 to your computer and use it in GitHub Desktop.
Save ctalkington/c1a7dac6876c037365ee2bca7bfcec50 to your computer and use it in GitHub Desktop.
#!/bin/bash
if ! [ -x "$(command -v jq)" ]; then
printf "\x1B[31m[ERROR] jq is not installed.\x1B[0m\n"
exit 1
fi
if ! [ -x "$(command -v sed)" ]; then
printf "\x1B[31m[ERROR] sed is not installed (only GNU sed works).\x1B[0m\n"
exit 1
fi
if ! sed --version | grep "sed (GNU sed)" &>/dev/null; then
printf "\x1B[31m[ERROR] GNU sed is not installed.\x1B[0m\n"
exit 1
fi
REMOTE_USER=""
VERBOSE=0
debug() {
if [ $VERBOSE == 1 ]; then
printf "\x1B[33m[DEBUG] %s\x1B[0m\n" "${1}"
fi
}
while getopts ":u:v" opt; do
case ${opt} in
u)
REMOTE_USER="$OPTARG"
;;
v )
VERBOSE=1
;;
*)
if [ "$OPTERR" = 1 ]; then
echo "Unknown option -${OPTARG}" >&2
exit 5
fi
;;
esac
done
WORKSPACE="${*:$OPTIND:1}"
WORKSPACE="${WORKSPACE:-$PWD}"
if [[ ! -d "$WORKSPACE" ]]; then
echo "Directory $WORKSPACE does not exist!" >&2
exit 6
fi
echo "Using workspace ${WORKSPACE}"
CONFIG_DIR=./.devcontainer
debug "CONFIG_DIR: ${CONFIG_DIR}"
CONFIG_FILE=devcontainer.json
debug "CONFIG_FILE: ${CONFIG_FILE}"
if ! [ -e "$CONFIG_DIR/$CONFIG_FILE" ]; then
echo "Folder contains no devcontainer configuration"
exit
fi
CONFIG="$(cat "$CONFIG_DIR/$CONFIG_FILE")"
# Replacing variables in the config file
localWorkspaceFolderBasename="$(basename "$(realpath "$CONFIG_DIR/..")")"
CONFIG="$(echo "$CONFIG" | sed "s#\${localWorkspaceFolderBasename}#$localWorkspaceFolderBasename#g")"
localWorkspaceFolder="$(dirname "$localWorkspaceFolderBasename")"
CONFIG="$(echo "$CONFIG" | sed "s#\${localWorkspaceFolder}#$localWorkspaceFolder#g")"
# Remove trailing comma's with sed
CONFIG=$(echo "$CONFIG" | grep -v // | sed -Ez 's#,([[:space:]]*[]}])#\1#gm')
debug "CONFIG: \n${CONFIG}"
if [[ ! -d "$CONFIG_DIR" ]]; then
echo "Config dir '$CONFIG_DIR' does not exist!" >&2
exit 7
fi
cd "$CONFIG_DIR" || return
eval $(echo "$CONFIG" | jq -r '@sh "
DC_CONTEXT=\(.context)
DC_DOCKERFILE=\(.dockerFile // .build.dockerfile)
DC_REMOTE_USER=\(.remoteUser)
DC_SHELL=\(.settings."terminal.integrated.shell.linux")
DC_POST_CREATE_CMD=\((.postCreateCommand | to_entries? | map(.value | join(" "))? | join(" && ")) // (.postCreateCommand | join(" ")?) // .postCreateCommand)
DC_POST_START_CMD=\((.postStartCommand | to_entries? | map(.value | join(" "))? | join(" && ")) // (.postStartCommand | join(" ")?) // .postStartCommand)
"')
DOCKER_FILE="${DC_DOCKERFILE}"
DOCKER_FILE="$(readlink -f "$DOCKER_FILE")"
debug "DOCKER_FILE: ${DOCKER_FILE}"
if ! [ -e "$DOCKER_FILE" ]; then
echo "Can not find dockerfile ${DOCKER_FILE}"
exit
fi
CONTEXT="${DC_CONTEXT}"
if [ "$CONTEXT" == "null" ]; then
CONTEXT="."
fi
debug "CONTEXT: ${CONTEXT}"
if [ "$REMOTE_USER" == "" ] && [ "$DC_REMOTE_USER" != "null" ]; then
REMOTE_USER="${DC_REMOTE_USER}"
fi
debug "REMOTE_USER: ${REMOTE_USER}"
ARGS=$(echo "$CONFIG" | jq -r '.build.args | to_entries? | map("--build-arg \(.key)=\"\(.value)\"")? | join(" ")')
debug "ARGS: ${ARGS}"
SHELL="${DC_SHELL}"
if [ "$SHELL" == "null" ]; then
SHELL="/bin/bash"
fi
debug "SHELL: ${SHELL}"
APP_PORTS=$(echo "$CONFIG" | jq -r '.appPort | if type == "string" then [.] elif type == "number" then [.] else . end | map("-p \(.)")? | join(" ")')
debug "APP_PORTS: ${APP_PORTS}"
FORWARD_PORTS=$(echo "$CONFIG" | jq -r '.forwardPorts | map("-p \(.):\(.)")? | join(" ")')
debug "FORWARD_PORTS: ${FORWARD_PORTS}"
ENVS=$(echo "$CONFIG" | jq -r '.remoteEnv | to_entries? | map("-e \(.key)=\(.value)")? | join(" ")')
debug "ENVS: ${ENVS}"
WORK_DIR="/workspace"
debug "WORK_DIR: ${WORK_DIR}"
MOUNT="${MOUNT} --mount type=bind,source=${WORKSPACE},target=${WORK_DIR}"
debug "MOUNT: ${MOUNT}"
POST_CREATE_CMD="${DC_POST_CREATE_CMD}"
if [ "$DC_POST_CREATE_CMD" == "null" ]; then
POST_CREATE_CMD=""
fi
debug "POST_CREATE_CMD: ${POST_CREATE_CMD}"
POST_START_CMD="${DC_POST_START_CMD}"
if [ "$DC_POST_START_CMD" == "null" ]; then
POST_START_CMD=""
fi
debug "POST_START_CMD: ${POST_START_CMD}"
echo "Building and starting container"
DOCKER_TAG=$(echo "$DOCKER_FILE" | md5sum - | awk '{ print $1 }')
docker build -f "$DOCKER_FILE" -t "$DOCKER_TAG" $ARGS $CONTEXT
build_status=$?
if [[ $build_status -ne 0 ]]; then
echo "Building docker image failed..." >&2
exit 7
fi
debug "DOCKER_TAG: ${DOCKER_TAG}"
set -e
PUID=$(id -u)
PGID=$(id -g)
docker run -it $APP_PORTS $FORWARD_PORTS $ENVS $MOUNT -w "$WORK_DIR" "$DOCKER_TAG" "$SHELL" -c "\
if [ '$REMOTE_USER' != '' ] && command -v usermod &>/dev/null; \
then \
sudo=''
if [ \"$(stat -f -c '%u' "$(which sudo)")\" = '0' ]; then
sudo=sudo
fi
\$sudo usermod -u $PUID $REMOTE_USER && \
\$sudo groupmod -g $PGID $REMOTE_USER && \
\$sudo passwd -d $REMOTE_USER && \
\$sudo chown $REMOTE_USER:$REMOTE_USER -R ~$REMOTE_USER $WORK_DIR && \
su $REMOTE_USER -s $SHELL; \
else \
if [ "$POST_CREATE_COMMAND" != "" ]; then
$POST_CREATE_CMD;
fi
if [ "$POST_START_COMMAND" != "" ]; then
$POST_START_CMD;
fi
$SHELL; \
fi"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment