From 725338f3ac3ffab4a25576573451be48b4bfaf5c Mon Sep 17 00:00:00 2001 From: Tanner Doshier Date: Thu, 26 Dec 2024 12:37:52 -0500 Subject: [PATCH] Refactor bin/docker-wrapper Catch it up on some breaking changes and generally try to make it less brittle. Mount a host log directory for the tool inside docker to write to. Mount the host gitconfig (if it exists), so git commits work inside docker. Try to mount any argument that looks like a host path into the docker environment, with the same path. Dropping the special handling for the "project dir" location. Remove the setuid bits from `bin/docker-entry`. Using docker-wrapper is pretty much required, so avoid extra work. --- bin/docker-entry | 7 --- bin/docker-wrapper | 110 ++++++++++++++++++++++++++++++--------------- 2 files changed, 75 insertions(+), 42 deletions(-) diff --git a/bin/docker-entry b/bin/docker-entry index 89c4fa1..b6b88d4 100755 --- a/bin/docker-entry +++ b/bin/docker-entry @@ -2,11 +2,4 @@ set -eu -PROJECT_DIR=${PROJECT_DIR:-.} - -if [ "$(id -u)" -ne "$(stat -c '%u' "${PROJECT_DIR}")" ]; then - # shellcheck disable=SC2046 - exec setpriv --clear-groups $(stat -c '--reuid %u --regid %g' "${PROJECT_DIR}") "$0" "$@" -fi - exec nava-platform "$@" diff --git a/bin/docker-wrapper b/bin/docker-wrapper index b89d190..3dfe8c0 100755 --- a/bin/docker-wrapper +++ b/bin/docker-wrapper @@ -11,63 +11,103 @@ # not explode in complexity, it does make some assumptions (or reserves the # right to): # -# - There will always be a project directory argument present and it will always -# be the last argument passed in # - File path arguments will start with either `.` or `/` # set -euo pipefail processed_args=() docker_flags=() +detected_host_paths=() + +looks_like_path() { + [[ "$1" =~ ^[.]*/ ]] +} + +uname_out="$(uname -s)" +case "${uname_out}" in + Linux*) host_os=Linux;; + Darwin*) host_os=Mac;; + *) host_os="UNKNOWN:${uname_out}" +esac # not strictly required with magic in `docker-entry` script and mostly for when # running on Linux, but explicitly run as host user to help avoid any file # permission issues with mounted locations from the host file system docker_flags+=(--user "$(id -u):$(id -g)") +# connect the host git config if present, for better chance committing with work +# inside the container (theoretically someone may not have their name/email +# configured even if the config file exists) +if [[ -e "$HOME/.gitconfig" ]]; then + docker_flags+=("-v=$HOME/.gitconfig:/.gitconfig") +fi + +# figure out what location to mount for logs +HOST_LOG_DIR=${HOST_LOG_DIR:-} + +if [[ -z "${HOST_LOG_DIR}" ]]; then + if [[ "${host_os}" == "Linux" ]]; then + HOST_LOG_DIR="${XDG_STATE_HOME:-${HOME}/.local/state}/nava-platform-cli/log" + elif [[ "${host_os}" == "Mac" ]]; then + HOST_LOG_DIR="${HOME}/Library/Logs/nava-platform-cli" + else + echo "Your host OS '${host_os}' is not supported automatically. Please set HOST_LOG_DIR env var explicitly and try again." + exit 1 + fi +fi + +if [[ ! -d "${HOST_LOG_DIR}" ]]; then + mkdir -p "${HOST_LOG_DIR}" +fi +docker_flags+=("-v=${HOST_LOG_DIR}:/.local/state/nava-platform-cli/log:z") + +# process the script arguments for ((i=1;i<=$#;i++)) do eval "arg=\${$i}" + option="" # shellcheck disable=SC2154 - if [[ "${arg}" =~ --template-uri.* ]]; then - if [[ "${arg}" == *"="* ]]; then - template_value="${arg#*=}" - else - ((i += 1)) # next value should be the path - eval "template_value=\${$i}" - fi + if [[ "${arg}" =~ ^- && "${arg}" == *"="* ]]; then + # part before the = + option="${arg%%=*}" + # part after the = + value="${arg#*=}" - # does it look like a local path? - if [[ -d "${template_value}" ]]; then - abs_path=$(realpath "${template_value}") - docker_flags+=("-v=${abs_path}:/template-dir:ro") - processed_args+=("--template-uri=/template-dir") - continue - fi + processed_args+=("${option}") + else + value="${arg}" fi - # the last argument should be the project path, will need adjusted if/when - # that's not the case for all commands - if [[ $i == "$#" ]]; then - last_arg="${arg}" - abs_last_arg=$(realpath "${last_arg}") + if looks_like_path "${value}"; then + abs_value=$(realpath "${value}") + detected_host_paths+=("${abs_value}") - host_project_dir=${abs_last_arg} - project_dir="/project-dir" - - docker_flags+=("-v=${host_project_dir}:${project_dir}:z") - processed_args+=("${project_dir}") - continue + docker_flags+=("-v=${abs_value}:${abs_value}:z") + processed_args+=("${abs_value}") + else + processed_args+=("${value}") fi - - processed_args+=("${arg}") done -# if the project path doesn't exist yet, create it before Docker tries to (with -# incorrect permissions) -if [[ ! -d "${host_project_dir}" ]]; then - mkdir "${host_project_dir}" -fi +# if host paths don't exist yet, particularly directories, create them before +# Docker does (with incorrect permissions) when it goes to mount them as a +# volume +# +# TODO: this is basically only to support initializing a project with a +# template. We could isolate that functionality in an `init` command that we +# have simpler special handling for or just not support that mode via docker? +for dpath in "${detected_host_paths[@]}"; do + if [[ ! -e "${dpath}" ]]; then + # basically, does the path look like a directory? + # - ends in a slash + # - or doesn't contain a `.` followed by a few characters, limiting the + # "file extension" to 8 charactes as the e2e tests create tmp directories + # with a dot followed by 9 characters + if [[ "${dpath}" =~ /$ || ! "${dpath}" =~ \..{1,8}$ ]]; then + mkdir -p "${dpath}" + fi + fi +done -docker run --interactive --tty --rm "${docker_flags[@]}" nava-platform-cli "${processed_args[@]}" +docker run --interactive --rm "${docker_flags[@]}" nava-platform-cli "${processed_args[@]}"