name: Build and Release Module "on": workflow_dispatch: inputs: nginx_versions: description: "Space-separated Nginx versions to build against" 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 Docker image to GHCR using the new tag" type: boolean required: true default: true permissions: contents: write packages: write env: DEFAULT_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 RELEASE_BUILD_IMAGE: ubuntu:22.04 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 module and publish release runs-on: ubuntu-22.04 timeout-minutes: 120 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 release packages shell: bash env: INPUT_NGINX_VERSIONS: ${{ github.event.inputs.nginx_versions }} NORMALIZED_TARGET_ARCHES: ${{ steps.targets.outputs.target_arches }} run: | set -euo pipefail versions="${INPUT_NGINX_VERSIONS:-$DEFAULT_NGINX_VERSIONS}" mkdir -p "$GITHUB_WORKSPACE/dist" "$GITHUB_WORKSPACE/.release-work" 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 ${target_arch} release packages" docker run --rm \ --platform "$docker_platform" \ -e BUILD_NGINX_VERSIONS="$versions" \ -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 TARGET_ARCH="$target_arch" \ -v "$GITHUB_WORKSPACE:/work" \ -w /work \ "$RELEASE_BUILD_IMAGE" \ bash -s <<'BUILD_SCRIPT' set -euo pipefail export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y --no-install-recommends \ build-essential \ ca-certificates \ curl \ file \ libpcre2-dev \ libssl-dev \ zlib1g-dev rm -rf /var/lib/apt/lists/* target_workdir="/work/.release-work/${TARGET_ARCH}" mkdir -p "$target_workdir" /work/dist for nginx_version in $BUILD_NGINX_VERSIONS; do echo "::group::Nginx ${nginx_version} (${TARGET_ARCH})" archive="nginx-${nginx_version}.tar.gz" archive_path="${target_workdir}/${archive}" workdir="${target_workdir}/nginx-${nginx_version}" if [[ ! -f "$archive_path" ]]; then curl -fsSLo "$archive_path" \ "https://nginx.org/download/${archive}" fi rm -rf "$workdir" tar -xzf "$archive_path" -C "$target_workdir" cd "$workdir" ./configure \ --prefix=/usr/local/nginx \ --sbin-path=/usr/local/sbin/nginx \ --modules-path=/usr/local/nginx/modules \ --conf-path=/etc/nginx/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --http-log-path=/var/log/nginx/access.log \ --pid-path=/var/run/nginx.pid \ --lock-path=/var/run/nginx.lock \ --http-client-body-temp-path=/var/cache/nginx/client_temp \ --http-proxy-temp-path=/var/cache/nginx/proxy_temp \ --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \ --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \ --http-scgi-temp-path=/var/cache/nginx/scgi_temp \ $NGINX_CONFIGURE_ARGS \ --add-dynamic-module=/work make -j"$(nproc)" modules package="${MODULE_NAME}-${NEXT_TAG}-nginx-${nginx_version}-${TARGET_ARCH}" package_dir="/work/dist/$package" mkdir -p "$package_dir/modules" "$package_dir/examples" "$package_dir/docs" cp "objs/${MODULE_NAME}.so" "$package_dir/modules/${MODULE_NAME}.so" strip --strip-unneeded "$package_dir/modules/${MODULE_NAME}.so" || true cp /work/examples/nginx.conf "$package_dir/examples/nginx.conf" cp /work/examples/monitoring-site.conf "$package_dir/examples/monitoring-site.conf" cp /work/README.md "$package_dir/README.md" cp /work/docs/API.md "$package_dir/docs/API.md" cp /work/docs/CONFIGURATION.md "$package_dir/docs/CONFIGURATION.md" cp /work/docs/PERFORMANCE.md "$package_dir/docs/PERFORMANCE.md" { printf 'module=%s\n' "$MODULE_NAME" printf 'module_version=%s\n' "$NEXT_VERSION" printf 'release_tag=%s\n' "$NEXT_TAG" printf 'previous_tag=%s\n' "$LATEST_TAG" printf 'nginx_version=%s\n' "$nginx_version" printf 'target=%s\n' "$TARGET_ARCH" printf 'docker_platform=%s\n' "$DOCKER_PLATFORM" printf 'build_image=%s\n' "$RELEASE_BUILD_IMAGE" printf 'build_machine=%s\n' "$(uname -m)" printf 'runner=ubuntu-22.04\n' printf 'git_ref=%s\n' "$GITHUB_REF" printf 'git_sha=%s\n' "$GITHUB_SHA" printf 'configure_args=%s\n' "$NGINX_CONFIGURE_ARGS" printf 'built_at=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" } > "$package_dir/METADATA.txt" { printf '# %s for Nginx %s\n\n' "$MODULE_NAME" "$nginx_version" printf 'Release: `%s`\n\n' "$NEXT_TAG" printf 'Target: `%s` (`%s`)\n\n' "$TARGET_ARCH" "$DOCKER_PLATFORM" printf 'This package contains a prebuilt Linux dynamic module:\n\n' printf '```text\n' printf 'modules/%s.so\n' "$MODULE_NAME" printf '```\n\n' printf 'Copy it into your Nginx modules directory and add this near the top of `nginx.conf`:\n\n' printf '```nginx\n' printf 'load_module modules/%s.so;\n' "$MODULE_NAME" printf '```\n\n' printf 'This binary is built with `--with-compat` against Nginx %s on Ubuntu 22.04 for %s. ' "$nginx_version" "$TARGET_ARCH" printf 'For safest production use, run the same Nginx version, CPU architecture, and a compatible Linux/glibc environment. ' printf 'If your Nginx package has a materially different module ABI, rebuild from source with this repository.\n' } > "$package_dir/INSTALL.md" ( cd "$package_dir" sha256sum "modules/${MODULE_NAME}.so" > SHA256SUMS file "modules/${MODULE_NAME}.so" > FILE.txt ) tar -czf "/work/dist/$package.tar.gz" -C /work/dist "$package" rm -rf "$package_dir" echo "::endgroup::" done BUILD_SCRIPT echo "::endgroup::" done ( cd "$GITHUB_WORKSPACE/dist" sha256sum *.tar.gz > SHA256SUMS.txt ) { printf '# ngx_http_monitoring_module %s\n\n' "$NEXT_TAG" printf 'Previous tag: `%s`\n\n' "$LATEST_TAG" printf 'Prebuilt Linux dynamic module packages are attached for targets: `%s`.\n\n' "$NORMALIZED_TARGET_ARCHES" printf 'Each tarball is built against the Nginx version and target architecture in its filename and includes:\n\n' printf -- '- `modules/%s.so`\n' "$MODULE_NAME" printf -- '- `INSTALL.md`\n' printf -- '- `METADATA.txt`\n' printf -- '- `SHA256SUMS`\n' printf -- '- example configuration and API docs\n\n' printf 'Nginx dynamic modules are ABI-sensitive. Use the package matching your Nginx version and CPU architecture, and rebuild locally if your Nginx distribution uses incompatible build options.\n' } > "$GITHUB_WORKSPACE/dist/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/*.tar.gz 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/*.tar.gz 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 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 tags: | ${{ steps.image.outputs.repo }}:${{ steps.version.outputs.next_tag }} ${{ steps.image.outputs.repo }}:latest