From 49d206305504b7269beca6dbd020df40b342edc9 Mon Sep 17 00:00:00 2001 From: Josh Curtiss Date: Tue, 19 Aug 2025 17:00:52 -0500 Subject: [PATCH 1/7] config: Add words to dictionary --- custom-words.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/custom-words.txt b/custom-words.txt index eaa569c..6e00c23 100644 --- a/custom-words.txt +++ b/custom-words.txt @@ -23,6 +23,7 @@ nodocker NOPASSWD opencontainers realpath +Retzky rmul rpms runnerdir From 1fb4de44b1fe95e42b72ed73b2e73b78d4c2e570 Mon Sep 17 00:00:00 2001 From: Josh Curtiss Date: Tue, 19 Aug 2025 16:08:29 -0500 Subject: [PATCH 2/7] feat: Add version to scripts Add ability for scripts that are installed to the system to output a version and build hash. --- rhel-9/bin/podman-install-service.sh | 16 +++++++++++++--- shared/bin/deploy.sh | 19 ++++++++++++++----- shared/bin/publish.sh | 21 ++++++++++++++++----- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/rhel-9/bin/podman-install-service.sh b/rhel-9/bin/podman-install-service.sh index c811da1..08fb11d 100755 --- a/rhel-9/bin/podman-install-service.sh +++ b/rhel-9/bin/podman-install-service.sh @@ -1,13 +1,22 @@ #!/bin/bash +version=1.0.0 bold=$(tput bold) ul=$(tput smul) red=$(tput setaf 1) yellow=$(tput setaf 3) norm=$(tput sgr0) +display_version() { + hash=$(cat "$0" | sha256sum | cut -c1-8) + echo "$(basename "$0") version $version build $hash" +} + display_help() { cat < [OPTIONS] Creates a system service for a container or pod. The container or pod must be @@ -18,17 +27,18 @@ option $bold--no-restart$norm, but then systemctl won't work until after an OS r Options: -h, --help Show this help message and exit. -n, --no-restart Don't restart the container when creating the service. +-V, --version Print version and exit. EOF } svc_name="$1" -[[ $* =~ -h || $* =~ --help ]] && display_help && exit +[[ $* =~ -h || $* =~ --help ]] && display_help && exit 1 +[[ $* =~ -V || $* =~ --version ]] && display_version && exit 1 [[ $* =~ -n || $* =~ --no-restart ]] && systemctl_opts=() || systemctl_opts=(--now) # Abort if service was not provided if [[ -z "$svc_name" ]] || [[ $svc_name == -* ]]; then - echo -e "${red}You must provide a service name.$norm\n" >&2 - display_help + echo -e "${red}You must provide a service name.$norm" >&2 exit 1 fi diff --git a/shared/bin/deploy.sh b/shared/bin/deploy.sh index e927817..8847d95 100755 --- a/shared/bin/deploy.sh +++ b/shared/bin/deploy.sh @@ -1,5 +1,6 @@ #!/bin/bash +version=1.0.0 bold=$(tput bold) dim=$(tput dim) ul=$(tput smul) @@ -14,8 +15,16 @@ verbose=false interactive=true env_file= +display_version() { + hash=$(cat "$0" | sha256sum | cut -c1-8) + echo "$(basename "$0") version $version build $hash" +} + display_help() { cat < [OPTIONS] Deploys an application stack with UIC Pharmacy standards: @@ -32,21 +41,19 @@ Options: -r, --repo Specify the repo to login to. (Default: $ul$repo_default$rmul) -u, --upgrade Upgrade by pulling the latest image. -v, --verbose Provide more verbose output. +-V, --version Print version and exit. EOF } # Positional parameter: Stack Path -if [[ $1 == -* || -z $1 ]]; then - [[ $1 == -h || $1 == --help ]] || echo "${red}You must provide a stack path.$norm" >&2 - display_help; exit 1; -else +if ! [[ $1 == -* || -z $1 ]]; then stack_path=$(realpath "$1") shift fi # Collect optional arguments. # spellchecker: disable-next-line -while getopts hnuve:r:-: OPT; do +while getopts hnuvVe:r:-: OPT; do # Ref: https://stackoverflow.com/a/28466267/519360 if [ "$OPT" = "-" ]; then OPT="${OPTARG%%=*}" # extract long option name @@ -55,6 +62,7 @@ while getopts hnuve:r:-: OPT; do fi case "$OPT" in h | help) display_help; exit 0 ;; + V | version) display_version; exit 0 ;; r | repo) repo=$OPTARG ;; e | env-file) env_file=$(realpath "$OPTARG") ;; n | non-interactive) interactive=false ;; @@ -67,6 +75,7 @@ done shift $((OPTIND - 1)) # Validation +[[ -z $stack_path ]] && echo "${red}You must provide a stack path.$norm" >&2 && exit 1 [[ ! -e $stack_path ]] && echo "${red}The stack $ul$stack_path$rmul doesn't exist.$norm" >&2 && exit 2 dir=${stack_path%/*} diff --git a/shared/bin/publish.sh b/shared/bin/publish.sh index 1926b0e..0838213 100755 --- a/shared/bin/publish.sh +++ b/shared/bin/publish.sh @@ -1,5 +1,6 @@ #!/bin/bash +version=1.0.0 bold=$(tput bold) ul=$(tput smul) rmul=$(tput rmul) @@ -24,8 +25,16 @@ err() { echo "$red$1$norm" >&2; } warn() { echo "$yellow$1$norm" >&2; } exec_cmd() { if $dry_run; then echo "${yellow}Command:${norm} $1" | tr -s ' '; else eval "$1"; fi } +display_version() { + hash=$(cat "$0" | sha256sum | cut -c1-8) + echo "$(basename "$0") version $version build $hash" +} + display_help() { cat < [context] [OPTIONS] Builds and publishes a container image with UIC Pharmacy standards: @@ -48,14 +57,12 @@ Options: --dry-run Show what would've happened without executing. --no-push Create the images, but do not push to registry. -v, --verbose Provide more verbose output. +-V, --version Print version and exit. EOF } # Positional parameter: Docker file -if [[ $1 == -* || -z $1 ]]; then - [[ $1 == -h || $1 == --help ]] || err "You must provide a Docker file." - display_help; exit 1; -else +if ! [[ $1 == -* || -z $1 ]]; then dockerfile=$(realpath "$1") shift fi @@ -71,7 +78,7 @@ fi # Collect optional arguments. # spellchecker: disable-next-line -while getopts heva:n:o:r:-: OPT; do +while getopts hevVa:n:o:r:-: OPT; do # Ref: https://stackoverflow.com/a/28466267/519360 if [ "$OPT" = "-" ]; then OPT="${OPTARG%%=*}" # extract long option name @@ -80,6 +87,7 @@ while getopts heva:n:o:r:-: OPT; do fi case "$OPT" in h | help) display_help; exit 0 ;; + V | version) display_version; exit 0 ;; a | arch) arches=$OPTARG ;; e | exact) exact=true ;; dry-run) dry_run=true ;; @@ -94,6 +102,9 @@ while getopts heva:n:o:r:-: OPT; do done shift $((OPTIND - 1)) +# Check Docker file +[[ -z $dockerfile ]] && err "You must provide a Docker file." && exit 2 + # Check dependencies, and abort if any are missing for c in tput realpath dirname basename docker jq; do if ! which "$c" &>/dev/null; then From 57534c6ca09a3827df0c1e227f23eabfb5bff44d Mon Sep 17 00:00:00 2001 From: Josh Curtiss Date: Mon, 18 Aug 2025 21:07:51 -0500 Subject: [PATCH 3/7] feat: Improve handling of Docker vs Podman in scripts The script is improved to handle either environments with Docker or Podman in a more effective way: - Do not rely on `docker` shim for podman - Detect which tool exists and execute it directly instead of using shims --- rhel-9/bin/podman-install-service.sh | 4 ++-- shared/bin/publish.sh | 24 +++++++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/rhel-9/bin/podman-install-service.sh b/rhel-9/bin/podman-install-service.sh index 08fb11d..7b08a66 100755 --- a/rhel-9/bin/podman-install-service.sh +++ b/rhel-9/bin/podman-install-service.sh @@ -42,8 +42,8 @@ if [[ -z "$svc_name" ]] || [[ $svc_name == -* ]]; then exit 1 fi -# Only run if "docker" is answering as podman -if [[ $(docker --version) == podman* ]]; then +# Only run if we are using podman +if [[ $(podman --version 2>/dev/null) == podman* ]]; then ( cd /etc/systemd/system || exit 1 echo "Setting up service as $(tput bold)$svc_name$(tput sgr0):" diff --git a/shared/bin/publish.sh b/shared/bin/publish.sh index 0838213..490a04b 100755 --- a/shared/bin/publish.sh +++ b/shared/bin/publish.sh @@ -105,20 +105,29 @@ shift $((OPTIND - 1)) # Check Docker file [[ -z $dockerfile ]] && err "You must provide a Docker file." && exit 2 +# Check if podman or docker is present +if [[ $(docker --version 2> /dev/null) =~ Docker ]]; then + container_binary=docker + is_podman=false +elif [[ $(podman --version 2> /dev/null) =~ podman ]]; then + container_binary=podman + is_podman=true +else + err 'This script requires either Docker or Podman. Aborting.' + ext 1 +fi + # Check dependencies, and abort if any are missing -for c in tput realpath dirname basename docker jq; do +for c in tput realpath dirname basename "$container_binary" jq; do if ! which "$c" &>/dev/null; then err "The $ul$c$rmul command is required by this script. Aborting." exit 1 fi done -# Detect podman -[[ $(docker --version) == podman* ]] && is_podman=true || is_podman=false - # Check arch... when running as podman, only support native arch. # Get arch info from docker/podman itself. Sadly, they report that info differently. -docker_info=$(docker info -f json) +docker_info=$("$container_binary" info -f json) native_arch=$(jq -r '.OSType' <<< "$docker_info")/$(jq -r '.Architecture' <<< "$docker_info") $is_podman && native_arch=$(jq -r '.host.os' <<< "$docker_info")/$(jq -r '.host.arch' <<< "$docker_info") if $is_podman && [[ $arches != "$native_arch" ]]; then @@ -179,6 +188,7 @@ if $verbose; then echo echo "${bold}${ul}Building $img$norm" echo + echo "${bold}Container Tool:$norm $container_binary" [[ -n $builder ]] && echo "${bold}Builder:$norm $builder" echo "${bold}Dockerfile:$norm $dockerfile" echo "${bold}Context:$norm $context" @@ -200,7 +210,7 @@ fi # Build images and push them to the registry $push && ! $is_podman && push_param=--push -exec_cmd "docker build \ +exec_cmd "$container_binary build \ -f $dockerfile \ --platform $arches \ $(for tag in "${tags[@]}"; do echo -n "-t $tag "; done) \ @@ -211,7 +221,7 @@ exec_cmd "docker build \ # In podman, we have to push the tags after building $is_podman && $push && for tag in "${tags[@]}"; do - exec_cmd "docker push $tag" + exec_cmd "podman push $tag" done # Stop builder when done (not needed for podman) From 4f32c29a2a6654812606c789f44ba408fc13bd63 Mon Sep 17 00:00:00 2001 From: Josh Curtiss Date: Tue, 19 Aug 2025 17:23:13 -0500 Subject: [PATCH 4/7] feat: Improve installer to be ephemeral Instead of cloning the project and keeping it around, the project is adjusted to be ephemeral, being downloaded to a temp directory just for the installation process. All pertinent yaml or scripts are installed. That said, a dev mode is supported, which will continue the pattern of using symlinks, which is useful for development. Also, improved the installer to not require running as root or with sudo. It will look at the OS flavor and run sudo if necessary. For instance, macOS should not use sudo, but other Linux flavors should. As a result of the entire project not being saved on the system, installation is adjusted to save needed scripts/configurations elsewhere: - Stacks are saved to `/etc/stackname` (or in non-root installations like macOS, they are saved to `$HOME/.stackname`), like /etc/nginxproxymanager. If installing in dev mode, it will bring up the stack using the yaml files right in the repo. - Reusable scripts like `deploy` and `publish` are saved in /usr/bin for Linux flavors and /usr/local/bin for macOS. If installing in dev mode, it will symlink the scripts. Otherwise, it will run `install` to copy the scripts to the bin directory. --- init.sh | 182 +++++++++++++++++--------- macos/docker.sh | 17 ++- rhel-9/docker.sh | 24 ++-- stacks/nginx-proxy-manager-install.sh | 36 +++-- stacks/nginx-proxy-manager.yml | 20 +-- stacks/portainer-install.sh | 14 +- ubuntu-22/docker.sh | 19 ++- 7 files changed, 204 insertions(+), 108 deletions(-) diff --git a/init.sh b/init.sh index 0fb48a5..273a9e7 100755 --- a/init.sh +++ b/init.sh @@ -1,8 +1,18 @@ #!/bin/bash -# Params +version=1.0.0 +bold=$(tput bold) +red=$(tput setaf 1) +yellow=$(tput setaf 3) +ul=$(tput smul) +rmul=$(tput rmul) +norm=$(tput sgr0) branch=main -[[ $1 != -* && -n $1 ]] && branch=$1 +dev=false +help_only=false +version_only=false +stacks_only=false +runner_only=false # Asks a yes/no question and returns 0 for 'yes' and 1 for 'no'. If the user does not # provide a response, it uses the default value. @@ -25,70 +35,105 @@ function yorn() { done } -bold=$(tput bold) -ul=$(tput smul) -norm=$(tput sgr0) -echo "$bold - ___ __ __ __ __ - / _ \ ___ ____ / /__ ___ ____ / // /___ ___ / /_ - / // // _ \/ __// '_// -_)/ __/ / _ // _ \ (_-&2 ;; + *) echo "${red}Some of these options are invalid:$norm $*" >&2; exit 2 ;; + esac +done +shift $((OPTIND - 1)) + +# Prerequisite commands +for cmd in curl cut find gzip install mktemp sort tar tr; do + if ! which "$cmd" > /dev/null; then + echo "${bold}${red}This installer requires $ul$cmd$rmul to work.$norm" >&2 + exit 1 + fi done -# Determine if git needs to be installed. On macOS, we check if developer tools are installed. On -# all other platforms, we just check if `git --version` is successful. -NEED_GIT=true -if [ "$(uname)" = "Darwin" ]; then - xcode-select --print-path &> /dev/null && NEED_GIT=false +# Load installer files into a temp directory +export dev +if $dev; then + dir=$(dirname "$(realpath "$0")") + echo "Will install from $dir in dev mode..." else - git --version &> /dev/null && NEED_GIT=false + echo "Downloading installation files from $branch branch..." + dir=$(mktemp -d -t uicpharm-docker-host-XXXXXX) + url=https://github.com/uicpharm/docker-host/archive/refs/heads/$branch.tar.gz + curl -fsL "$url" | tar xz --strip-components=1 -C "$dir" fi -if $NEED_GIT; then - if yorn 'We have to install git or we cannot proceed. Is that okay?' 'y'; then - # Install git with apt or yum - if [ -n "$(command -v dnf)" ]; then - dnf install -y git || exit 1 - elif [ -n "$(command -v yum)" ]; then - yum install -y git || exit 1 - elif [ -n "$(command -v xcode-select)" ]; then - xcode-select --install - # Wait until the interactive install is done - echo -n "Follow the GUI installer. Waiting for installation to finish" - until xcode-select --print-path &> /dev/null; do echo -n '.'; sleep 5; done - echo '\nGreat, developer tools are installed!' - elif [ -n "$(command -v apt)" ]; then - apt update -y && apt install -y git || exit 1 - else - echo "Could not determine application repository. Supports apt, dnf, yum, and xcode-select." - exit 1 - fi - else - echo 'Ok, we must abort then.' - exit - fi +# Help/version options only display their info and exit +if $help_only; then + display_help + exit +elif $version_only; then + display_version + exit fi -# Clone the project if the dir doesn't exist -REPO_DIR="$BASEDIR/docker-host" -REPO_URL="https://github.com/uicpharm/docker-host.git" -mkdir -p "$BASEDIR" || exit 1 -[ ! -d "$REPO_DIR" ] && git clone -b "$branch" "$REPO_URL" "$REPO_DIR" -[[ -d $REPO_DIR ]] && echo "The docker-host project is installed at $ul$REPO_DIR$norm." -cd "$REPO_DIR" || exit 1 +display_title +# Warn that we will ask for sudo password. +if [[ $EUID -ne 0 ]]; then + echo "Part of the installation requires 'sudo'. You may be asked for a sudo password." + echo +fi # Calculate the default flavor based on what we see on the system default_flavor='' @@ -103,7 +148,7 @@ elif [[ -f /etc/os-release ]]; then fi # Available flavor installers -flavors=$(find . -mindepth 1 -maxdepth 1 \( -type d -o -type l \) ! -name 'exp' ! -name 'node_modules' ! -name 's*' ! -name '.*' | cut -d'/' -f2 | sort) +flavors=$(find "$dir" -mindepth 1 -maxdepth 1 \( -type d -o -type l \) ! -name 'exp' ! -name 'node_modules' ! -name 's*' ! -name '.*' | awk -F'/' '{print $NF}' | sort) [[ ! $flavors =~ $default_flavor ]] && default_flavor='' if [[ -n $default_flavor ]] && yorn "It looks like you're running on $ul$default_flavor$norm, is that right?" y; then @@ -118,7 +163,20 @@ fi if [[ ! $flavors =~ $flavor ]]; then echo "Aborting because $ul$flavor$norm is not a valid choice." + exit 1 +fi + +# All flavors should run as sudo, except macOS. +cmd=(env) && [[ $flavor != macos ]] && cmd=(sudo env) +cmd+=(DEV="$dev") +if $stacks_only; then + cd "$dir/shared" || exit 1 + cmd+=(./stacks.sh) +elif $runner_only; then + cd "$dir/shared" || exit 1 + cmd+=(./github-actions-runner.sh) else - cd "$flavor" || exit 1 - ./setup.sh + cd "$dir/$flavor" || exit 1 + cmd+=(./setup.sh) fi +"${cmd[@]}" diff --git a/macos/docker.sh b/macos/docker.sh index 2f8759e..80715f7 100755 --- a/macos/docker.sh +++ b/macos/docker.sh @@ -1,6 +1,7 @@ #!/bin/bash scr_dir=$(realpath "${0%/*}") +[[ -z $DEV ]] && DEV=false SCRIPT_TITLE="Install Docker Desktop" if [[ " $* " == *" --title "* ]]; then echo "$SCRIPT_TITLE"; exit 0; fi @@ -21,12 +22,16 @@ echo # Add scripts to /usr/local/bin so it will be in the path # (On macOS, we must use this one because /usr/bin is not permitted) -if [ -d '/usr/local/bin' ]; then - bin_dir='/usr/local/bin' -fi -if [ -n "$bin_dir" ]; then - echo "Installing scripts to $bin_dir may require a password." +bin_dir=/usr/local/bin +sudo install -d $bin_dir +if [[ -d $bin_dir ]]; then + echo "Installing scripts to $(tput smul)$bin_dir$(tput rmul) (may require a password):" for scr_name in "$scr_dir"/../shared/bin/*.sh; do - sudo ln -f -s "$(realpath "$scr_name")" "$bin_dir/$(basename "$scr_name" .sh)" + cmd=(sudo install -b -v) && $DEV && cmd=(sudo ln -f -v -s) + cmd+=("$(realpath "$scr_name")") + cmd+=("$bin_dir/$(basename "$scr_name" .sh)") + "${cmd[@]}" done +else + echo "$(tput setaf 1)Cannot install scripts to $(tput smul)$bin_dir$(tput rmul) because it wasn't found.$(tput sgr0)" >&2 fi diff --git a/rhel-9/docker.sh b/rhel-9/docker.sh index 501a25b..df563e4 100755 --- a/rhel-9/docker.sh +++ b/rhel-9/docker.sh @@ -1,6 +1,7 @@ #!/bin/bash scr_dir=$(realpath "${0%/*}") +[[ -z $DEV ]] && DEV=false SCRIPT_TITLE="Install Docker/Podman" if [[ " $* " == *" --title "* ]]; then echo "$SCRIPT_TITLE"; exit 0; fi @@ -11,18 +12,21 @@ sleep 2 dnf install -y python-dotenv container-tools podman-compose # Add scripts to /usr/bin so it will be in the path -if [ -d '/usr/bin' ]; then - bin_dir='/usr/bin' -elif [ -d '/usr/local/bin' ]; then - bin_dir='/usr/local/bin' +if [[ -d /usr/bin ]]; then + bin_dir=/usr/bin +elif [[ -d /usr/local/bin ]]; then + bin_dir=/usr/local/bin fi -if [ -n "$bin_dir" ]; then - for scr_name in "$scr_dir"/../shared/bin/*.sh; do - ln -f -s "$(realpath "$scr_name")" "$bin_dir/$(basename "$scr_name" .sh)" - done - for scr_name in "$scr_dir"/bin/*.sh; do - ln -f -s "$(realpath "$scr_name")" "$bin_dir/$(basename "$scr_name" .sh)" +if [[ -n $bin_dir ]]; then + echo "Installing scripts to $(tput smul)$bin_dir$(tput rmul) (may require a password):" + for scr_name in "$scr_dir"/../shared/bin/*.sh "$scr_dir"/bin/*.sh; do + cmd=(install -b -v) && $DEV && cmd=(ln -f -v -s) + cmd+=("$(realpath "$scr_name")") + cmd+=("$bin_dir/$(basename "$scr_name" .sh)") + "${cmd[@]}" done +else + echo "$(tput setaf 1)Cannot install scripts to $(tput smul)$bin_dir$(tput rmul) because it wasn't found.$(tput sgr0)" >&2 fi # Silence Docker emulation messages diff --git a/stacks/nginx-proxy-manager-install.sh b/stacks/nginx-proxy-manager-install.sh index e4a6772..1bce4a1 100755 --- a/stacks/nginx-proxy-manager-install.sh +++ b/stacks/nginx-proxy-manager-install.sh @@ -7,24 +7,36 @@ echo "$(tput bold) # $(tput sgr0)" sleep 2 -scr_dir="${0%/*}" -sec_dir="$scr_dir/../secrets" -yml_file="$scr_dir/nginx-proxy-manager.yml" +scr_dir=$(realpath "$(dirname "$0")") +[[ -z $DEV ]] && DEV=false + +# Determine target directory and install +target_dir=/etc/nginxproxymanager +[[ "$(uname)" == "Darwin" || $EUID -ne 0 ]] && target_dir=$HOME/.nginxproxymanager +$DEV && target_dir=$scr_dir +sec_dir="$target_dir/secrets" +yml_file="$target_dir/nginx-proxy-manager.yml" +if ! $DEV; then + install -d "$sec_dir" + install -b "$scr_dir/nginx-proxy-manager.yml" "$yml_file" +fi [[ $* == -u || $* == --upgrade ]] && upgrade_args=(--pull always) -mkdir -p "$sec_dir" # Set up common "frontend" network shared among containers [ -z "$(docker network ls -qf name=frontend)" ] && docker network create frontend # Secrets for the passwords -[ ! -f "$sec_dir/nginxproxymanager_db_password.txt" ] && openssl rand -hex 32 > "$sec_dir/nginxproxymanager_db_password.txt" -[ ! -f "$sec_dir/nginxproxymanager_db_root_password.txt" ] && openssl rand -hex 32 > "$sec_dir/nginxproxymanager_db_root_password.txt" +[ ! -f "$sec_dir/db_password.txt" ] && openssl rand -hex 32 > "$sec_dir/db_password.txt" +[ ! -f "$sec_dir/db_root_password.txt" ] && openssl rand -hex 32 > "$sec_dir/db_root_password.txt" # If we're using podman, create the pod and include the podman arguments svc_name="nginxproxymanager" -if [[ $(docker --version) == podman* ]]; then - podman pod create --name "$svc_name" - podman-compose --in-pod "$svc_name" --podman-run-args "${upgrade_args[*]}" -f "$yml_file" up -d -else - docker compose -f "$yml_file" up -d "${upgrade_args[@]}" -fi +( + cd "$target_dir" || exit 1 + if [[ $(podman --version 2>/dev/null) == podman* ]]; then + podman pod create --name "$svc_name" + podman-compose --in-pod "$svc_name" --podman-run-args "${upgrade_args[*]}" -f "$yml_file" up -d + else + docker compose -f "$yml_file" up -d "${upgrade_args[@]}" + fi +) # If we're using podman and `podman-install-service` is available, create the systemd service command -v podman-install-service &> /dev/null && podman-install-service "$svc_name" echo Done installing Nginx Proxy Manager! diff --git a/stacks/nginx-proxy-manager.yml b/stacks/nginx-proxy-manager.yml index 2797d38..a8b405f 100644 --- a/stacks/nginx-proxy-manager.yml +++ b/stacks/nginx-proxy-manager.yml @@ -6,10 +6,10 @@ networks: backend: secrets: - nginxproxymanager_db_password: - file: ../secrets/nginxproxymanager_db_password.txt - nginxproxymanager_db_root_password: - file: ../secrets/nginxproxymanager_db_root_password.txt + db_password: + file: ./secrets/db_password.txt + db_root_password: + file: ./secrets/db_root_password.txt services: @@ -27,13 +27,13 @@ services: DB_MYSQL_HOST: 'db' DB_MYSQL_PORT: 3306 DB_MYSQL_USER: 'npm' - DB_MYSQL_PASSWORD__FILE: /run/secrets/nginxproxymanager_db_password + DB_MYSQL_PASSWORD__FILE: /run/secrets/db_password DB_MYSQL_NAME: 'npm' volumes: - 'data:/data' - 'letsencrypt:/etc/letsencrypt' secrets: - - nginxproxymanager_db_password + - db_password networks: - frontend - backend @@ -43,15 +43,15 @@ services: privileged: true restart: unless-stopped environment: - MYSQL_ROOT_PASSWORD__FILE: /run/secrets/nginxproxymanager_db_root_password + MYSQL_ROOT_PASSWORD__FILE: /run/secrets/db_root_password MYSQL_DATABASE: 'npm' MYSQL_USER: 'npm' - MYSQL_PASSWORD__FILE: /run/secrets/nginxproxymanager_db_password + MYSQL_PASSWORD__FILE: /run/secrets/db_password volumes: - 'db:/var/lib/mysql' secrets: - - nginxproxymanager_db_password - - nginxproxymanager_db_root_password + - db_password + - db_root_password networks: - backend diff --git a/stacks/portainer-install.sh b/stacks/portainer-install.sh index ce07a71..8eda52a 100755 --- a/stacks/portainer-install.sh +++ b/stacks/portainer-install.sh @@ -7,8 +7,18 @@ echo "$(tput bold) # $(tput sgr0)" sleep 2 -scr_dir="${0%/*}" -yml_file="$scr_dir/portainer.yml" +scr_dir=$(realpath "$(dirname "$0")") +[[ -z $DEV ]] && DEV=false + +# Determine target directory and install +target_dir=/etc/portainer +[[ "$(uname)" == "Darwin" || $EUID -ne 0 ]] && target_dir=$HOME/.portainer +$DEV && target_dir=$scr_dir +yml_file="$target_dir/portainer.yml" +if ! $DEV; then + install -d "$target_dir" + install -b "$scr_dir/portainer.yml" "$yml_file" +fi [[ $* == -u || $* == --upgrade ]] && upgrade_args=(--pull always) # Start the stack svc_name="portainer" diff --git a/ubuntu-22/docker.sh b/ubuntu-22/docker.sh index 810f01e..cdfd35f 100755 --- a/ubuntu-22/docker.sh +++ b/ubuntu-22/docker.sh @@ -1,6 +1,7 @@ #!/bin/bash scr_dir=$(realpath "${0%/*}") +[[ -z $DEV ]] && DEV=false SCRIPT_TITLE="Install Docker" if [[ " $* " == *" --title "* ]]; then echo "$SCRIPT_TITLE"; exit 0; fi @@ -22,15 +23,21 @@ apt-get update apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # Add scripts to /usr/bin so it will be in the path -if [ -d '/usr/bin' ]; then - bin_dir='/usr/bin' -elif [ -d '/usr/local/bin' ]; then - bin_dir='/usr/local/bin' +if [[ -d /usr/bin ]]; then + bin_dir=/usr/bin +elif [[ -d /usr/local/bin ]]; then + bin_dir=/usr/local/bin fi -if [ -n "$bin_dir" ]; then +if [[ -n $bin_dir ]]; then + echo "Installing scripts to $(tput smul)$bin_dir$(tput rmul) (may require a password):" for scr_name in "$scr_dir"/../shared/bin/*.sh; do - ln -f -s "$(realpath "$scr_name")" "$bin_dir/$(basename "$scr_name" .sh)" + cmd=(install -b -v) && $DEV && cmd=(ln -f -v -s) + cmd+=("$(realpath "$scr_name")") + cmd+=("$bin_dir/$(basename "$scr_name" .sh)") + "${cmd[@]}" done +else + echo "$(tput setaf 1)Cannot install scripts to $(tput smul)$bin_dir$(tput rmul) because it wasn't found.$(tput sgr0)" >&2 fi echo Done installing Docker! From 51b32ceaa17be0427b0a2bf479c374638ababa4f Mon Sep 17 00:00:00 2001 From: Josh Curtiss Date: Mon, 18 Aug 2025 20:52:19 -0500 Subject: [PATCH 5/7] chore: Remove the docker-compose shim for podman-compose --- rhel-9/bin/docker-compose.sh | 2 -- 1 file changed, 2 deletions(-) delete mode 100755 rhel-9/bin/docker-compose.sh diff --git a/rhel-9/bin/docker-compose.sh b/rhel-9/bin/docker-compose.sh deleted file mode 100755 index a52c86b..0000000 --- a/rhel-9/bin/docker-compose.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/sh -exec /usr/bin/podman-compose "$@" From d8b402ab3384c4afd5af19b3864228a6a6f54651 Mon Sep 17 00:00:00 2001 From: Josh Curtiss Date: Thu, 21 Aug 2025 12:17:01 -0500 Subject: [PATCH 6/7] chore: Remove "secrets" directory permission setting --- stacks/setup.sh | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/stacks/setup.sh b/stacks/setup.sh index 323d892..6508977 100755 --- a/stacks/setup.sh +++ b/stacks/setup.sh @@ -2,43 +2,6 @@ scr_dir="${0%/*}" -# Collect user groups. We do this in the background right away since a server connected to AD may take -# several seconds to respond with the group list. -real_user=${SUDO_USER:-$(whoami)} -groups_file=$(mktemp) -(id "$real_user" | cut -d'=' -f4 | tr ',' '\n' > "$groups_file") & -groups_pid="$!" - -# Create secrets directory -sec_dir=$(realpath "$scr_dir/..")/secrets -mkdir -p "$sec_dir" - -# Set strict permissions for the secrets -echo "It is highly recommended that you restrict permissions of the $(tput smul)$(basename "$sec_dir")$(tput rmul) directory." -echo "I can set the group to something your $real_user user also has access to." -read -r -p "Do you want to do this? [Y/n] " yorn -if [[ ${yorn:-y} =~ [Yy] ]]; then - echo -n "Great! Finding your user groups." - while ps "$groups_pid" > /dev/null; do echo -n '.'; sleep 1; done - echo -e '\n' - while IFS=$'\n' read -r line; do - group_array+=("$line") - done <<< "$(<"$groups_file")" - PS3="Either enter a number or manually type a group name: " - select group in "${group_array[@]}"; do - # Accept either the selected group or the manually-entered group - group=${group:-$REPLY} - # Don't accept a blank entry - [[ -n $group ]] && break - done - echo "Setting the group of the $(tput smul)$(basename "$sec_dir")$(tput rmul) directory to $(tput smul)$group$(tput rmul)." - # Groups will look like "123(group_name)", so we get just the number by looking to the left of '('. - # This will also work with manually entered groups like "group_name" or "123". - group=$(echo "$group" | cut -d'(' -f1) - chgrp -R "$group" "$sec_dir" - chmod -R 770 "$sec_dir" -fi - # Install individual stacks for stackinstaller in "$scr_dir"/*-install.sh; do stackname="$(basename "$stackinstaller" | sed -e "s/-install.sh//")" From 5288e3d04473853367dbecaeef4037a3a51e8391 Mon Sep 17 00:00:00 2001 From: Josh Curtiss Date: Tue, 26 Aug 2025 10:30:37 -0500 Subject: [PATCH 7/7] docs: Update installation instructions --- README.md | 17 +++-------------- stacks/README.md | 20 ++++++++++++-------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 664a96f..8b8de5a 100644 --- a/README.md +++ b/README.md @@ -12,21 +12,10 @@ support by virtue of support for Ubuntu. Actual Docker installation is handled b installation script, but you can use the stacks installer directly on any *nix machine by running [stacks/setup.sh](./stacks/setup.sh). -Login as root and execute: +To begin a standard installation: -```sh -source <(curl -H 'Cache-Control: no-cache, no-store' -o- https://raw.githubusercontent.com/uicpharm/docker-host/main/init.sh) -``` - -The script will download the project and walk you through executing the scripts. - -If you cannot login as root and can only sudo, then download it to your home directory and -execute it from there: - -```sh -curl -H 'Cache-Control: no-cache, no-store' -o- https://raw.githubusercontent.com/uicpharm/docker-host/main/init.sh > init.sh && \ -chmod +x init.sh && \ -sudo ./init.sh +```bash +bash <(curl -H 'Cache-Control: no-cache, no-store' -o- https://raw.githubusercontent.com/uicpharm/docker-host/main/init.sh) ``` ## FAQs diff --git a/stacks/README.md b/stacks/README.md index afa9df5..6ee6138 100644 --- a/stacks/README.md +++ b/stacks/README.md @@ -21,23 +21,27 @@ Docker environment, making it easy to monitor and configure your Docker services ### How do I install the stacks without running the whole setup? -You can run `./stacks/setup.sh` to just install the stacks, or run an individual stack's -install script directly, such as `./stacks/nginx-proxy-manager-install.sh`. +You can run the installer and specify just to install the stacks, like this: -### How do I update the stacks with the latest version? +```bash +bash <(curl -H 'Cache-Control: no-cache, no-store' -o- https://raw.githubusercontent.com/uicpharm/docker-host/main/init.sh) --stacks-only +``` -First, ensure your `docker-host` instance is using the latest code by navigating to its -directory and running `git pull`. +If you have downloaded or cloned the project, you can also run `./stacks/setup.sh` +directly, to just install the stacks, or run an individual stack's install script +directly, such as `./stacks/nginx-proxy-manager-install.sh`. + +### How do I update the stacks with the latest version? -For minor version bumps, you can stop the stack and reinstall it with the installer script -and the `--upgrade` flag. This is not destructive; it will create containers using the +To do this, you must download or clone the project and run the stack's installer directly, +and add the `--upgrade` flag. This is not destructive; it will create containers using the same data/volumes you had already set up. #### Example: Stopping the stack ```bash # On a Docker host, just use docker compose: -docker compose -f stacks/nginx-proxy-manager.yml down +docker compose -f /etc/nginxproxymanager/nginx-proxy-manager.yml down # On a Podman host, you should use systemctl: systemctl stop nginxproxymanager ```