diff --git a/.copier-docker-config.yaml b/.copier-docker-config.yaml index 8d07b0f..997f9b6 100644 --- a/.copier-docker-config.yaml +++ b/.copier-docker-config.yaml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier; do NOT edit manually -_commit: v0.30.0-6-gbd443ac +_commit: v0.30.6-10-g90bb37e _src_path: . app_dirname: entelecheia app_install_root: /opt @@ -73,6 +73,7 @@ use_deploy_workflows: true use_jupyter: true use_semantic_versioning_for_image: true use_ssh_service: true +use_tty: false use_web_service: true web_service_host_port: 19090 web_service_port: 8080 diff --git a/.copier-template/.docker/scripts/`{{ launch_scripts }}`.jinja b/.copier-template/.docker/scripts/`{{ launch_scripts }}`.jinja index 896fc7f..2462263 100644 --- a/.copier-template/.docker/scripts/`{{ launch_scripts }}`.jinja +++ b/.copier-template/.docker/scripts/`{{ launch_scripts }}`.jinja @@ -35,14 +35,15 @@ fi # set token to value of JUPYTER_TOKEN # set port to value of JUPYTER_DOCKER_PORT if [[ -z "$(command -v jupyter)" ]]; then - echo "Jupyter not installed. Exiting..." - exit 1 + echo "Jupyter not installed." +else + echo "Starting Jupyter Lab..." + jupyter lab \ + --no-browser \ + --notebook-dir="$WORKSPACE_ROOT" \ + --ServerApp.token="$JUPYTER_TOKEN" \ + --port="$JUPYTER_PORT" \ + --ip=0.0.0.0 \ + --allow-root fi -{% if not install_dotfiles %}exec gosu "${USER}" {% endif %}jupyter lab \ - --no-browser \ - --notebook-dir="$WORKSPACE_ROOT" \ - --ServerApp.token="$JUPYTER_TOKEN" \ - --port="$JUPYTER_PORT" \ - --ip=0.0.0.0 \ - --allow-root {%- endif %} diff --git a/.copier-template/.docker/scripts/entrypoint.sh.jinja b/.copier-template/.docker/scripts/entrypoint.sh.jinja index 2d8a848..db875b6 100644 --- a/.copier-template/.docker/scripts/entrypoint.sh.jinja +++ b/.copier-template/.docker/scripts/entrypoint.sh.jinja @@ -31,7 +31,7 @@ if [ "$USER_UID" != "$LOCAL_UID" ]; then echo "Changing ownership of $APP_INSTALL_ROOT directory to $USER_UID:$USER_UID" chown -R "$USER_UID:$USER_UID" "$APP_INSTALL_ROOT" fi - if [ -n "$WORKSPACE_ROOT" ] && [ -d "$WORKSPACE_ROOT" ]; then + if [ -n "$WORKSPACE_ROOT" ] && [ -d "$WORKSPACE_ROOT" ] && [ "$WORKSPACE_ROOT" != "$APP_INSTALL_ROOT" ]; then echo "Changing ownership of workspace directory [$WORKSPACE_ROOT] to $USER_UID:$USER_UID" chown -R "$USER_UID:$USER_UID" "$WORKSPACE_ROOT" fi diff --git a/.copier-template/.docker/{% if build_images_from_dockerfile %}docker-compose.`{{ docker_image_variant_name }}`.yaml{% endif %}.jinja b/.copier-template/.docker/{% if build_images_from_dockerfile %}docker-compose.`{{ docker_image_variant_name }}`.yaml{% endif %}.jinja index 55c7d7c..b4d1be4 100644 --- a/.copier-template/.docker/{% if build_images_from_dockerfile %}docker-compose.`{{ docker_image_variant_name }}`.yaml{% endif %}.jinja +++ b/.copier-template/.docker/{% if build_images_from_dockerfile %}docker-compose.`{{ docker_image_variant_name }}`.yaml{% endif %}.jinja @@ -28,10 +28,12 @@ services: image: $IMAGE_NAME:$IMAGE_TAG # Sets the hostname of the container hostname: $CONTAINER_HOSTNAME - command: - # Specifies the command to be executed when the container is run - - bash - - $CONTAINER_LAUNCH_SCRIPT + # Sets tty to true if the container needs a pseudo-TTY + tty: `{{ use_tty | lower }}` + {% if use_tty %}# {% endif %}command: + {% if use_tty %}# {% endif %} # Specifies the command to be executed when the container is run + {% if use_tty %}# {% endif %} - bash + {% if use_tty %}# {% endif %} - $CONTAINER_LAUNCH_SCRIPT # set the environment variables environment: USER_UID: $CONTAINER_USER_UID diff --git a/.copier-template/.github/workflows/{% if use_deploy_workflows %}deploy-`{{ docker_image_variant_name }}`-image.yaml{% endif %}.jinja b/.copier-template/.github/workflows/{% if use_deploy_workflows %}deploy-`{{ docker_image_variant_name }}`-image.yaml{% endif %}.jinja index 20f342e..a3e739c 100644 --- a/.copier-template/.github/workflows/{% if use_deploy_workflows %}deploy-`{{ docker_image_variant_name }}`-image.yaml{% endif %}.jinja +++ b/.copier-template/.github/workflows/{% if use_deploy_workflows %}deploy-`{{ docker_image_variant_name }}`-image.yaml{% endif %}.jinja @@ -73,7 +73,7 @@ jobs: # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository. # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. - name: Build and push Docker image - uses: docker/build-push-action@v6.1.0 + uses: docker/build-push-action@v6.3.0 with: context: . file: ./.docker/Dockerfile.`{{ docker_image_variant_name }}` diff --git a/.docker/docker-compose.base.yaml b/.docker/docker-compose.base.yaml index da4958f..658bfb1 100644 --- a/.docker/docker-compose.base.yaml +++ b/.docker/docker-compose.base.yaml @@ -26,6 +26,8 @@ services: image: $IMAGE_NAME:$IMAGE_TAG # Sets the hostname of the container hostname: $CONTAINER_HOSTNAME + # Sets tty to true if the container needs a pseudo-TTY + tty: false command: # Specifies the command to be executed when the container is run - bash diff --git a/.docker/scripts/entrypoint.sh b/.docker/scripts/entrypoint.sh index efa65b2..f448c17 100644 --- a/.docker/scripts/entrypoint.sh +++ b/.docker/scripts/entrypoint.sh @@ -31,7 +31,7 @@ if [ "$USER_UID" != "$LOCAL_UID" ]; then echo "Changing ownership of $APP_INSTALL_ROOT directory to $USER_UID:$USER_UID" chown -R "$USER_UID:$USER_UID" "$APP_INSTALL_ROOT" fi - if [ -n "$WORKSPACE_ROOT" ] && [ -d "$WORKSPACE_ROOT" ]; then + if [ -n "$WORKSPACE_ROOT" ] && [ -d "$WORKSPACE_ROOT" ] && [ "$WORKSPACE_ROOT" != "$APP_INSTALL_ROOT" ]; then echo "Changing ownership of workspace directory [$WORKSPACE_ROOT] to $USER_UID:$USER_UID" chown -R "$USER_UID:$USER_UID" "$WORKSPACE_ROOT" fi diff --git a/.docker/scripts/launch.sh b/.docker/scripts/launch.sh index 346cf43..302f060 100644 --- a/.docker/scripts/launch.sh +++ b/.docker/scripts/launch.sh @@ -19,13 +19,14 @@ sudo service ssh start # set token to value of JUPYTER_TOKEN # set port to value of JUPYTER_DOCKER_PORT if [[ -z "$(command -v jupyter)" ]]; then - echo "Jupyter not installed. Exiting..." - exit 1 + echo "Jupyter not installed." +else + echo "Starting Jupyter Lab..." + jupyter lab \ + --no-browser \ + --notebook-dir="$WORKSPACE_ROOT" \ + --ServerApp.token="$JUPYTER_TOKEN" \ + --port="$JUPYTER_PORT" \ + --ip=0.0.0.0 \ + --allow-root fi -jupyter lab \ - --no-browser \ - --notebook-dir="$WORKSPACE_ROOT" \ - --ServerApp.token="$JUPYTER_TOKEN" \ - --port="$JUPYTER_PORT" \ - --ip=0.0.0.0 \ - --allow-root diff --git a/.github/workflows/deploy-base-image.yaml b/.github/workflows/deploy-base-image.yaml index 50d563c..7a15b56 100644 --- a/.github/workflows/deploy-base-image.yaml +++ b/.github/workflows/deploy-base-image.yaml @@ -73,7 +73,7 @@ jobs: # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository. # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. - name: Build and push Docker image - uses: docker/build-push-action@v6.1.0 + uses: docker/build-push-action@v6.3.0 with: context: . file: ./.docker/Dockerfile.base diff --git a/copier.yaml b/copier.yaml index 4a77e83..5783708 100644 --- a/copier.yaml +++ b/copier.yaml @@ -208,6 +208,11 @@ cuda_device_id: type: str help: "What is your CUDA device to use? (default: all)" +use_tty: + default: false + type: bool + help: Do you want to enable tty (pseudo-terminal) when running the Docker container? + use_ssh_service: default: true type: bool diff --git a/tmp/.copier-docker-config.yaml b/tmp/.copier-docker-config.yaml index 4abd2f9..d022a0f 100644 --- a/tmp/.copier-docker-config.yaml +++ b/tmp/.copier-docker-config.yaml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier; do NOT edit manually -_commit: v0.29.2-4-g59101f8 +_commit: v0.30.6-4-gd74e456 _src_path: . app_dirname: entelecheia app_install_root: /workspace/projects @@ -73,6 +73,7 @@ use_deploy_workflows: true use_jupyter: true use_semantic_versioning_for_image: true use_ssh_service: true +use_tty: false use_web_service: true web_service_host_port: 18080 web_service_port: 8080 diff --git a/tmp/.docker/.docker-scripts/docker-compose.sh b/tmp/.docker/.docker-scripts/docker-compose.sh index 6036a7e..741396d 100644 --- a/tmp/.docker/.docker-scripts/docker-compose.sh +++ b/tmp/.docker/.docker-scripts/docker-compose.sh @@ -150,6 +150,7 @@ fi set +a # prepare docker network +CONTAINER_NETWORK_NAME=${CONTAINER_NETWORK_NAME:-""} if [[ -n "${CONTAINER_NETWORK_NAME}" ]] && ! docker network ls | grep -q "${CONTAINER_NETWORK_NAME}"; then echo "Creating network ${CONTAINER_NETWORK_NAME}" docker network create "${CONTAINER_NETWORK_NAME}" @@ -159,11 +160,16 @@ fi # prepare local workspace to be mounted echo "Preparing local workspace directories" -[ ! -d "${HOST_WORKSPACE_ROOT}" ] && mkdir -p "${HOST_WORKSPACE_ROOT}" -[ ! -d "${HOST_SCRIPTS_DIR}" ] && cp -r "$PWD/.docker/scripts" "${HOST_SCRIPTS_DIR}" -[ ! -d "${HOST_SSH_DIR}" ] && mkdir -p "${HOST_SSH_DIR}" -[ ! -d "${HOST_CACHE_DIR}" ] && mkdir -p "${HOST_CACHE_DIR}" -[ ! -d "${HOST_HF_HOME}" ] && mkdir -p "${HOST_HF_HOME}" +HOST_WORKSPACE_ROOT="${HOST_WORKSPACE_ROOT:-}" +[ -n "${HOST_WORKSPACE_ROOT}" ] && [ ! -d "${HOST_WORKSPACE_ROOT}" ] && mkdir -p "${HOST_WORKSPACE_ROOT}" +HOST_SCRIPTS_DIR="${HOST_SCRIPTS_DIR:-}" +[ -n "${HOST_SCRIPTS_DIR}" ] && [ ! -d "${HOST_SCRIPTS_DIR}" ] && cp -r "$PWD/.docker/scripts" "${HOST_SCRIPTS_DIR}" +HOST_SSH_DIR="${HOST_SSH_DIR:-}" +[ -n "${HOST_SSH_DIR}" ] && [ ! -d "${HOST_SSH_DIR}" ] && mkdir -p "${HOST_SSH_DIR}" +HOST_CACHE_DIR="${HOST_CACHE_DIR:-}" +[ -n "${HOST_CACHE_DIR}" ] && [ ! -d "${HOST_CACHE_DIR}" ] && mkdir -p "${HOST_CACHE_DIR}" +HOST_HF_HOME="${HOST_HF_HOME:-}" +[ -n "${HOST_HF_HOME}" ] && [ ! -d "${HOST_HF_HOME}" ] && mkdir -p "${HOST_HF_HOME}" # run docker-compose if [ "${COMMAND}" == "push" ]; then diff --git a/tmp/.docker/Dockerfile.base b/tmp/.docker/Dockerfile.base index 84255fe..6c3c902 100644 --- a/tmp/.docker/Dockerfile.base +++ b/tmp/.docker/Dockerfile.base @@ -1,6 +1,5 @@ # Sets the base image for subsequent instructions -ARG ARG_BUILD_FROM="library/ubuntu:22.04" -FROM $ARG_BUILD_FROM AS builder +FROM library/ubuntu:22.04 AS builder # Sets labels for the image LABEL org.opencontainers.image.source="https://github.com/entelecheia/hyperfast-docker-template" @@ -11,7 +10,7 @@ LABEL org.opencontainers.image.licenses="MIT" ARG DEBIAN_FRONTEND=noninteractive # Updates the image and installs necessary packages RUN apt-get update --fix-missing \ - && apt-get install -y curl wget jq sudo gosu git build-essential \ + && apt-get install -y curl wget jq sudo gosu git build-essential software-properties-common \ locales locales-all fontconfig fonts-nanum \ tzdata openssh-server \ # Cleans up unnecessary packages to reduce image size @@ -57,20 +56,33 @@ ENV USER_UID $ARG_USER_UID ENV USER_GID $ARG_USER_GID # Creates a non-root user with sudo privileges -RUN groupadd --gid $USER_GID $USERNAME \ - && adduser --uid $USER_UID --gid $USER_GID --force-badname --disabled-password --gecos "" $USERNAME \ - && echo "$USERNAME:$USERNAME" | chpasswd \ - && adduser $USERNAME sudo \ - && echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \ - && echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/$USERNAME \ - && chmod 0440 /etc/sudoers.d/$USERNAME - +# check if user exists and if not, create user +RUN if id -u $USERNAME >/dev/null 2>&1; then \ + # if the current user's user id is different from the specified user id, change the user id of the current user to the specified user id + if [ "$USER_UID" -ne "$(id -u $USERNAME)" ]; then \ + usermod --uid $USER_UID $USERNAME; \ + chown --recursive $USER_UID:$USER_UID $WORKSPACE_ROOT; \ + chown --recursive $USER_UID:$USER_UID $APP_INSTALL_ROOT; \ + fi; \ + else \ + groupadd --gid $USER_GID $USERNAME && \ + adduser --uid $USER_UID --gid $USER_GID --force-badname --disabled-password --gecos "" $USERNAME && \ + echo "$USERNAME:$USERNAME" | chpasswd && \ + adduser $USERNAME sudo && \ + echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \ + echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/$USERNAME && \ + chmod 0440 /etc/sudoers.d/$USERNAME; \ + fi USER root -RUN chown --recursive "${USER_UID}:${USER_GID}" "${WORKSPACE_ROOT}" -RUN chown --recursive "${USER_UID}:${USER_GID}" "${APP_INSTALL_ROOT}" -USER $USERNAME +# Copies entrypoint script from host into the image +COPY ./.docker/scripts/entrypoint.sh /usr/local/bin/entrypoint.sh +# Changes the entrypoint script permissions to make it executable +RUN chmod +x /usr/local/bin/entrypoint.sh +# Sets the entrypoint script as the default command that will be executed when the container is run +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] + # Specifies the command that will be executed when the container is run CMD ["bash"] diff --git a/tmp/.docker/docker-compose.base.yaml b/tmp/.docker/docker-compose.base.yaml index 408af57..68ff63d 100644 --- a/tmp/.docker/docker-compose.base.yaml +++ b/tmp/.docker/docker-compose.base.yaml @@ -1,5 +1,3 @@ -version: "3" - services: # Defines a service name workspace: @@ -22,6 +20,8 @@ services: image: $IMAGE_NAME:$IMAGE_TAG # Sets the hostname of the container hostname: $CONTAINER_HOSTNAME + # Sets tty to true if the container needs a pseudo-TTY + tty: false command: # Specifies the command to be executed when the container is run - bash diff --git a/tmp/.docker/docker.common.env b/tmp/.docker/docker.common.env index 3251edf..8952315 100644 --- a/tmp/.docker/docker.common.env +++ b/tmp/.docker/docker.common.env @@ -33,7 +33,7 @@ DOCKER_PROJECT_ID=${DOCKER_PROJECT_ID:-"default"} DOCKER_USERNAME=${DOCKER_USERNAME:-"entelecheia"} # The username for Docker. IMAGE_VERSION=${APP_VERSION:-"1.0.0"} # The version of the Docker image. If not set, "1.0.0" will be used. CONTAINER_REGISTRY=${CONTAINER_REGISTRY:-"ghcr.io"} # The Docker registry to push the image to. -CONTAINER_USERNAME=${USERNAME:-"app"} # The username of the user. If not set, the current user's username will be used. +CONTAINER_USERNAME="app" # The username of the user. If not set, the current user's username will be used. CONTAINER_USER_UID=${USER_UID:-"9001"} # The user ID in the Docker container. CONTAINER_USER_GID=${USER_GID:-"9001"} # The group ID in the Docker container. CONTAINER_GITHUB_USERNAME=${GITHUB_USERNAME:-"entelecheia"} # The GitHub username of the project diff --git a/tmp/.docker/scripts/entrypoint.sh b/tmp/.docker/scripts/entrypoint.sh new file mode 100644 index 0000000..efa65b2 --- /dev/null +++ b/tmp/.docker/scripts/entrypoint.sh @@ -0,0 +1,41 @@ +#!/bin/bash +VERBOSE=${DOCKER_VERBOSE:-"false"} +# add your custom commands here that should be executed every time the docker container starts +if [ "$VERBOSE" = "true" ]; then + echo "Starting docker container..." + echo "---" + # Print out the environment variables + env + echo "---" + echo "Fixing permissions..." + echo "---" +fi + +### Set the USER_UID envvar to match your user. +# Ensures files created in the container are owned by you: +# docker run --rm -it -v /some/path:/invokeai -e USER_UID=$(id -u) +# Default UID: 1000 chosen due to popularity on Linux systems. Possibly 501 on MacOS. +USER_UID=${USER_UID:-"1000"} +USERNAME=${USERNAME:-"app"} +LOCAL_UID=$(id -u "$USERNAME") +WORKSPACE_ROOT=${WORKSPACE_ROOT:-""} +APP_INSTALL_ROOT=${APP_INSTALL_ROOT:-""} + +if [ "$USER_UID" != "$LOCAL_UID" ]; then + echo "Updating UID and GID to $USER_UID:$USER_UID from $LOCAL_UID:$LOCAL_UID" + usermod -u "$USER_UID" "$USERNAME" + groupmod -g "$USER_UID" "$USERNAME" + echo "Changing ownership of home directory to $USER_UID:$USER_UID" + chown -R "$USER_UID:$USER_UID" "/home/$USERNAME" + if [ -n "$APP_INSTALL_ROOT" ] && [ -d "$APP_INSTALL_ROOT" ]; then + echo "Changing ownership of $APP_INSTALL_ROOT directory to $USER_UID:$USER_UID" + chown -R "$USER_UID:$USER_UID" "$APP_INSTALL_ROOT" + fi + if [ -n "$WORKSPACE_ROOT" ] && [ -d "$WORKSPACE_ROOT" ]; then + echo "Changing ownership of workspace directory [$WORKSPACE_ROOT] to $USER_UID:$USER_UID" + chown -R "$USER_UID:$USER_UID" "$WORKSPACE_ROOT" + fi +fi + +# Run the provided command +exec gosu "${USERNAME}" "$@" diff --git a/tmp/.github/workflows/deploy-base-image.yaml b/tmp/.github/workflows/deploy-base-image.yaml index e71016c..7a15b56 100644 --- a/tmp/.github/workflows/deploy-base-image.yaml +++ b/tmp/.github/workflows/deploy-base-image.yaml @@ -9,7 +9,8 @@ on: branches: - docker* paths: - - ".docker/**" + - ".docker/**/*.base*" + - ".docker/docker.version" # Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds. env: @@ -66,17 +67,17 @@ jobs: type=raw,value=${{ env.IMAGE_VERSION }}-${{ env.IMAGE_VARIANT }} # set latest tag for docker branch type=raw,value=latest-${{ env.IMAGE_VARIANT }} + type=raw,value=latest # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages. # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository. # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. - name: Build and push Docker image - uses: docker/build-push-action@v6.1.0 + uses: docker/build-push-action@v6.3.0 with: context: . file: ./.docker/Dockerfile.base build-args: | - ARG_BUILD_FROM=${{ env.BUILD_FROM }} ARG_USERNAME=${{ env.CONTAINER_USERNAME }} ARG_USER_UID=${{ env.CONTAINER_USER_UID }} ARG_USER_GID=${{ env.CONTAINER_USER_GID }} diff --git a/tmp/pyproject.toml b/tmp/pyproject.toml index c2686a5..c9006a0 100644 --- a/tmp/pyproject.toml +++ b/tmp/pyproject.toml @@ -8,7 +8,7 @@ repository = "https://github.com/entelecheia/hyperfast-docker-template" readme = "README.md" [tool.poetry.dependencies] -python = ">=3.9.1,<3.12" +python = ">=3.9,<3.13" [tool.poetry.group.dev] optional = true