feat: speed up API build and gate "latest" on a complete release - #359
Conversation
There was a problem hiding this comment.
Important
The release-assets job should keep a needs dependency on the image build jobs. Removing it lets release assets publish before the Docker images they reference are available, and it removes the failure gate that prevents a broken release from being advertised.
Reviewed changes
services/api/Dockerfile: switched the builder stage toFROM --platform=$BUILDPLATFORM, addedTARGETOS/TARGETARCHcross-compilation, and parameterizedGOMAXPROCS/GOMEMLIMIT/GOBUILD_Pwith conservative local defaults..github/workflows/cd.yml: passed the three build args to the API image build, and removed theneedsblock fromrelease-assets.
Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
The new commit (415de0a) reworks the release sequencing in response to the prior review:
- Restored the
release-assetsfailure gate.release-assetsagainneedsevery image and package job, soinstall.sh,upgrade.sh, and the compose file are only uploaded once every artefact is actually shippable. - Added
hold-release-label/promote-release. The release is demoted from GitHub's "Latest" marker immediately on publish, then re-promoted only after all images, packages, and release assets have succeeded.:latestDocker tags are also applied only at promotion time viadocker buildx imagetools create, so/releases/latest/download/*and:latestimage pulls stay consistent with the previous fully-shipped release until the new one is complete. - Pinned script defaults to the release tag.
install.shandupgrade.share stamped with the exact release tag during the asset upload, so a plainbash install.shnever depends on the floating:latesttag being current. - Updated
AGENT_SERVER_IMAGEhandling in scripts.install.shnow writes the versioned tag, andupgrade.shonly re-pins it when it already points to Paca's own image, preserving custom values.
The cross-compilation / build-arg changes in services/api/Dockerfile are unchanged from the previous review and remain sound.
Kimi K2 (free via Pullfrog for OSS) | 𝕏
…me in edit commands
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
The new commit (fbcf608) makes two small hardening tweaks to the release sequencing in .github/workflows/cd.yml:
- Replaced direct tag interpolation in
gh release editcommands with aTAG_NAMEenvironment variable, so the release tag flows into the shell via a proper env rather than a GitHub Actions context-expression string. - Sequenced the
hold-release-labeljob beforepromote-releaseby adding it topromote-release'sneeds, preventing any possibility of the "Latest" label being re-promoted before the initial demotion has run.
The script and Dockerfile changes from prior reviews are unchanged and remain sound.
Kimi K2 (free via Pullfrog for OSS) | 𝕏

Summary
CD: speed up the API image build. The
services/apibuilder stage now cross-compiles natively (--platform=$BUILDPLATFORM+GOOS=$TARGETOS GOARCH=$TARGETARCH) instead of running the Go compiler under QEMU emulation forlinux/arm64, which was the dominant cost of the ~20 minute build.GOMAXPROCS/GOMEMLIMIT/-pare now build-args (defaulting to the original single-core/768MiB values for local and e2e builds) thatcd.ymloverrides to4cores /6GiBfor CI.CD: never expose a half-shipped release. Previously
release-assets(which uploadsinstall.sh,upgrade.sh,docker-compose.yml, etc.) depended on every image/package job, so those files simply didn't exist for the ~20-30 minutes a release was building, and GitHub already marks a release "Latest" the instant it's published — so/releases/latest/download/*404'd, and any Docker:latestpull could land on a stale mix of old/new images depending on which per-service job happened to finish first. Fixed with three coordinated pieces:hold-release-label(new, runs first) immediately demotes the release off GitHub's "Latest" marker, so/releases/latestkeeps resolving to the previous, fully-shipped release for the whole build window.:latestDocker tag at all — only version-pinned tags ({{version}},{{major}}.{{minor}}).promote-release(new, final job, depends on every other job, skipped for pre-releases) retags every image:latestviadocker buildx imagetools create(registry-side copy, no rebuild) and only then marks the GitHub release "Latest" — tags first, label second, so/releases/latestnever points at a release whose:latestimages aren't there yet.release-assetskeeps its originalneedson every image/package job, so it (and the version it stamps into the scripts below) never runs for a release that isn't fully shippable.install.sh / upgrade.sh: pin to a specific version instead of floating
:latest. Both scripts now default toPACA_DEFAULT_VERSION="latest", a single dedicated, comment-flagged line that CD'srelease-assetsjob stamps to the release's own tag before upload (sed -i 's/^PACA_DEFAULT_VERSION="latest"$/PACA_DEFAULT_VERSION="<tag>"/'). A plainbash install.sh/bash upgrade.shdownloaded from a release therefore always targets a version guaranteed to exist, rather than depending on the:latestDocker tag being current.AGENT_SERVER_IMAGE(previously hardcoded to:latestin both scripts) now pins to the same version tag as everything else, including inupgrade.sh's re-pin and old-upstream-image migration logic. The source-tree copies of both scripts keepPACA_DEFAULT_VERSION="latest"so a plain checkout still behaves sensibly, and the header doc comments were reworded to be evergreen instead of needing their own sed pass.Test plan
/releases/latestkeeps pointing at the previous release untilpromote-releaseruns, then flips overpaca-api,paca-realtime,paca-web,paca-ai-agent,paca-agent-server) gets a:latesttag on both GHCR and DockerHub only afterpromote-releasesucceedsinstall.sh/upgrade.shdownloaded from that release havePACA_DEFAULT_VERSIONstamped to the release tag, and that a plain run pinsPACA_API_IMAGE,PACA_WEB_IMAGE,PACA_REALTIME_IMAGE,PACA_AI_AGENT_IMAGE, andAGENT_SERVER_IMAGEto that versionlinux/amd64/linux/arm64images still run correctlydocker compose/ e2e builds ofservices/apiare unaffected (still single-core defaults), andscripts/install.sh/scripts/upgrade.shrun directly from a checkout still default tolatest