Files
The-World-Is-Yours/.gitea/workflows/build-publish.yml
T
2026-04-26 04:15:12 +00:00

230 lines
11 KiB
YAML

# =============================================================================
# build-and-publish (multi-distro matrix)
#
# Builds twiy as a Debian .deb for each target distro in parallel:
# - trixie (Debian 13) -> uploaded to NEXUS_REPO_TRIXIE
# - raccoon (Ubuntu 26.04 LTS) -> uploaded to NEXUS_REPO_RACCOON
#
# Each matrix job spins up a Docker container of the target distro on the
# Gitea runner host, builds nginx + modules INSIDE the container so apt deps
# and ldd resolution match what end users have, then uploads the resulting
# .deb to that distro's Nexus apt-hosted repository.
#
# Required repository secrets:
# NEXUS_USER, NEXUS_PASS, NEXUS_URL (shared)
# NEXUS_REPO_TRIXIE (Debian 13 target)
# NEXUS_REPO_RACCOON (Ubuntu 26.04 target)
# =============================================================================
name: build-and-publish
on:
push:
branches: [master]
workflow_dispatch:
jobs:
build:
# Runner is just a docker host; build OS is determined by matrix.image.
runs-on: ubuntu-22.04
strategy:
# If trixie fails, still finish raccoon (and vice versa) — surface both.
fail-fast: false
matrix:
target: [trixie, raccoon]
include:
- target: trixie
image: debian:13
nexus_repo_secret: NEXUS_REPO_TRIXIE
- target: raccoon
image: ubuntu:26.04
nexus_repo_secret: NEXUS_REPO_RACCOON
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Build nginx and assemble .deb inside ${{ matrix.image }}
id: pkg
env:
TARGET: ${{ matrix.target }}
IMAGE: ${{ matrix.image }}
run: |
set -euo pipefail
mkdir -p dist
# The whole compile + .deb assembly happens inside the target distro
# container. Output is dropped into ./dist/ (mounted from the runner)
# so the publish step on the host can grab it.
sudo docker run --rm \
-v "$PWD:/repo" \
-w /repo \
-e TARGET="$TARGET" \
"$IMAGE" \
bash -euxc '
# build script handles its own apt-get install (per-distro list)
touch /.dockerenv
bash build/${TARGET}.sh new
bash build/${TARGET}.sh build
bash build/${TARGET}.sh postfix
PKG_NAME="twiy"
NGINX_VER="$(nginx -v 2>&1 | awk -F/ "{print \$2}")"
# Append CI run number AND target so each rebuild is a strictly-
# greater Debian revision. Without this, `apt upgrade twiy` would
# be a no-op when upstream nginx hasnt moved, so packaging fixes
# wouldnt reach users who already have the package installed.
# The ~target suffix keeps trixie/raccoon versions distinct in
# case any introspection ever compares them.
VERSION="${NGINX_VER}-${GITHUB_RUN_NUMBER:-1}~${TARGET}"
ARCH="amd64"
PKG_DIR="/opt/${PKG_NAME}_${VERSION}_${ARCH}"
DEB_DIR="${PKG_DIR}/DEBIAN"
# The *_temp dirs under /usr/local/nginx are nginxs compiled-in
# defaults for client_body / proxy / fastcgi / uwsgi / scgi temp
# storage (no --http-*-temp-path was passed to ./configure). They
# must exist before `nginx -t` runs, so we ship them empty in the
# .deb and the postinst chowns them to the nginx user.
mkdir -p "${PKG_DIR}/usr/sbin" "${PKG_DIR}/nginx" \
"${PKG_DIR}/etc/systemd/system" "${PKG_DIR}/var/log/nginx" \
"${PKG_DIR}/usr/lib" "${PKG_DIR}/usr/local/lib" \
"${PKG_DIR}/hostdata/default/public_html" \
"${PKG_DIR}/usr/nginx_lua" \
"${PKG_DIR}/usr/local/nginx/client_body_temp" \
"${PKG_DIR}/usr/local/nginx/proxy_temp" \
"${PKG_DIR}/usr/local/nginx/fastcgi_temp" \
"${PKG_DIR}/usr/local/nginx/uwsgi_temp" \
"${PKG_DIR}/usr/local/nginx/scgi_temp"
cp /usr/sbin/nginx "${PKG_DIR}/usr/sbin/"
cp -R /nginx/* "${PKG_DIR}/nginx/" || true
cp /etc/systemd/system/nginx.service "${PKG_DIR}/etc/systemd/system/"
cp -R /hostdata/default "${PKG_DIR}/hostdata/" || true
cp -R /usr/nginx_lua "${PKG_DIR}/usr/" || true
# Bundle every shared library nginx links against. ldd resolves
# against THIS containers libraries (not the runner host) so the
# .deb gets the correct per-distro libs.
for lib in $(ldd /usr/sbin/nginx | grep "=> /" | awk "{print \$3}"); do
cp "$lib" "${PKG_DIR}/usr/lib/" || true
done
# ---- DEBIAN/control --------------------------------------------
mkdir -p "${DEB_DIR}"
cat > "${DEB_DIR}/control" <<EOF
Package: ${PKG_NAME}
Version: ${VERSION}
Section: base
Priority: optional
Architecture: ${ARCH}
Depends: libjemalloc2
Maintainer: Julio <me@julio.al>
Description: Nginx L7 DDoS Protection (The-World-Is-Yours), built by RAWeb CI for ${TARGET}.
EOF
# ---- DEBIAN/postinst -------------------------------------------
cat > "${DEB_DIR}/postinst" <<"EOFPOSTINST"
#!/bin/bash
# Idempotent: safe on first install, upgrade, and reinstall.
useradd -r -d /usr/local/nginx -s /bin/false nginx 2>/dev/null || true
install -d -o nginx -g nginx -m 0755 \
/usr/local/nginx \
/usr/local/nginx/client_body_temp \
/usr/local/nginx/proxy_temp \
/usr/local/nginx/fastcgi_temp \
/usr/local/nginx/uwsgi_temp \
/usr/local/nginx/scgi_temp \
/var/log/nginx
chown -R nginx:nginx /var/log/nginx /nginx /usr/local/nginx 2>/dev/null || true
systemctl daemon-reload 2>/dev/null || true
systemctl enable nginx.service 2>/dev/null || true
systemctl restart nginx.service 2>/dev/null || true
exit 0
EOFPOSTINST
chmod 755 "${DEB_DIR}/postinst"
dpkg-deb --build "${PKG_DIR}"
cp "${PKG_DIR}.deb" /repo/dist/
# Hand ownership back to the runner UID so the host job can read.
chown $(stat -c "%u:%g" /repo) /repo/dist/$(basename "${PKG_DIR}.deb")
# Stash version for the publish step.
echo "${PKG_NAME}_${VERSION}_${ARCH}.deb" > /repo/dist/${TARGET}.name
echo "${VERSION}" > /repo/dist/${TARGET}.version
echo "${PKG_NAME}" > /repo/dist/${TARGET}.pkg
'
# Surface the artifact paths for the next step.
DEB_FILE="$PWD/dist/$(cat dist/${TARGET}.name)"
PKG_NAME="$(cat dist/${TARGET}.pkg)"
VERSION="$(cat dist/${TARGET}.version)"
{
echo "deb_file=${DEB_FILE}"
echo "version=${VERSION}"
echo "pkg_name=${PKG_NAME}"
} >> "$GITHUB_OUTPUT"
ls -la "${DEB_FILE}"
sha256sum "${DEB_FILE}"
# ─────────────────────────────────────────────────────────────────────────
# Publish to Nexus (runs on the runner host, not in the build container).
# Same security posture as the previous workflow:
# * tmpfs scratch dir for credentials
# * trap covers EXIT INT TERM HUP
# * netrc auth (no -u user:pass on cmdline → no /proc leak)
# * NEXUS_HOST derived from NEXUS_URL so forks don't have to edit YAML
# The matrix-driven secret indirection picks the right per-distro repo.
# ─────────────────────────────────────────────────────────────────────────
- name: Publish to Nexus (${{ matrix.target }})
env:
NEXUS_USER: ${{ secrets.NEXUS_USER }}
NEXUS_PASS: ${{ secrets.NEXUS_PASS }}
NEXUS_URL: ${{ secrets.NEXUS_URL }}
NEXUS_REPO: ${{ secrets[matrix.nexus_repo_secret] }}
DEB_FILE: ${{ steps.pkg.outputs.deb_file }}
PKG_NAME: ${{ steps.pkg.outputs.pkg_name }}
TARGET: ${{ matrix.target }}
run: |
set -euo pipefail
umask 077
SECDIR="$(mktemp -d -p /dev/shm twiy-XXXXXXXX 2>/dev/null \
|| mktemp -d -t twiy-XXXXXXXX)"
chmod 700 "$SECDIR"
cleanup() {
find "$SECDIR" -type f -exec shred -uz {} + 2>/dev/null || true
rm -rf "$SECDIR"
}
trap cleanup EXIT INT TERM HUP
NEXUS_HOST="$(printf '%s' "$NEXUS_URL" | awk -F/ '{print $3}')"
printf 'machine %s login %s password %s\n' \
"$NEXUS_HOST" "$NEXUS_USER" "$NEXUS_PASS" > "$SECDIR/netrc"
unset NEXUS_USER NEXUS_PASS
# Replace the prior version of this same package in this same repo,
# if any. Best-effort: missing prior is not an error. (apt-hosted
# repos in Nexus retain every upload otherwise.)
OLD_ID="$(curl -fsS --netrc-file "$SECDIR/netrc" \
"$NEXUS_URL/service/rest/v1/components?repository=$NEXUS_REPO" \
| PKG_NAME="$PKG_NAME" python3 -c '
import sys, json, os
for c in json.load(sys.stdin).get("items", []):
if c.get("name") == os.environ["PKG_NAME"]:
print(c["id"]); break
' || true)"
if [ -n "$OLD_ID" ]; then
curl -fsS -X DELETE --netrc-file "$SECDIR/netrc" \
"$NEXUS_URL/service/rest/v1/components/$OLD_ID" -o /dev/null
fi
HTTP="$(curl -sS --netrc-file "$SECDIR/netrc" \
-o "$SECDIR/upload.body" -w '%{http_code}' \
-X POST -F "apt.asset=@$DEB_FILE" \
"$NEXUS_URL/service/rest/v1/components?repository=$NEXUS_REPO")"
case "$HTTP" in
201|204) echo "[$TARGET] uploaded $(basename "$DEB_FILE") to $NEXUS_URL/repository/$NEXUS_REPO/" ;;
*) echo "[$TARGET] upload failed (HTTP $HTTP)"; head -c 400 "$SECDIR/upload.body"; exit 1 ;;
esac