name: build-and-publish on: push: branches: [master] workflow_dispatch: jobs: build: runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: target: [trixie, raccoon] include: - target: trixie image: debian:13 distro_dir: Trixie nexus_repo_secret: NEXUS_REPO_TRIXIE nexus_user_secret: NEXUS_USER_TRIXIE nexus_pass_secret: NEXUS_PASS_TRIXIE - target: raccoon image: ubuntu:26.04 distro_dir: Raccoon nexus_repo_secret: NEXUS_REPO_RACCOON nexus_user_secret: NEXUS_USER_RACCOON nexus_pass_secret: NEXUS_PASS_RACCOON container: image: ${{ matrix.image }} steps: - name: Bootstrap run: | apt-get update -qq DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ git ca-certificates nodejs - name: Checkout uses: actions/checkout@v4 - name: Build id: pkg env: TARGET: ${{ matrix.target }} DISTRO_DIR: ${{ matrix.distro_dir }} run: | set -euo pipefail REPO_ROOT="$PWD" # captured before any cd in the build script touch /.dockerenv bash build/${TARGET}.sh new bash build/${TARGET}.sh build bash build/${TARGET}.sh postfix NGINX_VER="$(nginx -v 2>&1 | awk -F/ '{print $2}')" VERSION="${NGINX_VER}-${GITHUB_RUN_NUMBER:-1}~${TARGET}" ARCH="amd64" assemble_deb() { local pkg_name="$1" unit_src="$2" conflicts="$3" local pkg_dir="/opt/${pkg_name}_${VERSION}_${ARCH}" local deb_dir="${pkg_dir}/DEBIAN" mkdir -p "${pkg_dir}/usr/sbin" \ "${pkg_dir}/etc/systemd/system" \ "${pkg_dir}/usr/lib" \ "${pkg_dir}/usr/nginx_lua" \ "${pkg_dir}/usr/share/twiy/defaults/nginx" \ "${pkg_dir}/usr/share/twiy/defaults/hostdata" cp /usr/sbin/nginx "${pkg_dir}/usr/sbin/" # Pristine configs + default site go into a defaults stash, NOT the # live /nginx and /hostdata trees. postinst seeds them from here # without clobbering local edits (writes .new when a target # already exists). The live files stay untracked by dpkg, so an # upgrade never overwrites a customised config. cp -R /nginx/. "${pkg_dir}/usr/share/twiy/defaults/nginx/" || true cp -R /hostdata/default "${pkg_dir}/usr/share/twiy/defaults/hostdata/" || true cp "${unit_src}" "${pkg_dir}/etc/systemd/system/nginx.service" cp -R /usr/nginx_lua "${pkg_dir}/usr/" || true for d in /usr/local/aws-lc /usr/local/LuaJIT /usr/local/modsecurity /usr/local/zlib-ng; do [ -d "$d" ] && cp -R "$d" "${pkg_dir}/usr/local/" || true done mkdir -p "${pkg_dir}/usr/local/lib" cp -R /usr/local/lib/. "${pkg_dir}/usr/local/lib/" 2>/dev/null || true for lib in $(ldd /usr/sbin/nginx | grep '=> /' | awk '{print $3}'); do case "$lib" in /usr/local/*) continue ;; esac cp "$lib" "${pkg_dir}/usr/lib/" || true done mkdir -p "${deb_dir}" printf 'Package: %s\nVersion: %s\nSection: base\nPriority: optional\nArchitecture: %s\nDepends: libjemalloc2, libsystemd0\nConflicts: %s\nReplaces: %s\nMaintainer: Julio \nDescription: Nginx L7 DDoS Protection (%s), built by RAWeb CI for %s.\n' \ "${pkg_name}" "${VERSION}" "${ARCH}" "${conflicts}" "${conflicts}" "${pkg_name}" "${TARGET}" \ > "${deb_dir}/control" # Shared maintainer script: seeds /nginx + /hostdata from the # defaults stash without overwriting files the admin already has. cp "${REPO_ROOT}/build/deb/postinst" "${deb_dir}/postinst" chmod 755 "${deb_dir}/postinst" dpkg-deb --build "${pkg_dir}" } assemble_deb "twiy" "${REPO_ROOT}/static/${DISTRO_DIR}/nginx.service" "twiy-raweb" assemble_deb "twiy-raweb" "${REPO_ROOT}/static/${DISTRO_DIR}/nginx-raweb.service" "twiy" DEB_TWIY="/opt/twiy_${VERSION}_${ARCH}.deb" DEB_RAWEB="/opt/twiy-raweb_${VERSION}_${ARCH}.deb" { echo "deb_twiy=${DEB_TWIY}" echo "deb_raweb=${DEB_RAWEB}" echo "version=${VERSION}" } >> "$GITHUB_OUTPUT" ls -la /opt/twiy*.deb sha256sum /opt/twiy*.deb - name: Publish env: NEXUS_USER: ${{ secrets[matrix.nexus_user_secret] }} NEXUS_PASS: ${{ secrets[matrix.nexus_pass_secret] }} NEXUS_URL: ${{ secrets.NEXUS_URL }} NEXUS_REPO: ${{ secrets[matrix.nexus_repo_secret] }} DEB_TWIY: ${{ steps.pkg.outputs.deb_twiy }} DEB_RAWEB: ${{ steps.pkg.outputs.deb_raweb }} TARGET: ${{ matrix.target }} run: | set -euo pipefail umask 077 apt-get install -y -q --no-install-recommends curl python3 ca-certificates >/dev/null 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 publish_one() { local deb="$1" pkg_name="$2" local old_id 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 local http http="$(curl -sS --netrc-file "$SECDIR/netrc" \ -o "$SECDIR/upload.body" -w '%{http_code}' \ -X POST -F "apt.asset=@$deb" \ "$NEXUS_URL/service/rest/v1/components?repository=$NEXUS_REPO")" case "$http" in 201|204) echo "[$TARGET] uploaded $(basename "$deb")" ;; *) echo "[$TARGET] upload failed for $pkg_name (HTTP $http)"; cat "$SECDIR/upload.body"; exit 1 ;; esac } publish_one "$DEB_TWIY" "twiy" publish_one "$DEB_RAWEB" "twiy-raweb"