diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..d9c5a6ee --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +test/libs/ diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 099a8788..00000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: - - master - - development - pull_request: - branches: - - master - - development - schedule: - - cron: '32 11 * * 6' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - - permissions: - actions: read - contents: read - security-events: write - - steps: - - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2 - # Initializes the CodeQL tools for scanning. - - - name: Initialize CodeQL - uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 #v4.35.2 - with: - languages: 'python' - - - name: Autobuild - uses: github/codeql-action/autobuild@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 #v4.35.2 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 #v4.35.2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 58f3cc42..7caf7d6a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,11 +7,6 @@ on: permissions: contents: read -env: - FORCE_COLOR: 1 - PYTHONUNBUFFERED: 1 - PYTHONUTF8: 1 - jobs: smoke-tests: if: github.event.pull_request.draft == false @@ -25,7 +20,7 @@ jobs: - name: Check scripts in repository are executable run: | IFS=$'\n'; - for f in $(find . -name '*.sh'); do if [[ ! -x $f ]]; then echo "$f is not executable" && FAIL=1; fi ;done + for f in $(find . -name '*.sh' -o -name '*.bats'); do if [[ ! -x $f ]]; then echo "$f is not executable" && FAIL=1; fi ;done unset IFS; # If FAIL is 1 then we fail. [[ $FAIL == 1 ]] && exit 1 || echo "Scripts are executable!" @@ -37,7 +32,7 @@ jobs: display-engine: sarif-fmt - name: Secret Scanning with TruffleHog - uses: trufflesecurity/trufflehog@17456f8c7d042d8c82c9a8ca9e937231f9f42e26 #v3.95.2 + uses: trufflesecurity/trufflehog@37b77001d0174ebec2fcca2bd83ff83a6d45a3ab #v3.95.3 with: extra_args: --results=verified,unknown @@ -52,12 +47,6 @@ jobs: - name: Run editorconfig-checker run: editorconfig-checker - - name: Check python code formatting with black - uses: psf/black@c6755bb741b6481d6b3d3bb563c83fa060db96c9 #26.3.1 - with: - src: "./test" - options: "--check --diff --color" - distro-test: if: github.event.pull_request.draft == false runs-on: ubuntu-latest @@ -85,22 +74,9 @@ jobs: alpine_3_22, alpine_3_23, ] - env: - DISTRO: ${{matrix.distro}} steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2 - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 #v6.2.0 - with: - python-version: "3.13" - - - name: Install wheel - run: pip install wheel - - - name: Install dependencies - run: pip install -r test/requirements.txt - - - name: Test with tox - run: tox -c test/tox.${DISTRO}.ini + - name: Run BATS test suite for ${{ matrix.distro }} + run: DISTRO=${{ matrix.distro }} bash test/run.sh diff --git a/.gitignore b/.gitignore index 6322fd3e..97748824 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,7 @@ .DS_Store -*.pyc *.swp -__pycache__ -.cache -.pytest_cache -.tox -.eggs -*.egg-info .idea/ *.iml .vscode/ -.venv/ .fleet/ -.cache/ +test/libs/ diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 73deb53b..eaee8747 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -162,7 +162,7 @@ EOM ) # List of required packages on APK based systems -PIHOLE_META_VERSION_APK=0.2 +PIHOLE_META_VERSION_APK=0.3 PIHOLE_META_DEPS_APK=( bash bash-completion @@ -172,6 +172,7 @@ PIHOLE_META_DEPS_APK=( cronie curl dialog + gawk git grep iproute2-minimal # piholeARPTable.sh diff --git a/gravity.sh b/gravity.sh index 71f428fb..ccf5b1df 100755 --- a/gravity.sh +++ b/gravity.sh @@ -772,18 +772,22 @@ gravity_DownloadBlocklistFromUrl() { # Define the generic error message curlOutputFormat='%{http_code};No message available. Non supported curl version.' - # Check if the installed curl version supports the "-w %{errormsg}" option (available as of curl 7.75.0) - # (https://github.com/pi-hole/pi-hole/pull/6605#discussion_r3112153347) - # First we get the current curl version. + # Get the current installed curl version. curlVersion=$(curl --version | awk '{print $2;exit}') - # After that, we pipe the current version along with the string '7.75' (the minimum version supporting the required option.) - # Then we sort the list in natural (version) order and return the first item which will be the lowest version seen. - # If it is "7.75" then the current version is greater than or equal to "7.75.0". - # Compatibility notes: - # Busybox doesn't support some long flags: - # - "sort -V" is short form of "sort --version-sort" - # - "head -n1" is short form of "head --lines=1" - if [[ "$(printf '%s\n' "${curlVersion}" "7.75" | sort -V | head -n1)" == 7.75 ]]; then + + # Check if the installed curl version supports the "-w %{errormsg}" option. + # The minimum curl version supporting this option is 7.75.0. + # (https://github.com/pi-hole/pi-hole/pull/6605#discussion_r3112153347) + # + # We use "awk" to compare versions by subtracting 7.75 from the version number. + # If the result is greater than or equal to zero, the option is supported. + # (see https://github.com/pi-hole/pi-hole/issues/6615) + # + # Notes: + # - Use parameter expansion to get only Major and Minor version parts (containing only one dot). + # - The comparison result will be true or false. We use it as exit code. + # - awk considers "true=1". We negate the comparison to exit with "0" when a desired version is found. + if echo "${curlVersion%.*}" | awk '{exit !($1 - 7.75 >= 0)}'; then # Use the error message returned by curl curlOutputFormat='%{http_code};%{errormsg}' fi @@ -796,48 +800,49 @@ gravity_DownloadBlocklistFromUrl() { # a generic message is returned. curlOutput=$(curl --connect-timeout ${curl_connect_timeout} -s --fail -L ${compression:+${compression}} ${customUpstreamResolver:+${customUpstreamResolver}} "${modifiedOptions[@]}" -w "${curlOutputFormat}" "${url}" -o "${listCurlBuffer}") curlExitCode="$?" + + + # Retrieve http_code and errormsg values, returned by curl command + IFS=";" read -r httpCode curlErrorMsg <<<"$curlOutput" + + case $url in + # Did we "download" a local file? + "file"*) + if [[ -s "${listCurlBuffer}" ]]; then + echo -e "${OVER} ${TICK} ${str} Retrieval successful" + success=true + else + echo -e "${OVER} ${CROSS} ${str} Retrieval failed / empty list" + fi + ;; + # Did we "download" a remote file? + *) + # Use the exit code to determine if curl was successful or not. + # Use HTTP code only to select the correct error message. + if [[ "${curlExitCode}" == "0" ]]; then + case "${httpCode}" in + "200") echo -e "${OVER} ${TICK} ${str} Retrieval successful" ;; + "304") echo -e "${OVER} ${TICK} ${str} No changes detected" ;; + *) echo -e "${OVER} ${TICK} ${str} Success (http_code=${COL_CYAN}${httpCode}${COL_NC})" ;; + esac + success=true + else + case "${httpCode}" in + "403") echo -e "${OVER} ${CROSS} ${str} Forbidden" ;; + "404") echo -e "${OVER} ${CROSS} ${str} Not found" ;; + "408") echo -e "${OVER} ${CROSS} ${str} Time-out" ;; + "451") echo -e "${OVER} ${CROSS} ${str} Unavailable For Legal Reasons" ;; + "500") echo -e "${OVER} ${CROSS} ${str} Internal Server Error" ;; + "504") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Gateway)" ;; + "521") echo -e "${OVER} ${CROSS} ${str} Web Server Is Down (Cloudflare)" ;; + "522") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Cloudflare)" ;; + *) echo -e "${OVER} ${CROSS} ${str} Retrieval failed (exit_code=${COL_CYAN}${curlExitCode}${COL_NC} Msg: ${COL_CYAN}${curlErrorMsg}${COL_NC})" ;; + esac + fi + ;; + esac fi - # Retrieve http_code and errormsg values, returned by curl command - IFS=";" read -r httpCode curlErrorMsg <<<"$curlOutput" - - case $url in - # Did we "download" a local file? - "file"*) - if [[ -s "${listCurlBuffer}" ]]; then - echo -e "${OVER} ${TICK} ${str} Retrieval successful" - success=true - else - echo -e "${OVER} ${CROSS} ${str} Retrieval failed / empty list" - fi - ;; - # Did we "download" a remote file? - *) - # Use the exit code to determine if curl was successful or not. - # Use HTTP code only to select the correct error message. - if [[ "${curlExitCode}" == "0" ]]; then - case "${httpCode}" in - "200") echo -e "${OVER} ${TICK} ${str} Retrieval successful" ;; - "304") echo -e "${OVER} ${TICK} ${str} No changes detected" ;; - *) echo -e "${OVER} ${TICK} ${str} Success (http_code=${COL_CYAN}${httpCode}${COL_NC})" ;; - esac - success=true - else - case "${httpCode}" in - "403") echo -e "${OVER} ${CROSS} ${str} Forbidden" ;; - "404") echo -e "${OVER} ${CROSS} ${str} Not found" ;; - "408") echo -e "${OVER} ${CROSS} ${str} Time-out" ;; - "451") echo -e "${OVER} ${CROSS} ${str} Unavailable For Legal Reasons" ;; - "500") echo -e "${OVER} ${CROSS} ${str} Internal Server Error" ;; - "504") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Gateway)" ;; - "521") echo -e "${OVER} ${CROSS} ${str} Web Server Is Down (Cloudflare)" ;; - "522") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Cloudflare)" ;; - *) echo -e "${OVER} ${CROSS} ${str} Retrieval failed (exit_code=${COL_CYAN}${curlExitCode}${COL_NC} Msg: ${COL_CYAN}${curlErrorMsg}${COL_NC})" ;; - esac - fi - ;; - esac - local done="false" # Determine if the blocklist was downloaded and saved correctly if [[ "${success}" == true ]]; then diff --git a/test/README.md b/test/README.md index 692155b7..e99cdc99 100644 --- a/test/README.md +++ b/test/README.md @@ -1,25 +1,48 @@ # Recommended way to run tests -Make sure you have Docker and Python w/pip package manager. +The test suite is implemented with BATS and runs inside distro-specific Docker containers. -From command line all you need to do is: +## Requirements -- `pip install tox` -- `tox` +- Docker (with buildx support) +- Bash shell -Tox handles setting up a virtual environment for python dependencies, installing dependencies, building the docker images used by tests, and finally running tests. It's an easy way to have travis-ci like build behavior locally. +## Run tests -## Alternative py.test method of running tests +From the repository root, run: -You're responsible for setting up your virtual env and dependencies in this situation. - -``` -py.test -vv -n auto -m "build_stage" -py.test -vv -n auto -m "not build_stage" +```bash +bash test/run.sh --distro debian_12 ``` -The build_stage tests have to run first to create the docker images, followed by the actual tests which utilize said images. Unless you're changing your dockerfiles you shouldn't have to run the build_stage every time - but it's a good idea to rebuild at least once a day in case the base Docker images or packages change. +`test/run.sh` will: -# How do I debug python? +- Build the distro test image from `test/_.Dockerfile` +- Run the mock/function BATS suite in a fresh container +- Run the fresh-install BATS suite in a separate fresh container -Highly recommended: Setup PyCharm on a **Docker enabled** machine. Having a python debugger like PyCharm changes your life if you've never used it :) +## Available distros + +If you are unsure which distro names are valid, run: + +```bash +bash test/run.sh --help +``` + +The help output includes the current list of supported distros. + +## Optional: override BATS library versions + +`test/run.sh` accepts optional environment variable overrides when building test images: + +- `BATS_CORE_VER` +- `BATS_SUPPORT_VER` +- `BATS_ASSERT_VER` +- `BATS_MOCK_VER` +- `BATS_FILE_VER` + +Example: + +```bash +BATS_CORE_VER=v1.14.0 DISTRO=debian_12 bash test/run.sh +``` diff --git a/test/__init__.py b/test/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/test/_alpine_3_21.Dockerfile b/test/_alpine_3_21.Dockerfile index d7b88f20..18480ed5 100644 --- a/test/_alpine_3_21.Dockerfile +++ b/test/_alpine_3_21.Dockerfile @@ -3,7 +3,7 @@ FROM alpine:3.21 ENV GITDIR=/etc/.pihole ENV SCRIPTDIR=/opt/pihole RUN sed -i 's/#\(.*\/community\)/\1/' /etc/apk/repositories -RUN apk --no-cache add bash coreutils curl git jq openrc shadow +RUN apk --no-cache add bash coreutils curl git jq ncurses openrc shadow RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole ADD . $GITDIR @@ -13,6 +13,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_alpine_3_22.Dockerfile b/test/_alpine_3_22.Dockerfile index 25beb4e0..53b12eb9 100644 --- a/test/_alpine_3_22.Dockerfile +++ b/test/_alpine_3_22.Dockerfile @@ -3,7 +3,7 @@ FROM alpine:3.22 ENV GITDIR=/etc/.pihole ENV SCRIPTDIR=/opt/pihole RUN sed -i 's/#\(.*\/community\)/\1/' /etc/apk/repositories -RUN apk --no-cache add bash coreutils curl git jq openrc shadow +RUN apk --no-cache add bash coreutils curl git jq ncurses openrc shadow RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole ADD . $GITDIR @@ -13,6 +13,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_alpine_3_23.Dockerfile b/test/_alpine_3_23.Dockerfile index 2cb34137..542de879 100644 --- a/test/_alpine_3_23.Dockerfile +++ b/test/_alpine_3_23.Dockerfile @@ -3,7 +3,7 @@ FROM alpine:3.23 ENV GITDIR=/etc/.pihole ENV SCRIPTDIR=/opt/pihole RUN sed -i 's/#\(.*\/community\)/\1/' /etc/apk/repositories -RUN apk --no-cache add bash coreutils curl git jq openrc shadow +RUN apk --no-cache add bash coreutils curl git jq ncurses openrc shadow RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole ADD . $GITDIR @@ -13,6 +13,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_centos_10.Dockerfile b/test/_centos_10.Dockerfile index 78a89789..0b8747ed 100644 --- a/test/_centos_10.Dockerfile +++ b/test/_centos_10.Dockerfile @@ -14,6 +14,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_centos_9.Dockerfile b/test/_centos_9.Dockerfile index 73f53fa5..317333af 100644 --- a/test/_centos_9.Dockerfile +++ b/test/_centos_9.Dockerfile @@ -14,6 +14,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_debian_11.Dockerfile b/test/_debian_11.Dockerfile index 2389063c..b5f26d1d 100644 --- a/test/_debian_11.Dockerfile +++ b/test/_debian_11.Dockerfile @@ -11,6 +11,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_debian_12.Dockerfile b/test/_debian_12.Dockerfile index a6c5f1ed..9fccc645 100644 --- a/test/_debian_12.Dockerfile +++ b/test/_debian_12.Dockerfile @@ -11,6 +11,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_debian_13.Dockerfile b/test/_debian_13.Dockerfile index cfff2235..743ff9d7 100644 --- a/test/_debian_13.Dockerfile +++ b/test/_debian_13.Dockerfile @@ -11,6 +11,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_fedora_40.Dockerfile b/test/_fedora_40.Dockerfile index 43913895..e5f33d63 100644 --- a/test/_fedora_40.Dockerfile +++ b/test/_fedora_40.Dockerfile @@ -12,6 +12,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_fedora_41.Dockerfile b/test/_fedora_41.Dockerfile index c03371a5..ee1254e6 100644 --- a/test/_fedora_41.Dockerfile +++ b/test/_fedora_41.Dockerfile @@ -12,6 +12,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_fedora_42.Dockerfile b/test/_fedora_42.Dockerfile index 90b17c0b..4d9a072f 100644 --- a/test/_fedora_42.Dockerfile +++ b/test/_fedora_42.Dockerfile @@ -12,6 +12,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_fedora_43.Dockerfile b/test/_fedora_43.Dockerfile index 85f06ff8..fa400c7a 100644 --- a/test/_fedora_43.Dockerfile +++ b/test/_fedora_43.Dockerfile @@ -12,6 +12,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_ubuntu_20.Dockerfile b/test/_ubuntu_20.Dockerfile index 5b8deb5d..7b0850fb 100644 --- a/test/_ubuntu_20.Dockerfile +++ b/test/_ubuntu_20.Dockerfile @@ -11,6 +11,16 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_ubuntu_22.Dockerfile b/test/_ubuntu_22.Dockerfile index c3be89e1..53040d35 100644 --- a/test/_ubuntu_22.Dockerfile +++ b/test/_ubuntu_22.Dockerfile @@ -12,6 +12,16 @@ ENV DEBIAN_FRONTEND=noninteractive RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/_ubuntu_24.Dockerfile b/test/_ubuntu_24.Dockerfile index cf57c2aa..098e4760 100644 --- a/test/_ubuntu_24.Dockerfile +++ b/test/_ubuntu_24.Dockerfile @@ -12,6 +12,16 @@ ENV DEBIAN_FRONTEND=noninteractive RUN true && \ chmod +x $SCRIPTDIR/* +ARG BATS_CORE_VER +ARG BATS_SUPPORT_VER +ARG BATS_ASSERT_VER +ARG BATS_MOCK_VER +ARG BATS_FILE_VER +RUN git clone --depth=1 --single-branch --branch "${BATS_CORE_VER}" https://github.com/bats-core/bats-core $GITDIR/test/libs/bats && \ + git clone --depth=1 --single-branch --branch "${BATS_SUPPORT_VER}" https://github.com/bats-core/bats-support $GITDIR/test/libs/bats-support && \ + git clone --depth=1 --single-branch --branch "${BATS_ASSERT_VER}" https://github.com/bats-core/bats-assert $GITDIR/test/libs/bats-assert && \ + git clone --depth=1 --single-branch --branch "${BATS_MOCK_VER}" https://github.com/jasonkarns/bats-mock $GITDIR/test/libs/bats-mock && \ + git clone --depth=1 --single-branch --branch "${BATS_FILE_VER}" https://github.com/bats-core/bats-file $GITDIR/test/libs/bats-file + ENV SKIP_INSTALL=true -#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/conftest.py b/test/conftest.py deleted file mode 100644 index d4c763e7..00000000 --- a/test/conftest.py +++ /dev/null @@ -1,175 +0,0 @@ -import pytest -import testinfra -import testinfra.backend.docker -import subprocess -from textwrap import dedent - -IMAGE = "pytest_pihole:test_container" -tick_box = "[✓]" -cross_box = "[✗]" -info_box = "[i]" - - -# Monkeypatch sh to bash, if they ever support non hard code /bin/sh this can go away -# https://github.com/pytest-dev/pytest-testinfra/blob/master/testinfra/backend/docker.py -def run_bash(self, command, *args, **kwargs): - cmd = self.get_command(command, *args) - if self.user is not None: - out = self.run_local( - "docker exec -u %s %s /bin/bash -c %s", self.user, self.name, cmd - ) - else: - out = self.run_local("docker exec %s /bin/bash -c %s", self.name, cmd) - out.command = self.encode(cmd) - return out - - -testinfra.backend.docker.DockerBackend.run = run_bash - - -@pytest.fixture -def host(): - # run a container - docker_id = ( - subprocess.check_output(["docker", "run", "-t", "-d", "--cap-add=ALL", IMAGE]) - .decode() - .strip() - ) - - # return a testinfra connection to the container - docker_host = testinfra.get_host("docker://" + docker_id) - - yield docker_host - # at the end of the test suite, destroy the container - subprocess.check_call(["docker", "rm", "-f", docker_id]) - - -# Helper functions -def mock_command(script, args, container): - """ - Allows for setup of commands we don't really want to have to run for real - in unit tests - """ - full_script_path = "/usr/local/bin/{}".format(script) - mock_script = dedent(r"""\ - #!/bin/bash -e - echo "\$0 \$@" >> /var/log/{script} - case "\$1" in""".format(script=script)) - for k, v in args.items(): - case = dedent(""" - {arg}) - echo {res} - exit {retcode} - ;;""".format(arg=k, res=v[0], retcode=v[1])) - mock_script += case - mock_script += dedent(""" - esac""") - container.run( - """ - cat < {script}\n{content}\nEOF - chmod +x {script} - rm -f /var/log/{scriptlog}""".format( - script=full_script_path, content=mock_script, scriptlog=script - ) - ) - - -def mock_command_passthrough(script, args, container): - """ - Per other mock_command* functions, allows intercepting of commands we don't want to run for real - in unit tests, however also allows only specific arguments to be mocked. Anything not defined will - be passed through to the actual command. - - Example use-case: mocking `git pull` but still allowing `git clone` to work as intended - """ - orig_script_path = container.check_output("command -v {}".format(script)) - full_script_path = "/usr/local/bin/{}".format(script) - mock_script = dedent(r"""\ - #!/bin/bash -e - echo "\$0 \$@" >> /var/log/{script} - case "\$1" in""".format(script=script)) - for k, v in args.items(): - case = dedent(""" - {arg}) - echo {res} - exit {retcode} - ;;""".format(arg=k, res=v[0], retcode=v[1])) - mock_script += case - mock_script += dedent(r""" - *) - {orig_script_path} "\$@" - ;;""".format(orig_script_path=orig_script_path)) - mock_script += dedent(""" - esac""") - container.run( - """ - cat < {script}\n{content}\nEOF - chmod +x {script} - rm -f /var/log/{scriptlog}""".format( - script=full_script_path, content=mock_script, scriptlog=script - ) - ) - - -def mock_command_run(script, args, container): - """ - Allows for setup of commands we don't really want to have to run for real - in unit tests - """ - full_script_path = "/usr/local/bin/{}".format(script) - mock_script = dedent(r"""\ - #!/bin/bash -e - echo "\$0 \$@" >> /var/log/{script} - case "\$1 \$2" in""".format(script=script)) - for k, v in args.items(): - case = dedent(""" - \"{arg}\") - echo {res} - exit {retcode} - ;;""".format(arg=k, res=v[0], retcode=v[1])) - mock_script += case - mock_script += dedent(r""" - esac""") - container.run( - """ - cat < {script}\n{content}\nEOF - chmod +x {script} - rm -f /var/log/{scriptlog}""".format( - script=full_script_path, content=mock_script, scriptlog=script - ) - ) - - -def mock_command_2(script, args, container): - """ - Allows for setup of commands we don't really want to have to run for real - in unit tests - """ - full_script_path = "/usr/local/bin/{}".format(script) - mock_script = dedent(r"""\ - #!/bin/bash -e - echo "\$0 \$@" >> /var/log/{script} - case "\$1 \$2" in""".format(script=script)) - for k, v in args.items(): - case = dedent(""" - \"{arg}\") - echo \"{res}\" - exit {retcode} - ;;""".format(arg=k, res=v[0], retcode=v[1])) - mock_script += case - mock_script += dedent(r""" - esac""") - container.run( - """ - cat < {script}\n{content}\nEOF - chmod +x {script} - rm -f /var/log/{scriptlog}""".format( - script=full_script_path, content=mock_script, scriptlog=script - ) - ) - - -def run_script(Pihole, script): - result = Pihole.run(script) - assert result.rc == 0 - return result diff --git a/test/requirements.txt b/test/requirements.txt deleted file mode 100644 index 95e17e96..00000000 --- a/test/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -pyyaml == 6.0.3 -pytest == 9.0.3 -pytest-xdist == 3.8.0 -pytest-testinfra == 10.2.2 -tox == 4.52.1 -pytest-clarity == 1.0.1 diff --git a/test/run.sh b/test/run.sh new file mode 100755 index 00000000..a07b8c25 --- /dev/null +++ b/test/run.sh @@ -0,0 +1,162 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "${SCRIPT_DIR}" + +# --------------------------------------------------------------------------- +# CLI arguments (optional) +# --------------------------------------------------------------------------- + +usage() { + echo "Usage:" + echo " DISTRO= bash test/run.sh" + echo " bash test/run.sh --distro " + echo "" + echo "Options:" + echo " -d, --distro Distro to test (e.g., debian_12)" + echo " -h, --help Show this help" +} + +list_distros() { + find . -maxdepth 1 -name '_*.Dockerfile' | sed 's|^\./||;s/^_//;s/\.Dockerfile$//' | sort +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -d|--distro) + shift + if [[ $# -eq 0 ]]; then + echo "Error: --distro requires a value" + usage + exit 1 + fi + DISTRO="$1" + ;; + -h|--help) + usage + echo "" + echo "Available distros:" + list_distros + exit 0 + ;; + *) + echo "Error: Unknown option '$1'" + usage + exit 1 + ;; + esac + shift +done + +# --------------------------------------------------------------------------- +# Distro selection +# --------------------------------------------------------------------------- + +if [[ -z "${DISTRO:-}" ]]; then + echo "Error: DISTRO is required." + echo "Example: DISTRO=debian_12 bash test/run.sh" + echo "or: bash test/run.sh --distro debian_12" + echo "" + echo "Available distros:" + list_distros + exit 1 +fi + +DOCKERFILE="_${DISTRO}.Dockerfile" +if [[ ! -f "${DOCKERFILE}" ]]; then + echo "Error: Unknown distro '${DISTRO}'. Available distros:" + list_distros | sed 's/^/ /' + exit 1 +fi + +# Determine distro family to select which test files to run. +# rhel: CentOS/Fedora — includes SELinux tests +# alpine: Alpine Linux +# debian: Debian/Ubuntu (default) +distro_family() { + case "$1" in + centos_* | fedora_*) echo "rhel" ;; + alpine_*) echo "alpine" ;; + *) echo "debian" ;; + esac +} +DISTRO_FAMILY=$(distro_family "${DISTRO}") + +# --------------------------------------------------------------------------- +# Suite definitions +# +# Suite 1 — mock/function tests: run together in one container. Each test +# cleans up its own state via setup()/teardown(); no full install occurs. +# +# Suite 2 — fresh install: runs alone in its own container so the installer +# can mutate the filesystem freely without needing any teardown. +# --------------------------------------------------------------------------- + +SUITE_1=( + test_automated_install.bats + test_installer_ftl.bats + test_network.bats + test_utils.bats +) +[[ "${DISTRO_FAMILY}" == "rhel" ]] && SUITE_1+=(test_selinux.bats) + +SUITE_2=(test_fresh_install.bats) + +# --------------------------------------------------------------------------- +# BATS library versions — single source of truth, passed to Docker as build +# args so the Dockerfiles themselves stay version-agnostic. Override any of +# these by setting the corresponding environment variable before calling this +# script, e.g. BATS_CORE_VER=v1.14.0 bash test/run.sh --distro debian_12 +# --------------------------------------------------------------------------- + +BATS_CORE_VER="${BATS_CORE_VER:-v1.13.0}" +BATS_SUPPORT_VER="${BATS_SUPPORT_VER:-v0.3.0}" +BATS_ASSERT_VER="${BATS_ASSERT_VER:-v2.2.4}" +BATS_MOCK_VER="${BATS_MOCK_VER:-v1.2.5}" +BATS_FILE_VER="${BATS_FILE_VER:-v0.4.0}" + +# --------------------------------------------------------------------------- +# Build the test image (once, shared by both suites) +# --------------------------------------------------------------------------- + +IMAGE_TAG="pihole_test:${DISTRO}" + +docker buildx build \ + --load \ + --progress plain \ + --build-arg "BATS_CORE_VER=${BATS_CORE_VER}" \ + --build-arg "BATS_SUPPORT_VER=${BATS_SUPPORT_VER}" \ + --build-arg "BATS_ASSERT_VER=${BATS_ASSERT_VER}" \ + --build-arg "BATS_MOCK_VER=${BATS_MOCK_VER}" \ + --build-arg "BATS_FILE_VER=${BATS_FILE_VER}" \ + -f "${DOCKERFILE}" \ + -t "${IMAGE_TAG}" \ + ../ + +# --------------------------------------------------------------------------- +# run_suite