feat(release): server-side packaging — attach full-repo + entry_point subtree zips on release [#809] #814

Merged
jmiller merged 2 commits from feature/release-packaging-hook into dev 2026-07-19 02:49:45 +00:00

2 Commits

Author SHA1 Message Date
Moko Consulting 19d498f6c3 fix(release): mark generated artifacts, serialize + reorder attach to prevent data loss [#809]
Universal: Auto Version Bump / Version Bump (push) Successful in 21s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Project CI / Lint & Validate (pull_request) Successful in 43s
Universal: PR Check / Validate PR (pull_request) Successful in 14s
Universal: PR Check / Secret Scan (pull_request) Successful in 1m32s
Generic: Project CI / Tests (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Address data-safety review of the server-side packaging hook:

- DATA LOSS (HIGH): tag generated zips with UploaderID = user_model.ActionsUserID
  (-2), a built-in bot sentinel that cannot collide with human uploads (which
  always use a positive doer ID). Only attachments carrying this marker are ever
  deleted, so a user-uploaded asset sharing a generated name is never destroyed.

- CONCURRENCY (HIGH): serialize the per-release delete+attach sequence with
  globallock.LockAndDo, keyed release_packaging_<id>, matching the forge's
  existing working-lock convention. Attachments are re-read inside the lock.

- DELETE-THEN-FAIL (LOW): reorder to attach-new-then-delete-old — the fresh
  archive is stored under the canonical name first and stale generated copies
  are removed only on success, so a failed regeneration never nets asset loss.

- FILENAME (LOW): sanitize the tag segment (replace path separators) so a tag
  like release/1.0 yields a clean asset name.

- ENTRY_POINT (LOW): normalizeEntryPoint trims a leading slash (git archive
  rejects absolute paths) and treats a now-empty result as root/skip.

- CHECKSUM re-hash cost (MEDIUM): documented as a follow-up at the checksum
  call sites in release.go.

The verified-correct io.Pipe streaming is unchanged.

Refs #809 #812 EPIC #367

Authored-by: Moko Consulting
2026-07-18 16:39:19 -05:00
Moko Consulting 2280f02bec feat(release): server-side packaging — attach full-repo + entry_point subtree zips on release [#809]
Universal: Auto Version Bump / Version Bump (push) Successful in 25s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Project CI / Lint & Validate (pull_request) Successful in 43s
Universal: PR Check / Validate PR (pull_request) Successful in 20s
Universal: PR Check / Secret Scan (pull_request) Successful in 1m31s
Generic: Project CI / Tests (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Add GenerateReleaseArtifacts in services/release/packaging.go, invoked from
CreateRelease and the UpdateRelease publish path (drafts skipped). It streams a
full-repository zip (<repo>-<tag>.zip) and, when repo metadata declares a
non-root entry_point, an entry_point subtree zip (<repo>-<tag>-source.zip) via
gitrepo.CreateArchive through an io.Pipe into attachment_service.NewAttachment,
so archives are never buffered in memory. The helper is idempotent (existing
artifacts of the same name are replaced) and runs before GenerateReleaseChecksums
so each zip receives a .sha256 sidecar automatically.

Refs #809 #812 EPIC #367

Authored-by: Moko Consulting
2026-07-18 16:08:20 -05:00