The build date stored in version.h is now the single time source for a
release. Each package workflow converts it to SOURCE_DATE_EPOCH and passes
that into the build containers, the debian changelog entry is stamped from it
rather than from the build clock, and rpmbuild is told to use it as the build
time and to clamp file mtimes to it.
rpm also records the build host, which is a container id and therefore differs
on every run, so pin it as well - otherwise the timestamps alone do not make
the package reproducible.
Rebuilding the same commit now produces byte identical packages, verified for
both formats by building twice and comparing checksums.
In the version workflow the clock is read exactly once, when a new build date
is minted. YEAR3PROXY is derived from the build date instead of being read
separately, so the two cannot straddle a year boundary and a re-run in a later
year no longer rewrites version.h.
The version files were derived from RELEASE by a script kept outside this
repository, so bumping RELEASE and updating src/version.h, the spec version
and debian/changelog were separate manual steps.
Do it in a workflow triggered by a change to RELEASE. It writes the same
content the external script produced, verified byte for byte against the
current tree.
The build date is now minted once, when RELEASE changes, and stored in
version.h rather than taken from the clock of whichever build is running. The
package workflows read it from there, so rebuilding a commit produces the same
version string and the same package file names. Re-running the workflow
without a RELEASE change keeps the existing build date and makes no commit.
The rpm build runs in a container as root, so the packages arrived in the
workspace owned by root. Renaming them still worked, since that only needs
write permission on the directory, but rpm --addsign rewrites the file in
place and failed:
error: 3proxy-0.9.9.0.el9.x86_64.rpm: open failed: Permission denied
Restore the invoking user's ownership before leaving the container, falling
back to a mode change where the container cannot chown, as under a rootless
container runtime.
dpkg-buildpackage ran on the runner itself, so the packages inherited its ABI.
On ubuntu-latest that means glibc 2.38 and libssl3t64, which restricts the
packages to Ubuntu 24.04 and newer - Debian 12, Debian 13 and Ubuntu 22.04
cannot install them.
Nothing in the source needs glibc 2.38. The floor comes from the build host:
gcc 13 with glibc 2.38 redirects sscanf and strtol to __isoc23_ variants, and
arc4random arrived in glibc 2.36.
Build in an Ubuntu 22.04 container instead. That lowers the dependencies to
libc6 (>= 2.34) and libssl3, and libssl3t64 declares Provides: libssl3, so one
package covers Ubuntu 22.04 and later as well as Debian 12 and later. Verified
by installing on all four.
The armhf cross build moves into the same container, keeping the cross
toolchain on PATH so that install -s uses the ARM strip rather than the host
one.
rpmbuild ran directly on the Ubuntu runner, so the packages carried Ubuntu's
ABI: glibc 2.38, libssl.so.3 and libpcre2-8. No RPM distribution can satisfy
that - RHEL 9 has glibc 2.34, RHEL 8 has 2.28 and OpenSSL 1.1 - so the rpms
were installable nowhere.
Build them in AlmaLinux 8, 9 and 10 containers instead, covering supported
AlmaLinux and CentOS Stream releases. Release: 1%{?dist} already in the spec
now expands, so the packages are named el8, el9 and el10 and can coexist.
Two spec changes are needed for a build outside Ubuntu:
debug_package is disabled. Enterprise Linux enables debuginfo extraction by
default while Ubuntu's rpm does not; the build produces no debug sources, so
rpmbuild aborted on an empty debugsourcefiles.list.
/bin/3proxy is excluded from automatic requires. The installed configuration
file is executable and begins with #!/bin/3proxy, so rpm generates a
dependency on it. rpm 4.16 and later canonicalise that to /usr/bin/3proxy
while %files declares /bin/3proxy, leaving a dependency that can never be
satisfied and making the package uninstallable on el9 and el10.
Enterprise Linux has no 32-bit ARM, so the armhf workflow now builds only the
deb package.
The release key is ed25519, so rpm stores the header signature in
DSAHEADER (EdDSA/SHA256) and RSAHEADER is empty. The verification only
looked at RSAHEADER and failed correctly signed packages.
Do not rely on the gpg-agent passphrase cache surviving between steps:
the signing command gets the passphrase from a file. A test signature is
made first, so a wrong passphrase or a key which can not sign fails with
a clear gpg error instead of an rpm which is silently left unsigned.
On failure the rpm version, the sign command and the key capabilities are
printed.
rpm on Debian/Ubuntu defaults %__gpg to /usr/bin/gpg2, which does not exist,
so rpm --addsign failed with 'Could not exec gpg' and packages were left
unsigned.
GPG signing of rpm packages and detached signatures for deb packages and
for the SHA256SUMS files, using GPG_PRIVATE_KEY / GPG_PASSPHRASE secrets.
Checksums are published as release assets.
Build provenance is attested before the assets are uploaded, so a failed
attestation does not leave unattested files published.
Workflow permissions are read-only, write permissions are requested per
job only where they are used.
Docker: single workflow, one job per image per platform instead of one
workflow per registry. Platforms are built in parallel and pushed by
digest, then combined into a manifest list pushed to Docker Hub and GHCR
at once, so both registries get identical digests. arm64 and arm/v7 build
on native arm runners, ppc64le is dropped.
Registry provenance/sbom attestations are disabled (they were shown as
unknown/unknown entries in the registries), build provenance is attested
with actions/attest-build-provenance instead and is verifiable with
'gh attestation verify oci://...'. Release binaries (rpm, deb, zip) are
attested the same way. cosign version is pinned and images are signed by
digest.
Release images built from the 0.9 branch are tagged lts / lts-busybox /
lts-minimal instead of latest / busybox / minimal, so master keeps the
latest tags. Version tags are unchanged.