Forgejo at https://forgejo.argentwolf.org/alan/activity-relay is the
repository and release authority. GitHub is a downstream public mirror and
independent-validation surface; mirrored commits or tags must not cause GitHub
to publish release artifacts or mutable container tags.
The current GitHub release workflow is deliberately manual and validation-only. Forgejo owns the manual canonical release-artifact build and the authoritative exact-byte publication path proven during the 3.0 RC cycle. The build accepts only an exact reviewed commit and version and does not publish by itself. Do not tag a new release until that workflow has passed on the exact release-preparation commit, its retained artifact set has been inspected, and the accepted bytes are identified for publication. Do not tag merely to exercise either workflow.
- Patch release: backward-compatible fixes, for example
2.5.1. - Minor release: backward-compatible features, for example
2.6.0. - Major release: incompatible configuration, API, storage, protocol, or identity changes.
- Release candidate: append
-rcN, for examplev2.6.0-rc1.
Prerelease tags publish only their complete prerelease container tag. They do
not move latest, major, or major/minor tags.
The workflow derives the Debian version series from the tag and preserves the
top changelog version when it already belongs to that series. Debian packaging
metadata and application identity are intentionally distinct: Debian revisions
and prerelease ordering syntax must never leak into relay --version,
/status.json, or activity_relay_build_info. The stable v3.0.0 release uses:
3.0.0-1
A future prerelease such as v2.6.0-rc1 uses a Debian series such as
2.6.0~rc1-1. If the top changelog entry does not match the tag series, the
workflow starts that series at Debian revision -1.
Release filenames replace ~ with - in public artifact filenames; the
package's internal Debian version is unchanged.
- Work from a clean
masterbranch. - Review
AGENTS.md,ARCHITECTURE.md, andTODO.md. - Update
CHANGELOG.md,readme.md, affected deployment documentation, and the versioned release notes underdocs/releases/. - Confirm no local notes, generated packages, suspicious filenames, or machine-specific paths are tracked.
- Validate the Compose preflight behavior with missing, directory-valued,
and valid
actor.pemfiles. - Run
gofmton changed Go files. - Run
go vet ./.... - Run Redis-backed
go test -count=1 -p 1 ./.... - Run Redis-backed
go test -race -count=1 -p 1 ./.... - Run
python3 -m unittest discover -s contrib/web -p 'test_*.py'. - Run
python3 -m unittest discover -s contrib/ops -p 'test_*.py'. - Validate the Caddy example when it changes.
- Run
git diff --check. - Build the local container and verify:
/usr/bin/relay;/usr/share/activity-relay/web/build-site.py;- the compiled version string.
- Build the native Debian package, require the intended
nobledistribution in the.changesmetadata, and run Lintian error validation on the binary.deb. - Smoke-test
/actor,/nodeinfo/2.1, and/status.json. - Complete the applicable container and native-package matrix in
docs/INTEGRATION-TESTING.md, retaining external evidence for the exact commit and artifacts. - For task-transport changes, run the interruption matrix and confirm:
- queued work survives pre-claim downtime;
- graceful shutdown completes and acknowledges active work;
- lease expiry recovers work after abrupt termination;
- remote success followed by local pre-acknowledgement failure produces the expected at-least-once duplicate rather than silent loss;
- delayed retries survive worker downtime; and
- final ready, delayed, claim-payload, and claim-lease state is empty.
- Confirm the exact Machinery pseudo-version and commit in
go.modmatch the revision covered by local, CI, container, and package evidence. - For publisher/fan-out changes, verify a real accepted publisher activity reaches a receiving server.
- Before stable promotion from a release candidate, complete a production soak of the exact candidate application code. Classify every observed failure as release-caused, receiver-caused, or environmental, and retain the evidence outside the public repository.
- Commit and push the release preparation to the authoritative Forgejo repository.
- Run the manual canonical Forgejo release-artifact workflow against the exact reviewed commit and version. Inspect its public artifacts and retained evidence, and verify every checksum.
- Confirm an exact-byte publication path exists for that artifact set. Do not rebuild the Debian package or OCI archive after acceptance. If exact-byte promotion is not available, stop.
- Create and push the annotated tag to Forgejo only after the accepted canonical bytes are identified unambiguously.
- Publish those exact accepted bytes and only the reviewed versioned release notes for that artifact set.
- Verify the canonical published release, checksums, package metadata, packaged examples, container manifests, and downstream mirror propagation.
Use a disposable Redis instance:
docker rm -f activity-relay-release-test-redis \
>/dev/null 2>&1 || true
docker run \
--detach \
--rm \
--name activity-relay-release-test-redis \
--publish 127.0.0.1:6381:6379 \
redis:7-alpine
until docker exec activity-relay-release-test-redis redis-cli ping |
grep -qx PONG
do
sleep 1
done
REDIS_URL='redis://127.0.0.1:6381' \
go test -count=1 -p 1 ./...
REDIS_URL='redis://127.0.0.1:6381' \
go test -race -count=1 -p 1 ./...
go vet ./...
python3 -m unittest discover \
-s contrib/web \
-p 'test_*.py'
python3 -m unittest discover \
-s contrib/ops \
-p 'test_*.py'
docker run --rm \
--volume "$PWD/contrib/caddy/Caddyfile.example:/etc/caddy/Caddyfile:ro" \
caddy:2.11.2-alpine \
caddy validate \
--config /etc/caddy/Caddyfile \
--adapter caddyfile
docker rm -f activity-relay-release-test-redisBuild and inspect the container using the application version represented by the current Debian changelog entry:
DEB_VERSION="$(dpkg-parsechangelog -SVersion)"
APP_VERSION="${DEB_VERSION%%-*}"
APP_VERSION="${APP_VERSION//\~/-}"
ACTIVITY_RELAY_VERSION="$APP_VERSION" \
docker compose \
-f compose.yml \
-f compose.build.yml \
build
docker run \
--rm \
activity-relay:local \
--version
docker run \
--rm \
--entrypoint /bin/sh \
activity-relay:local \
-c '
test -x /usr/bin/relay
test -f /usr/share/activity-relay/web/build-site.py
test -f /usr/share/activity-relay/web/site.json.example
test -x /usr/bin/activity-relay-resource-guard
test -x /usr/lib/activity-relay/resource-guard.py
'Build and inspect the Debian package:
DEB_VERSION="$(dpkg-parsechangelog -SVersion)"
DEB_UPSTREAM="${DEB_VERSION%-*}"
APP_VERSION="${DEB_UPSTREAM//\~/-}"
ARCH="$(dpkg --print-architecture)"
dpkg-buildpackage \
--build=binary \
--no-sign
changes="../activity-relay_${DEB_VERSION}_${ARCH}.changes"
package="../activity-relay_${DEB_VERSION}_${ARCH}.deb"
grep --quiet '^Distribution: noble$' "$changes"
lintian --fail-on error "$package"
extract="$(mktemp -d)"
trap 'rm -rf "$extract"' EXIT
dpkg-deb -x "$package" "$extract"
test "$("$extract/usr/bin/relay" --version)" = "relay version $APP_VERSION"Before creating a release tag, dispatch Canonical Release Artifacts from Forgejo on the exact release-preparation commit. Supply:
expected_commit: <full 40-character reviewed commit>
version: X.Y.Z or X.Y.Z-rcN
confirm: BUILD <the exact version above>
The versioned release-notes file must record both:
- Application version: `X.Y.Z` or `X.Y.Z-rcN`
- Debian package version: `X.Y.Z-1` or `X.Y.Z~rcN-1`
The workflow must produce one retained deterministic
activity-relay-canonical.tar containing the complete public/ and evidence/
trees. Verify public/SHA256SUMS, inspect BUILD-METADATA.txt, the Debian
metadata and Lintian output, and the OCI index evidence. The OCI archive must
contain both linux/amd64 and linux/arm64, and both exact archived images must
report the requested application version when executed.
Forgejo's outer Actions artifact wrapper is transport only. It may rewrite ZIP
entry timestamps and other metadata, so it is not a release identity or checksum
authority. The deterministic tar carrier sorts members, normalizes UID/GID to
zero, and sets member mtimes to the source_date_epoch already recorded in
public/BUILD-METADATA.txt. Existing file modes are preserved. The workflow
builds the carrier twice and requires byte identity before upload; the packaging
script re-extracts the carrier and revalidates public/SHA256SUMS before
returning success.
After downloading a Forgejo Actions artifact, extract its outer wrapper and then
extract activity-relay-canonical.tar. Perform release acceptance and later
publication from the resulting public/ assets rather than directly from the
outer wrapper.
Passing this workflow means the canonical release bytes were built and retained; it does not mean they were tagged or published. Those are later gates.
Do not create a new release tag until the canonical Forgejo release workflow has passed on the exact release-preparation commit, the retained bytes have been inspected, and the exact-byte publication path has been proven. Then tag only the exact reviewed Forgejo commit:
git switch master
git pull --ff-only origin master
git tag -s vX.Y.Z -m 'Activity-Relay vX.Y.Z'
git push origin vX.Y.ZPromote a validated release candidate by creating a new stable preparation
commit and a new stable vX.Y.Z tag. Never move or rename the published RC tag.
For a future release candidate, use its complete vX.Y.Z-rcN tag and matching
versioned release-notes file. Do not move an already-published release tag;
correct mistakes with another release candidate or patch release.
Forgejo push and pull-request CI is authoritative for source validation. It runs the serialized Redis-backed Go and race suites, Python tests, container and Caddy validation, and Debian package build/lint checks on the managed Forgejo runner pool.
The GitHub mirror retains independent source/container validation. Its former release workflow is manual and validation-only: it may validate an existing tag and retain ephemeral Actions artifacts, but it does not create GitHub Releases, push container images, or move mutable container tags.
.forgejo/workflows/release.yml is the manual canonical release build. It
requires the exact reviewed commit, application version, and explicit BUILD <version> confirmation; re-runs source/package/container validation; emits one
checksummed Debian package, CycloneDX SBOM, multi-architecture OCI archive,
release-note copy, build metadata, and retained evidence tree; wraps that tree in
one deterministic metadata-preserving tar carrier; and publishes nothing
externally. The 3.0 RC1 and RC2 cycles exercised the exact-byte build/publication
model; current releases additionally protect canonical file metadata from the
Forgejo artifact transport layer.
curl --fail --silent --show-error \
http://127.0.0.1:8080/actor \
>/dev/null
curl --fail --silent --show-error \
http://127.0.0.1:8080/nodeinfo/2.1 \
>/dev/null
curl --fail --silent --show-error \
http://127.0.0.1:8080/status.json |
python3 -m json.tool