diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 3273609..067a2b7 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -15,6 +15,47 @@ jobs: test: name: "Test" runs-on: ubuntu-latest + strategy: + matrix: + include: + # Node + - item: node6 + - item: node7 + - item: node8 + - item: node9 + - item: node10 + - item: node11 + - item: node12 + - item: node13 + - item: node14 + - item: node15 + - item: node16 + - item: node17 + - item: node18 + - item: node19 + - item: node20 + - item: node21 + - item: node22 + - item: node23 + - item: node24 + - item: node26 + # PHP + - item: php56 + - item: php70 + - item: php71 + - item: php72 + - item: php73 + - item: php74 + - item: php80 + - item: php81 + - item: php82 + - item: php83 + - item: php84 + - item: php85 + # Ansitest + - item: ansitest + + fail-fast: false steps: - name: Checkout uses: actions/checkout@v6 @@ -22,8 +63,17 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - - name: Test representative images - run: make test + - name: Build image + uses: docker/build-push-action@v7 + with: + context: ${{ matrix.item }} + tags: dockette/ci:${{ matrix.item }} + platforms: linux/amd64 + load: true + cache-from: type=gha + + - name: Test image + run: .scripts/test.sh ${{ matrix.item }} build: name: "Build" diff --git a/.scripts/test.sh b/.scripts/test.sh new file mode 100755 index 0000000..e08cef0 --- /dev/null +++ b/.scripts/test.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +# +# Smoke test for one dockette/ci image. +# +# Usage: .scripts/test.sh [image] +# item directory name, for example php84, node24 or ansitest +# image image to test (default: dockette/ci:) +# +set -uo pipefail + +ITEM="${1:?Usage: $0 [image]}" +IMAGE="${2:-dockette/ci:${ITEM}}" +FAILED=0 + +# Run a shell command in the image and print its output. +run() { + docker run --rm "${IMAGE}" sh -c "$1" 2>&1 +} + +# check : the command must exit with 0. +check() { + local out + if out="$(run "$2")"; then + echo "ok $1: $(echo "${out}" | head -n 1)" + else + echo "FAIL $1" + echo "${out}" | sed 's/^/ /' + FAILED=1 + fi +} + +# expect : the command output must be equal to the value. +expect() { + local out + out="$(run "$2")" + if [ "${out}" = "$3" ]; then + echo "ok $1: ${out}" + else + echo "FAIL $1: expected '$3', got:" + echo "${out}" | sed 's/^/ /' + FAILED=1 + fi +} + +test_common() { + check "tools" "for t in bash git curl make; do command -v \$t >/dev/null || { echo \"missing \$t\"; exit 1; }; done" +} + +test_php() { + local version="${ITEM#php}" + + expect "php version" "php -r 'echo PHP_MAJOR_VERSION . PHP_MINOR_VERSION;'" "${version}" + # A broken extension prints a startup warning, but php still exits with 0. + check "php startup" "out=\$(php -v 2>&1) && echo \"\$out\" | head -n 1 && ! echo \"\$out\" | grep -iE 'warning|error|unable to load|failed loading'" + check "php extensions" "m=\$(php -m) && for e in ctype curl gd iconv intl openssl pdo_mysql pdo_pgsql pdo_sqlite zip; do echo \"\$m\" | grep -qix \$e || { echo \"missing \$e\"; exit 1; }; done && echo all" + check "phpxd" "phpxd -v | grep -i xdebug" + check "composer" "composer --version" + test_common +} + +test_node() { + local version="${ITEM#node}" + + check "node" "node --version" + expect "node major version" "node -p 'process.versions.node.split(\".\")[0]'" "${version}" + check "npm" "npm --version" + if [ "${version}" -ge 18 ]; then + check "pnpm" "pnpm --version" + fi + test_common +} + +test_ansitest() { + check "ansible" "ansible --version" + check "ansible-lint" "ansible-lint --version" + check "yamllint" "yamllint --version" + check "molecule" "molecule --version" + check "docker" "docker --version" + check "collections" "ansible-galaxy collection list community.docker" + test_common +} + +echo "Testing ${IMAGE}" + +case "${ITEM}" in + php*) test_php ;; + node*) test_node ;; + ansitest) test_ansitest ;; + *) echo "No tests for ${ITEM}"; exit 1 ;; +esac + +exit "${FAILED}" diff --git a/AGENTS.md b/AGENTS.md index c5eb634..34f5997 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -59,6 +59,37 @@ For quick local testing, you can use `--load` with your native platform: docker buildx build --load -t dockette/ci:node24 ./node24 ``` +## Testing + +Each image has a smoke test in `.scripts/test.sh`. To test an image, build it +first, then run the script with the image directory name: + +```bash +# 1. Build the image and load it into the local Docker +docker buildx build --load -t dockette/ci:node24 ./node24 + +# 2. Run the smoke test +.scripts/test.sh node24 +``` + +The script starts the image and prints `ok` or `FAIL` for each check. It exits +with 1 if any check fails. + +To test an image with a different tag, pass the tag as the second argument: + +```bash +.scripts/test.sh php84 my-local/ci:php84 +``` + +What the script checks: + +- `phpXX`: PHP version, PHP starts without warnings, main extensions, `phpxd`, `composer` +- `nodeXX`: Node.js version, `npm`, `pnpm` (node18 and newer) +- `ansitest`: `ansible`, `ansible-lint`, `yamllint`, `molecule`, `docker`, `community.docker` collection +- all images: `bash`, `git`, `curl`, `make` + +`make test` builds and tests `php84`, `node24` and `ansitest` in one step. + ## Platform Detection To check your current platform: diff --git a/Makefile b/Makefile index 5f2c34d..9e02790 100644 --- a/Makefile +++ b/Makefile @@ -18,11 +18,9 @@ _docker-build-%: build: build-php-8.4 build-node24 build-ansitest test: build - docker run --rm ${DOCKER_IMAGE}:${PHP_TEST_TAG} php -v - docker run --rm ${DOCKER_IMAGE}:${PHP_TEST_TAG} composer --version - docker run --rm ${DOCKER_IMAGE}:${NODE_TEST_TAG} node --version - docker run --rm ${DOCKER_IMAGE}:${NODE_TEST_TAG} npm --version - docker run --rm ${DOCKER_IMAGE}:${ANSIBLE_TEST_TAG} ansible --version + .scripts/test.sh ${PHP_TEST_TAG} ${DOCKER_IMAGE}:${PHP_TEST_TAG} + .scripts/test.sh ${NODE_TEST_TAG} ${DOCKER_IMAGE}:${NODE_TEST_TAG} + .scripts/test.sh ${ANSIBLE_TEST_TAG} ${DOCKER_IMAGE}:${ANSIBLE_TEST_TAG} run: docker run -it --rm -v $$(pwd):/srv ${DOCKER_IMAGE}:${PHP_TEST_TAG} diff --git a/README.md b/README.md index 7d11349..dbbb7c1 100644 --- a/README.md +++ b/README.md @@ -74,10 +74,10 @@ docker run -it --rm -v $(pwd):/srv dockette/ci:php56 | NodeJS 24 (v24.18.1) | Alpine v3.24 | node24 | v24.18.1 | 11.12.1 | 11.20.0 | [Dockerfile](https://github.com/dockette/ci/blob/master/node24/Dockerfile) | | NodeJS 23 (v23.11.1) | Alpine v3.22 | node23 | v23.11.1 | 11.4.2 | 10.9.0 | [Dockerfile](https://github.com/dockette/ci/blob/master/node23/Dockerfile) | | NodeJS 22 (v22.23.2) | Alpine v3.21 | node22 | v22.23.2 | 10.9.1 | 9.15.9 | [Dockerfile](https://github.com/dockette/ci/blob/master/node22/Dockerfile) | -| NodeJS 21 (v21.7.3) | Alpine v3.20 | node21 | v21.7.3 | 10.9.1 | 10.22.0 | [Dockerfile](https://github.com/dockette/ci/blob/master/node21/Dockerfile) | -| NodeJS 20 (v20.8.1) | Alpine v3.18 | node20 | v20.8.1 | 9.6.6 | 10.22.0 | [Dockerfile](https://github.com/dockette/ci/blob/master/node20/Dockerfile) | -| NodeJS 19 (v19.7.0) | Alpine v3.17 | node19 | v19.7.0 | 9.1.2 | 10.22.0 | [Dockerfile](https://github.com/dockette/ci/blob/master/node19/Dockerfile) | -| NodeJS 18 (v18.9.1) | Alpine v3.16 | node18 | v18.9.1 | 8.10.0 | N/A | [Dockerfile](https://github.com/dockette/ci/blob/master/node18/Dockerfile) | +| NodeJS 21 (v21.7.3) | Alpine v3.20 | node21 | v21.7.3 | 10.9.1 | 10.34.6 | [Dockerfile](https://github.com/dockette/ci/blob/master/node21/Dockerfile) | +| NodeJS 20 (v20.8.1) | Alpine v3.18 | node20 | v20.8.1 | 9.6.6 | 10.34.6 | [Dockerfile](https://github.com/dockette/ci/blob/master/node20/Dockerfile) | +| NodeJS 19 (v19.7.0) | Alpine v3.17 | node19 | v19.7.0 | 9.1.2 | 10.34.6 | [Dockerfile](https://github.com/dockette/ci/blob/master/node19/Dockerfile) | +| NodeJS 18 (v18.9.1) | Alpine v3.16 | node18 | v18.9.1 | 8.10.0 | 8.15.9 | [Dockerfile](https://github.com/dockette/ci/blob/master/node18/Dockerfile) | | NodeJS 17 (v17.9.0) | Alpine v3.15 | node17 | v17.9.0 | 8.1.3 | N/A | [Dockerfile](https://github.com/dockette/ci/blob/master/node17/Dockerfile) | | NodeJS 16 (v16.11.1) | Alpine v3.14 | node16 | v16.11.1 | 7.17.0 | N/A | [Dockerfile](https://github.com/dockette/ci/blob/master/node16/Dockerfile) | | NodeJS 15 (v15.10.0) | Alpine v3.13 | node15 | v15.10.0 | 6.14.17 | N/A | [Dockerfile](https://github.com/dockette/ci/blob/master/node15/Dockerfile) | diff --git a/node12/Dockerfile b/node12/Dockerfile index d376bc0..10d0204 100644 --- a/node12/Dockerfile +++ b/node12/Dockerfile @@ -8,7 +8,7 @@ RUN echo '@community http://dl-cdn.alpinelinux.org/alpine/v3.12/community' >> /e # DEPENDENCIES ############################################################# apk add --update bash git ca-certificates curl openssh tzdata make direnv@v314 && \ # NODEJS ################################################################### - apk add --update nodejs-current npm && \ + apk add --update nodejs npm && \ # CLEAN UP ################################################################# rm -rf /var/cache/apk/* diff --git a/node18/Dockerfile b/node18/Dockerfile index b137edd..f164d17 100644 --- a/node18/Dockerfile +++ b/node18/Dockerfile @@ -8,7 +8,7 @@ RUN echo '@community http://dl-cdn.alpinelinux.org/alpine/v3.16/community' >> /e apk add --update bash git ca-certificates curl openssh tzdata make direnv && \ # NODEJS ################################################################### apk add --update nodejs-current@community npm@community && \ - corepack enable && corepack prepare pnpm@latest --activate && \ + corepack enable && corepack prepare pnpm@8.15.9 --activate && \ # CLEAN UP ################################################################# rm -rf /var/cache/apk/* diff --git a/node19/Dockerfile b/node19/Dockerfile index 77fa900..c909f8d 100644 --- a/node19/Dockerfile +++ b/node19/Dockerfile @@ -8,7 +8,7 @@ RUN echo '@community http://dl-cdn.alpinelinux.org/alpine/v3.17/community' >> /e apk add --update bash git ca-certificates curl openssh tzdata make direnv && \ # NODEJS ################################################################### apk add --update nodejs-current@community npm@community && \ - corepack enable && corepack prepare pnpm@latest --activate && \ + corepack enable && corepack prepare pnpm@10.34.6 --activate && \ # CLEAN UP ################################################################# rm -rf /var/cache/apk/* diff --git a/node20/Dockerfile b/node20/Dockerfile index 3f95b72..7b7985e 100644 --- a/node20/Dockerfile +++ b/node20/Dockerfile @@ -8,7 +8,7 @@ RUN echo '@community http://dl-cdn.alpinelinux.org/alpine/v3.18/community' >> /e apk add --update bash git ca-certificates curl openssh tzdata make direnv && \ # NODEJS ################################################################### apk add --update nodejs-current@community npm@community && \ - corepack enable && corepack prepare pnpm@latest --activate && \ + corepack enable && corepack prepare pnpm@10.34.6 --activate && \ # CLEAN UP ################################################################# rm -rf /var/cache/apk/* diff --git a/node21/Dockerfile b/node21/Dockerfile index 64b0922..70a1e8f 100644 --- a/node21/Dockerfile +++ b/node21/Dockerfile @@ -8,7 +8,7 @@ RUN echo '@community http://dl-cdn.alpinelinux.org/alpine/v3.20/community' >> /e apk add --update bash git ca-certificates curl openssh tzdata make direnv && \ # NODEJS ################################################################### apk add --update nodejs-current@community npm@community && \ - corepack enable && corepack prepare pnpm@latest --activate && \ + corepack enable && corepack prepare pnpm@10.34.6 --activate && \ # CLEAN UP ################################################################# rm -rf /var/cache/apk/* diff --git a/node8/Dockerfile b/node8/Dockerfile index aeb0394..f43c079 100644 --- a/node8/Dockerfile +++ b/node8/Dockerfile @@ -8,7 +8,7 @@ RUN echo '@main http://dl-cdn.alpinelinux.org/alpine/v3.8/main' >> /etc/apk/repo # DEPENDENCIES ############################################################# apk add --update bash git ca-certificates curl openssh tzdata make direnv@v314 && \ # NODEJS ################################################################### - apk add --update nodejs-current@main && \ + apk add --update nodejs-npm@main && \ # CLEAN UP ################################################################# rm -rf /var/cache/apk/*