diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-container-template.yml similarity index 55% rename from .github/workflows/ci-docker.yml rename to .github/workflows/ci-container-template.yml index 616fe6d..739449f 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-container-template.yml @@ -1,15 +1,18 @@ -name: CI (docker) +name: Template for CI (container) on: - push: - branches: - - main - pull_request: - workflow_dispatch: + workflow_call: + inputs: + container_cmd: + required: true + type: string permissions: contents: read +env: + CONTAINER_CMD: ${{ inputs.container_cmd }} + jobs: build-and-run: name: Build and run @@ -25,9 +28,9 @@ jobs: run: make build - name: Run --help - run: docker run --rm nava-platform-cli --help + run: ${CONTAINER_CMD} run --rm nava-platform-cli --help - - name: Run e2e through docker-wrapper + - name: Run e2e through container-wrapper env: - CMD: ./bin/docker-wrapper + CMD: ./bin/container-wrapper run: make test-e2e diff --git a/.github/workflows/ci-container.yml b/.github/workflows/ci-container.yml new file mode 100644 index 0000000..081657a --- /dev/null +++ b/.github/workflows/ci-container.yml @@ -0,0 +1,22 @@ +name: CI (container) + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + ci: + strategy: + fail-fast: false + matrix: + container_cmd: [docker, podman] + + uses: ./.github/workflows/ci-container-template.yml + with: + container_cmd: ${{ matrix.container_cmd }} diff --git a/Dockerfile b/Containerfile similarity index 93% rename from Dockerfile rename to Containerfile index 8c01431..3fdfdd1 100644 --- a/Dockerfile +++ b/Containerfile @@ -2,7 +2,7 @@ # renovate: datasource=python-version depName=python ARG PYTHON_VERSION=3.12 -FROM ghcr.io/astral-sh/uv:python$PYTHON_VERSION-trixie-slim@sha256:36cdfbf910c8b0f651355c013e7ece9678f4ecbf030a9fd9e6779de421189805 +FROM ghcr.io/astral-sh/uv:python$PYTHON_VERSION-trixie-slim@sha256:36cdfbf910c8b0f651355c013e7ece9678f4ecbf030a9fd9e6779de421189805 # allow all users to get into "home", like git checking for a global ignore # file, until better user juggling in the future @@ -32,7 +32,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ ENV PATH="/app/.venv/bin:$PATH" -COPY bin/docker-entry /usr/local/bin +COPY bin/container-entry /usr/local/bin WORKDIR /project-dir -ENTRYPOINT ["docker-entry"] +ENTRYPOINT ["container-entry"] diff --git a/Makefile b/Makefile index 3dcf4ce..043d44e 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,8 @@ PY_SRCS := nava tests PY_RUN ?= uv run --frozen +CONTAINER_CMD ?= docker +export CONTAINER_CMD ifdef CI FMT_ARGS :=--check @@ -14,8 +16,8 @@ FMT_ARGS := LINT_ARGS :=--fix endif -build: ## Build docker image - docker build --tag $(PKG_NAME) . +build: ## Build container image + $(CONTAINER_CMD) build --tag $(PKG_NAME) --file Containerfile . check: ## Run checks check: check-static test test-e2e @@ -31,7 +33,7 @@ clean: clean-docs find . -type d -name .mypy_cache -print -exec rm -r {} + find . -type d -name .pytest_cache -print -exec rm -r {} + $(PY_RUN) ruff clean - -docker image rm $(PKG_NAME) + -$(CONTAINER_CMD) image rm $(PKG_NAME) clean-docs: ## Remove generated doc files rm -f docs/index.md @@ -122,7 +124,7 @@ test-watch: ## Run tests continually and watch for changes $(PY_RUN) pytest-watcher --clear $(PY_SRCS) $(args) update-container-digest: ## Update container digests to latest - ./bin/update-container-digest Dockerfile + ./bin/update-container-digest Containerfile help: ## Display this help screen @grep -Eh '^[[:print:]]+:.*?##' $(MAKEFILE_LIST) | \ diff --git a/bin/docker-entry b/bin/container-entry similarity index 100% rename from bin/docker-entry rename to bin/container-entry diff --git a/bin/docker-wrapper b/bin/container-wrapper similarity index 67% rename from bin/docker-wrapper rename to bin/container-wrapper index d796be1..eed76a3 100755 --- a/bin/docker-wrapper +++ b/bin/container-wrapper @@ -1,11 +1,11 @@ #!/usr/bin/env bash # -# Wrap running `nava-platform` via Docker. Pass arguments as you would to +# Wrap running `nava-platform` via a container. Pass arguments as you would to # `nava-platform` itself. # # Since the tool heavily involves manipulating files on the host system, some of # which are in locations that may need created first, it can be fiddly to -# manually run via Docker. +# manually run. # # So this script tries to automate the annoying parts for common situations. To # not explode in complexity, it does make some assumptions (or reserves the @@ -15,8 +15,12 @@ # set -euo pipefail +set -x + +CONTAINER_CMD=${CONTAINER_CMD:-docker} + processed_args=() -docker_flags=() +run_flags=() detected_host_paths=() looks_like_path() { @@ -30,16 +34,31 @@ case "${uname_out}" in *) 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)") +# TODO: make a consolidated setting/detection for rootless podman which seems to +# have broken --userns=keep-id/--userns=keep-id:uid=$(id -u),gid=$(id -g) +# handling so we need to let things inside the container run as root to most +# seamlessly map created files back to the hosts uid/gid + +# mostly for when running on Linux, but when using a daemon running as root, +# explicitly run the container as host user to help avoid any file permission +# issues with mounted locations from the host file system +if [[ "${CONTAINER_CMD}" == "docker" ]]; then + run_flags+=(--user "$(id -u):$(id -g)") +fi + + +if [[ "${CONTAINER_CMD}" == "docker" ]]; then + CONTAINER_USER_HOME="" +else + # otherwise the host user is going to be map to root user in the container + CONTAINER_USER_HOME="/root" +fi # 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") + run_flags+=("-v=$HOME/.gitconfig:${CONTAINER_USER_HOME}/.gitconfig") fi # figure out what location to mount for logs @@ -59,7 +78,7 @@ 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") +run_flags+=("-v=${HOST_LOG_DIR}:${CONTAINER_USER_HOME}/.local/state/nava-platform-cli/log:z") # process the script arguments for ((i=1;i<=$#;i++)) @@ -83,7 +102,7 @@ do abs_value=$(realpath "${value}") detected_host_paths+=("${abs_value}") - docker_flags+=("-v=${abs_value}:${abs_value}:z") + run_flags+=("-v=${abs_value}:${abs_value}:z") processed_args+=("${abs_value}") else processed_args+=("${value}") @@ -91,12 +110,12 @@ do done # 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 +# the container runtime 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? +# have simpler special handling for or just not support that mode via a container? for dpath in "${detected_host_paths[@]}"; do if [[ ! -e "${dpath}" ]]; then # basically, does the path look like a directory? @@ -110,4 +129,4 @@ for dpath in "${detected_host_paths[@]}"; do fi done -docker run --interactive --rm "${docker_flags[@]}" nava-platform-cli "${processed_args[@]}" +${CONTAINER_CMD} run --interactive --rm "${run_flags[@]}" nava-platform-cli "${processed_args[@]}" diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index ae1d3c6..4d39b97 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -140,15 +140,15 @@ yourself. for simplified execution: ```sh - ./bin/docker-wrapper infra install ./my_project_directory + ./bin/container-wrapper infra install ./my_project_directory ``` (or create an alias in your shell like `alias nava-platform = - /bin/docker-wrapper`) + /bin/container-wrapper`) !!! warning - The `docker-wrapper` script makes assumptions about your + The `container-wrapper` script makes assumptions about your environment. Review the script comments before use. ??? note "Running manually" diff --git a/docs/reference/development.md b/docs/reference/development.md index b6dd9ae..0a1b04f 100644 --- a/docs/reference/development.md +++ b/docs/reference/development.md @@ -88,7 +88,7 @@ for a overview: ├── nava/ # CLI app source code ├── tests/ # Unit tests ├── tests-e2e/ # End-to-end tests for CLI behavior -├── Dockerfile # Source for the container build of the CLI +├── Containerfile # Source for the container build of the CLI ├── flake.lock # Nix ├── flake.nix # Nix ├── Makefile # Main development interface diff --git a/flake.nix b/flake.nix index d86fef6..a69f8cf 100644 --- a/flake.nix +++ b/flake.nix @@ -129,7 +129,6 @@ ''; }); - # TODO: could add docker-client here? generalDevPackages = with pkgs; [ # dev tooling gnumake @@ -144,7 +143,7 @@ skopeo ]; - dockerEntryPkg = + containerEntryPkg = let scriptDeps = [ pkgs.coreutils # for id, stat @@ -152,30 +151,30 @@ ]; in pkgs.stdenv.mkDerivation { - name = "docker-entry"; + name = "container-entry"; src = pkgs.lib.fileset.toSource { root = ./.; - fileset = ./bin/docker-entry; + fileset = ./bin/container-entry; }; nativeBuildInputs = [ pkgs.makeWrapper ]; installPhase = '' mkdir -p $out/bin - install $src/bin/docker-entry $out/bin/docker-entry + install $src/bin/container-entry $out/bin/container-entry - wrapProgram $out/bin/docker-entry --prefix PATH : ${pkgs.lib.makeBinPath scriptDeps} + wrapProgram $out/bin/container-entry --prefix PATH : ${pkgs.lib.makeBinPath scriptDeps} ''; }; - dockerBuildArgs = { + containerBuildArgs = { name = "nava-platform-cli"; tag = "latest"; contents = [ - dockerEntryPkg + containerEntryPkg nava-platform-cli ] ++ runtimePackages; config = { - Entrypoint = "docker-entry"; + Entrypoint = "container-entry"; WorkingDir = "/project-dir"; }; }; @@ -214,8 +213,8 @@ nava-platform-cli-bin = nava-platform-cli-bin; docs = cli-docs-site; - docker = pkgs.dockerTools.buildLayeredImage dockerBuildArgs; - dockerStream = pkgs.dockerTools.streamLayeredImage dockerBuildArgs; + container = pkgs.dockerTools.buildLayeredImage containerBuildArgs; + containerStream = pkgs.dockerTools.streamLayeredImage containerBuildArgs; }; # nix run . @@ -350,6 +349,19 @@ pkgs.pipx ]; }; + + # Shell for container interaction. For testing running the package via + # a container runtime or other needs. + podman = pkgs.mkShell { + packages = [ + generalDevPackages + pkgs.podman + ]; + + env = { + CONTAINER_CMD = "podman"; + }; + }; }; legacyPackages = pkgs;