Files
ngx_http_monitoring_module/.github/workflows/release.yml

428 lines
15 KiB
YAML

name: Build and Release Module
"on":
workflow_dispatch:
inputs:
distro_targets:
description: "Space-separated distro images to build in, for example: ubuntu:24.04 debian:12 debian:13"
required: true
default: "ubuntu:24.04 debian:12 debian:13"
upstream_nginx_versions:
description: "Space-separated upstream nginx.org versions for generic tarball builds"
required: true
default: "1.22.1 1.24.0 1.26.3 1.28.0 1.30.0"
target_arches:
description: "Space-separated targets: linux-x86_64 linux-arm64 linux-armv7"
required: true
default: "linux-x86_64 linux-arm64 linux-armv7"
version_bump:
description: "Semantic version part to increment from the latest v* tag"
required: true
type: choice
default: patch
options:
- patch
- minor
- major
push_image:
description: "Push multi-platform demo Docker image to GHCR using the new tag"
type: boolean
required: true
default: true
permissions:
contents: write
packages: write
env:
DEFAULT_DISTRO_TARGETS: "ubuntu:24.04 debian:12 debian:13"
DEFAULT_UPSTREAM_NGINX_VERSIONS: "1.22.1 1.24.0 1.26.3 1.28.0 1.30.0"
DEFAULT_TARGET_ARCHES: "linux-x86_64 linux-arm64 linux-armv7"
MODULE_NAME: ngx_http_monitoring_module
DEB_PACKAGE_NAME: libnginx-mod-http-monitoring
RELEASE_BUILD_IMAGE: ubuntu:18.04
RELEASE_LIBC_BASELINE: ubuntu-bionic-glibc-2.27
NGINX_CONFIGURE_ARGS: >-
--with-compat
--with-http_ssl_module
--with-http_stub_status_module
--with-http_realip_module
--with-http_v2_module
--with-http_gzip_static_module
jobs:
release:
name: Build distro modules and publish release
runs-on: ubuntu-22.04
timeout-minutes: 240
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Compute next release tag
id: version
shell: bash
env:
INPUT_VERSION_BUMP: ${{ github.event.inputs.version_bump }}
run: |
set -euo pipefail
git fetch --tags --force
latest_tag="$(
git tag -l 'v*' \
| grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \
| sort -V \
| tail -n 1 \
|| true
)"
if [[ -z "$latest_tag" ]]; then
latest_tag="v0.0.0"
fi
version="${latest_tag#v}"
IFS='.' read -r major minor patch <<< "$version"
case "$INPUT_VERSION_BUMP" in
major)
major=$((major + 1))
minor=0
patch=0
;;
minor)
minor=$((minor + 1))
patch=0
;;
patch)
patch=$((patch + 1))
;;
*)
echo "Unsupported version_bump: $INPUT_VERSION_BUMP" >&2
exit 1
;;
esac
next_tag="v${major}.${minor}.${patch}"
while git rev-parse -q --verify "refs/tags/${next_tag}" >/dev/null; do
patch=$((patch + 1))
next_tag="v${major}.${minor}.${patch}"
done
{
printf 'NEXT_TAG=%s\n' "$next_tag"
printf 'NEXT_VERSION=%s\n' "${next_tag#v}"
printf 'LATEST_TAG=%s\n' "$latest_tag"
} >> "$GITHUB_ENV"
{
printf 'next_tag=%s\n' "$next_tag"
printf 'next_version=%s\n' "${next_tag#v}"
printf 'latest_tag=%s\n' "$latest_tag"
} >> "$GITHUB_OUTPUT"
echo "Latest tag: $latest_tag"
echo "Next tag: $next_tag"
- name: Normalize target architectures
id: targets
shell: bash
env:
INPUT_TARGET_ARCHES: ${{ github.event.inputs.target_arches }}
run: |
set -euo pipefail
raw_targets="${INPUT_TARGET_ARCHES:-$DEFAULT_TARGET_ARCHES}"
normalized_targets=""
docker_platforms=""
add_target() {
local target_arch="$1"
local docker_platform="$2"
case " $normalized_targets " in
*" $target_arch "*)
return
;;
esac
normalized_targets="${normalized_targets}${normalized_targets:+ }${target_arch}"
docker_platforms="${docker_platforms}${docker_platforms:+,}${docker_platform}"
}
for raw_target in $raw_targets; do
case "$raw_target" in
linux-x86_64|x86_64|amd64|linux-amd64)
add_target "linux-x86_64" "linux/amd64"
;;
linux-arm64|linux-aarch64|arm64|aarch64)
add_target "linux-arm64" "linux/arm64"
;;
linux-armv7|linux-armhf|armv7|armhf)
add_target "linux-armv7" "linux/arm/v7"
;;
*)
echo "Unsupported target_arches entry: $raw_target" >&2
echo "Supported entries: linux-x86_64 linux-arm64 linux-armv7" >&2
exit 1
;;
esac
done
if [[ -z "$normalized_targets" ]]; then
echo "No release target architectures were selected" >&2
exit 1
fi
{
printf 'target_arches=%s\n' "$normalized_targets"
printf 'docker_platforms=%s\n' "$docker_platforms"
} >> "$GITHUB_OUTPUT"
echo "Release targets: $normalized_targets"
echo "Docker platforms: $docker_platforms"
- name: Set up QEMU for ARM builds
uses: docker/setup-qemu-action@v3
with:
platforms: arm64,arm
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build distro release packages
shell: bash
env:
INPUT_DISTRO_TARGETS: ${{ github.event.inputs.distro_targets }}
NORMALIZED_TARGET_ARCHES: ${{ steps.targets.outputs.target_arches }}
run: |
set -euo pipefail
distro_targets="${INPUT_DISTRO_TARGETS:-$DEFAULT_DISTRO_TARGETS}"
mkdir -p "$GITHUB_WORKSPACE/dist" "$GITHUB_WORKSPACE/.release-work"
if [[ -z "$distro_targets" ]]; then
echo "No distro_targets were selected" >&2
exit 1
fi
for distro_image in $distro_targets; do
for target_arch in $NORMALIZED_TARGET_ARCHES; do
case "$target_arch" in
linux-x86_64)
docker_platform="linux/amd64"
;;
linux-arm64)
docker_platform="linux/arm64"
;;
linux-armv7)
docker_platform="linux/arm/v7"
;;
*)
echo "Unexpected normalized target: $target_arch" >&2
exit 1
;;
esac
echo "::group::Build ${distro_image} ${target_arch}"
docker run --rm -i \
--platform "$docker_platform" \
-e DEB_PACKAGE_NAME \
-e DISTRO_IMAGE="$distro_image" \
-e DOCKER_PLATFORM="$docker_platform" \
-e GITHUB_REF \
-e GITHUB_SHA \
-e LATEST_TAG \
-e MODULE_NAME \
-e NEXT_TAG \
-e NEXT_VERSION \
-e TARGET_ARCH="$target_arch" \
-v "$GITHUB_WORKSPACE:/work" \
-w /work \
"$distro_image" \
bash /work/scripts/build-distro-release.sh
echo "::endgroup::"
done
done
- name: Build upstream nginx.org release tarballs
shell: bash
env:
INPUT_UPSTREAM_NGINX_VERSIONS: ${{ github.event.inputs.upstream_nginx_versions }}
NORMALIZED_TARGET_ARCHES: ${{ steps.targets.outputs.target_arches }}
run: |
set -euo pipefail
upstream_versions="${INPUT_UPSTREAM_NGINX_VERSIONS:-$DEFAULT_UPSTREAM_NGINX_VERSIONS}"
if [[ -z "$upstream_versions" ]]; then
echo "No upstream_nginx_versions were selected" >&2
exit 1
fi
for target_arch in $NORMALIZED_TARGET_ARCHES; do
case "$target_arch" in
linux-x86_64)
docker_platform="linux/amd64"
;;
linux-arm64)
docker_platform="linux/arm64"
;;
linux-armv7)
docker_platform="linux/arm/v7"
;;
*)
echo "Unexpected normalized target: $target_arch" >&2
exit 1
;;
esac
echo "::group::Build nginx.org ${target_arch} tarballs"
docker run --rm -i \
--platform "$docker_platform" \
-e BUILD_NGINX_VERSIONS="$upstream_versions" \
-e DIST_DIR=/work/dist \
-e DOCKER_PLATFORM="$docker_platform" \
-e GITHUB_REF \
-e GITHUB_SHA \
-e LATEST_TAG \
-e MODULE_NAME \
-e NEXT_TAG \
-e NEXT_VERSION \
-e NGINX_CONFIGURE_ARGS \
-e RELEASE_BUILD_IMAGE \
-e RELEASE_LIBC_BASELINE \
-e TARGET_ARCH="$target_arch" \
-v "$GITHUB_WORKSPACE:/work" \
-w /work \
"$RELEASE_BUILD_IMAGE" \
bash /work/scripts/build-upstream-release.sh
echo "::endgroup::"
done
- name: Generate release manifests and notes
shell: bash
run: |
set -euo pipefail
cd "$GITHUB_WORKSPACE/dist"
shopt -s nullglob
compatibility_files=( *.compatibility.json )
if [[ "${#compatibility_files[@]}" -eq 0 ]]; then
echo "No compatibility metadata files were generated" >&2
exit 1
fi
jq -s \
--arg releaseTag "$NEXT_TAG" \
--arg generatedAt "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'{
schema: 1,
release_tag: $releaseTag,
generated_at: $generatedAt,
artifacts: .
}' \
"${compatibility_files[@]}" > COMPATIBILITY-MATRIX.json
find . -maxdepth 1 -type f \
\( -name '*.deb' -o -name '*.tar.gz' -o -name '*.compatibility.json' -o -name 'COMPATIBILITY-MATRIX.json' \) \
-print0 \
| sort -z \
| xargs -0 sha256sum > SHA256SUMS.txt
{
printf '# %s %s\n\n' "$MODULE_NAME" "$NEXT_TAG"
printf 'Previous tag: `%s`\n\n' "$LATEST_TAG"
printf 'This release includes two artifact tiers:\n\n'
printf -- '- distro `.deb` packages built from Ubuntu/Debian Nginx source packages and pinned to the matching packaged Nginx revision\n'
printf -- '- nginx.org upstream-version tarballs for users who build or run Nginx from upstream source versions\n\n'
printf 'Recommended production artifacts are the `.deb` packages. They depend on the exact distro `nginx-abi-*` virtual package when the distribution exposes one and on the exact Nginx binary package version used for the build. That strict pin prevents Nginx package revisions from silently reusing an unrebuilt module.\n\n'
printf 'The nginx.org `.tar.gz` packages preserve upstream-version coverage but are not a safe substitute for distro packages on Ubuntu/Debian packaged Nginx.\n\n'
printf '## Distro Package Matrix\n\n'
jq -r '
.artifacts[]
| select(.build.method == "distro-source-package")
| "- `\(.artifacts.deb_file)`: \(.target.os_id) \(.target.os_version) \(.target.architecture), "
+ "\(.nginx.binary_package) \(.nginx.binary_version), source \(.nginx.source_package) \(.nginx.source_version), "
+ "ABI `\(if .nginx.abi == "" then "package-version-pinned" else .nginx.abi end)`"
' COMPATIBILITY-MATRIX.json
printf '\n## nginx.org Tarballs\n\n'
jq -r '
.artifacts[]
| select(.build.method == "upstream-nginxorg-source")
| "- `\(.artifacts.tarball_file)`: nginx.org \(.nginx.source_version), \(.target.architecture), baseline `\(.target.libc_baseline)`"
' COMPATIBILITY-MATRIX.json
printf '\n## Validation\n\n'
printf 'Distro `.deb` artifacts are installed into their target distro containers and validated with packaged `nginx -t` plus live endpoint checks. nginx.org tarballs are validated with the Nginx binary built from the same upstream source during the tarball build.\n'
} > RELEASE_NOTES.md
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: ngx-http-monitoring-module-${{ steps.version.outputs.next_tag }}-release-assets
path: |
dist/*.deb
dist/*.tar.gz
dist/*.compatibility.json
dist/COMPATIBILITY-MATRIX.json
dist/SHA256SUMS.txt
dist/RELEASE_NOTES.md
if-no-files-found: error
- name: Create incremented tag
shell: bash
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$NEXT_TAG" -m "Release $NEXT_TAG"
git push origin "$NEXT_TAG"
- name: Publish GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
gh release create "$NEXT_TAG" \
dist/*.deb \
dist/*.tar.gz \
dist/*.compatibility.json \
dist/COMPATIBILITY-MATRIX.json \
dist/SHA256SUMS.txt \
--title "$NEXT_TAG" \
--notes-file dist/RELEASE_NOTES.md
- name: Compute Docker image name
if: github.event.inputs.push_image == 'true'
id: image
shell: bash
run: |
set -euo pipefail
image_repo="$(echo "ghcr.io/${GITHUB_REPOSITORY}" | tr '[:upper:]' '[:lower:]')"
printf 'repo=%s\n' "$image_repo" >> "$GITHUB_OUTPUT"
- name: Log in to GHCR
if: github.event.inputs.push_image == 'true'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Build and push demo Docker image
if: github.event.inputs.push_image == 'true'
uses: docker/build-push-action@v6
with:
context: .
file: dockerized/Dockerfile
platforms: ${{ steps.targets.outputs.docker_platforms }}
push: true
build-args: |
BASE_IMAGE=debian:bookworm-slim
OPENSSL_RUNTIME_PACKAGE=libssl3
tags: |
${{ steps.image.outputs.repo }}:${{ steps.version.outputs.next_tag }}
${{ steps.image.outputs.repo }}:latest