diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ffc4d43..2323ac8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,7 +1,10 @@ name: Main on: + pull_request: push: + branches: + - main tags: - v* @@ -11,30 +14,64 @@ env: BUILD_PLATFORMS: linux/amd64,linux/arm64 jobs: + test-image: + name: Test image (${{ matrix.platform }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + goss_asset: goss-linux-amd64 + - platform: linux/arm64 + goss_asset: goss-linux-arm64 + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Install dgoss + run: | + curl -fsSL https://github.com/goss-org/goss/releases/download/v0.4.9/${{ matrix.goss_asset }} -o /usr/local/bin/goss + curl -fsSL https://github.com/goss-org/goss/releases/download/v0.4.9/dgoss -o /usr/local/bin/dgoss + chmod +x /usr/local/bin/goss /usr/local/bin/dgoss + + - name: Build and test image + run: make test BUILD_EXTRA_ARGS="--platform ${{ matrix.platform }}" TEST_PLATFORM=${{ matrix.platform }} + build-and-push-image: name: Build and push image + if: startsWith(github.ref, 'refs/tags/v') + needs: test-image runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v6 - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v4 - name: Login to DockerHub - uses: docker/login-action@v2 + uses: docker/login-action@v4 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Log in to the Container registry - uses: docker/login-action@v1 + uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -42,7 +79,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v6 with: images: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} @@ -53,7 +90,7 @@ jobs: type=match,pattern=v(.*),group=1 - name: Build and push Docker image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v7 with: context: . push: true diff --git a/CHANGELOG.md b/CHANGELOG.md index a68b614..6486292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +### Changed + +- Updated the packaged `imapfilter` version to `2.8.2`, pinned the Docker base image to Alpine `3`, removed stale explicit OpenSSL and unused network-tool package installs, and relied on the `imapfilter` package to pull its runtime dependencies from Alpine `edge/testing`. +- Added a `dgoss`-based container validation suite and CI job so pull requests can require a stable image test status check before merge. +- Extended CI container validation to run against both `amd64` and `arm64`, while keeping local `make test` behavior aligned with the host architecture by default. +- Fixed local `dgoss` execution on Apple Silicon by using a Linux `arm64` `goss` binary for container-side validation instead of the macOS host binary. + ## [2026-05-18] ### Added diff --git a/Dockerfile b/Dockerfile index ca18eea..19a6d0c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:latest +FROM alpine:3 ARG IMAPFILTER_CONFIG=/config ARG IMAPFILTER_LOGS=/logs @@ -17,8 +17,8 @@ RUN set -xe \ && apk update \ && apk upgrade \ && apk add --no-cache \ - libcrypto1.1 libssl1.1 moreutils bash wget curl \ - && apk add --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ \ + moreutils bash \ + && apk add --no-cache --repository https://dl-cdn.alpinelinux.org/alpine/edge/testing/ \ imapfilter \ && apk del --progress --purge \ && rm -rf /var/cache/apk/* diff --git a/IMAPFILTER_VERSION b/IMAPFILTER_VERSION index a603bb5..1817afe 100644 --- a/IMAPFILTER_VERSION +++ b/IMAPFILTER_VERSION @@ -1 +1 @@ -2.7.5 +2.8.2 diff --git a/Makefile b/Makefile index 13a28c4..01eb432 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,50 @@ IMAGE := imapfilter # override with: make BUILD_CMD=podman BUILD_CMD ?= docker +DGOSS_CMD ?= dgoss +BUILD_EXTRA_ARGS ?= +TEST_PLATFORM ?= +DGOSS_GOSS_PATH ?= CONFIG_DIR ?=$(shell pwd)/config LOG_DIR ?= _DOCKER_ENV := -e IMAPFILTER_DRY_RUN=yes -e IMAPFILTER_VERBOSE=yes -e IMAPFILTER_CONFIG_DIR=/config $(DOCKER_ENV) _EXTRA_DOCKER_PARAMS := $(EXTRA_DOCKER_PARAMS) +_DGOSS_ENV := +UNAME_S := $(shell uname -s) +UNAME_M := $(shell uname -m) ifneq ($(LOG_DIR),) _EXTRA_DOCKER_PARAMS := $(_EXTRA_DOCKER_PARAMS) -v $(LOG_DIR):/logs _DOCKER_ENV := $(_DOCKER_ENV) -e IMAPFILTER_LOG_DIR=/logs endif +ifneq ($(TEST_PLATFORM),) +_DGOSS_ENV := DOCKER_DEFAULT_PLATFORM=$(TEST_PLATFORM) +endif + +ifeq ($(DGOSS_GOSS_PATH),) +ifeq ($(UNAME_S),Darwin) +ifeq ($(UNAME_M),arm64) +DGOSS_GOSS_PATH := /Users/sandipb/bin/goss-linux-arm64 +endif +endif +endif + DOCKER_RUN_PARAMS := -ti --rm --init $(_EXTRA_DOCKER_PARAMS) .PHONY: build build: - $(BUILD_CMD) build -t local/${IMAGE} . + $(BUILD_CMD) build $(BUILD_EXTRA_ARGS) -t local/${IMAGE} . + +.PHONY: test +test: build + @command -v $(DGOSS_CMD) >/dev/null 2>&1 || { echo "$(DGOSS_CMD) is required"; exit 1; } + @tmp_dir=$$(mktemp -d /tmp/imapfilter-goss.XXXXXX); \ + trap 'rm -rf "$$tmp_dir"' EXIT; \ + cp $(CURDIR)/goss.yaml "$$tmp_dir/goss.yaml"; \ + printf 'imapfilter_version: %s\n' "$$(cat IMAPFILTER_VERSION)" > "$$tmp_dir/vars.yaml"; \ + $(_DGOSS_ENV) GOSS_FILES_PATH="$$tmp_dir" GOSS_VARS="vars.yaml" \ + GOSS_PATH="$(DGOSS_GOSS_PATH)" $(DGOSS_CMD) run --rm --entrypoint sh local/${IMAGE} -c "sleep 30" ## Update the base imapfilter version file .PHONY: update-version diff --git a/README.md b/README.md index c0cfc54..a069212 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,24 @@ The `Makefile` uses Docker by default. To use Podman or another compatible comma $ make BUILD_CMD=podman build ``` +The image is built on Alpine `3` and installs `imapfilter` from Alpine `edge/testing`, because `imapfilter` is not currently available in the stable Alpine repositories. + +Run the container validation suite with `dgoss`: + +```shell-session +$ make test +``` + +This requires `dgoss` to be installed locally and validates that the container contains the expected tooling, reports the version tracked in `IMAPFILTER_VERSION`, and can invoke `imapfilter` through the packaged entrypoint. + +On macOS, `dgoss` needs a Linux `goss` binary because it copies `goss` into the test container and executes it there. The `Makefile` defaults to `/Users/sandipb/bin/goss-linux-arm64` on Apple Silicon. Override `DGOSS_GOSS_PATH` if your local setup uses a different location or architecture. + +By default, `make test` validates the image for the local Docker host architecture. To exercise a specific Docker platform explicitly, pass `TEST_PLATFORM`, for example: + +```shell-session +$ make test BUILD_EXTRA_ARGS="--platform linux/arm64" TEST_PLATFORM=linux/arm64 +``` + Run the local image against a config directory: ```shell-session diff --git a/goss.yaml b/goss.yaml new file mode 100644 index 0000000..fcf4433 --- /dev/null +++ b/goss.yaml @@ -0,0 +1,25 @@ +command: + bash-present: + exec: bash --version + exit-status: 0 + + ts-present: + exec: sh -lc 'command -v ts' + exit-status: 0 + + imapfilter-version: + exec: "sh -lc 'test \"$(/usr/bin/imapfilter -V 2>&1 | awk '\\''{print $2}'\\'')\" = \"{{ .Vars.imapfilter_version }}\"'" + exit-status: 0 + + entrypoint-version: + exec: sh -lc 'IMAPFILTER_EXTRA_ARGS=-V /entrypoint.sh 2>&1' + exit-status: 0 + stdout: + - "Running: /usr/bin/imapfilter -V" + - "IMAPFilter {{ .Vars.imapfilter_version }}" + +file: + /entrypoint.sh: + exists: true + /usr/bin/imapfilter: + exists: true