Release pipeline: sign, config rename, packaging fixes, doctor output, dupload #54

Merged
heiko merged 32 commits from dev into master 2026-05-17 00:15:24 +02:00
Owner

Summary

  • sign: typed SignMode enum (auto/on/off); auto-detect from git user.signingkey; .sig extension for sidecar files
  • config: rename destinations (repo_release→releases, repo_packages→packages); add dupload built-in; rename blobs→blob; unify bool-accessor naming to Is- prefix
  • dupload: wire Publish.Blob; fix Architecture field in .changes when source files are present (reprepro rejection); conditional --to flag
  • lintian: fix 6 lintian errors in generated deb/source packages
  • doctor: restructure output groups; drop blank-line separators; warn on unclean worktree
  • pack/deb: overlay debian/ dir into binary and source packages
  • cli: drop release command alias; only publish remains
  • lint: fix all 41+ golangci-lint issues (wsl_v5, revive, errcheck, unused) — 0 issues reported

Test plan

  • golangci-lint run reports 0 issues
  • go test ./... — all 27 packages pass
  • reprepro accepts .changes with Architecture: amd64 source for mixed packages
  • End-to-end: gogogo publish for a real module against staging Forgejo instance

🤖 Generated with Claude Code

## Summary - **sign**: typed `SignMode` enum (`auto`/`on`/`off`); auto-detect from `git user.signingkey`; `.sig` extension for sidecar files - **config**: rename destinations (`repo_release→releases`, `repo_packages→packages`); add `dupload` built-in; rename `blobs→blob`; unify bool-accessor naming to `Is-` prefix - **dupload**: wire `Publish.Blob`; fix `Architecture` field in `.changes` when source files are present (reprepro rejection); conditional `--to` flag - **lintian**: fix 6 lintian errors in generated deb/source packages - **doctor**: restructure output groups; drop blank-line separators; warn on unclean worktree - **pack/deb**: overlay `debian/` dir into binary and source packages - **cli**: drop `release` command alias; only `publish` remains - **lint**: fix all 41+ golangci-lint issues (wsl_v5, revive, errcheck, unused) — 0 issues reported ## Test plan - [x] `golangci-lint run` reports 0 issues - [x] `go test ./...` — all 27 packages pass - [x] reprepro accepts `.changes` with `Architecture: amd64 source` for mixed packages - [ ] End-to-end: `gogogo publish` for a real module against staging Forgejo instance 🤖 Generated with [Claude Code](https://claude.com/claude-code)
All three series (C/B/E) are merged to master.

ai:claude-sonnet-4-6
Co-Authored-By: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
Replace bool --no-sign with --sign string (default: auto).
Auto mode signs only when git user.signingkey is configured,
matching the existing --gpg-sign behaviour in the tag command.
--sign=true always signs (errors if gpg absent).
--sign=false skips signing entirely.

ai:claude-sonnet-4-6
Co-Authored-By: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
--sign is now a pure boolean flag (absent = auto-detect via
git user.signingkey, --sign = always sign, --sign=false = skip).
The "auto" string is no longer an accepted explicit value; the
zero-value Sign="" in Flags triggers the auto path internally.

publishFromState returns an early error when --sign is set but
the saved pack state contains no .sig files, directing the user
to re-run `gogogo pack --sign` first.

ai:claude-sonnet-4-6
Co-Authored-By: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
Replace stringly-typed Sign string with SignMode (SignAuto/SignOn/SignOff),
eliminating the runtime validation branch and making invalid states
unrepresentable at the type boundary.

Reorder signBinaryAssets: filter targets before the mode switch so the
git subprocess is skipped for deb/rpm-only releases with no blob assets.

Replace manual length arithmetic in isSignatureFile with strings.HasSuffix.
Replace hasSig for/break loop in publishFromState with slices.ContainsFunc.
Remove noise comment from empty SignOn switch case.

ai:claude-sonnet-4-6
Co-Authored-By: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
All config boolean helpers now follow the Is-prefix convention:
IsEnabled, IsSystemdEnabled, IsLintianEnabled on Deb; IsEnabled on RPM
and Source. Also updates doctor check ordering so project config and
workspace checks run before VCS/remote and binary tool checks, and
replaces raw DerefBool call-sites with the new IsEnabled methods.

ai:claude-sonnet-4-6

Co-Authored-By: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
gogogo pack with prior StagePack state now always re-runs packaging (skipping
only the build), mirroring how gogogo build always rebuilds. Previously it
returned nil immediately, silently doing nothing — even when the state was
incomplete (e.g. Deb=[]).

Also fixes a pre-existing bug where copyAssetsToOutdir would truncate and
delete blob files when src and dst resolved to the same path. This bug was
newly reachable via the resume-from-pack path introduced above.

Closes #53.

ius:ai:claude-sonnet-4-6
ius:ai:claude-sonnet-4-6
Reference /usr/share/common-licenses/<license> for Apache-2.0 and other
common Debian licenses instead of embedding the full text. Also add the
copyright year (from ModTime, falling back to current year) so the
Copyright: field satisfies lintian's copyright-without-copyright-notice check.

ius:ai:claude-sonnet-4-6
Fall back to time.Now() when the VCS commit timestamp is unavailable,
instead of letting the zero time propagate to Unix epoch in the archive.
Epoch-dated files trigger lintian's package-contains-ancient-file error.

ius:ai:claude-sonnet-4-6
Use gzip.BestCompression (level 9) for changelog.Debian.gz.

ius:ai:claude-sonnet-4-6
Rename changelog.Debian.gz to changelog.gz. All gogogo-generated packages
are native (no -N Debian revision suffix), so the Debian policy name
for the changelog is changelog.gz.

ius:ai:claude-sonnet-4-6
Filter KindDebSrc assets to only .dsc files before calling lintian.
Source tarballs (.tar.gz) are not valid lintian targets; only .deb and
.dsc files are. Also adds a test that tarballs are excluded.

ius:ai:claude-sonnet-4-6
Two fixes:
- FormatDeb: fall back to stripping the v-prefix when DebVersion rejects
  the tag (pseudo-versions and pre-formatted Debian versions are not
  valid semver, causing the version field to be empty).
- buildChangelogGz: use spec.ModTime for the placeholder entry date
  instead of hardcoded epoch, so the changelog does not contain a
  1970 timestamp.

ius:ai:claude-sonnet-4-6
Replace gitSigningKeyConfigured (bool) with gitResolveSigningKey (string, bool)
so the key identifier (user.signingkey value or user.email) is available for
display. Verbose output now prints "sign: signing key: <id>" instead of the
generic "signing key found ... proceeding" message.

ius:ai:claude-sonnet-4-6
Old names are kept as back-compat aliases (BuiltInDestination accepts both).
Also allows "default" as a list element in publish.* routes, matching the
scalar form.

ius:ai:claude-sonnet-4-6
- Rename Artifacts.Blobs→Blob and Publish.Blobs→Blob (yaml: blobs→blob)
  to match the output directory layout (blob/, deb/, rpm/).
- Add "releases" as the canonical built-in for Forgejo releases
  (old "release"/"repo_release" aliases kept for back-compat).
- Add "dupload" as a built-in destination (type: dupload, no host).
- Remove the dupload host validation guard; host is now optional and
  dupload uses its own default when unset.
- Update default.yml, migrate.go, and .gogogo.conf to reflect the new
  names and examples.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ius:ai:claude-sonnet-4-6
Update Publish.Blobs→Blob callsite and default destination name
("release"→"releases") in the publish stage. Make runDupload emit
--to only when host is non-empty, consistent with the relaxed
validation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ius:ai:claude-sonnet-4-6
Move the license check after the VCS/repo group so the output
follows the natural repo → license → tools order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ius:ai:claude-sonnet-4-6
Reorder Check() into three visual groups separated by blank lines:
  1. repo (remote reachability + upload settings) + license
  2. config (success entry with filename) + module/outdir (fail only) + git + go
  3. conditional packaging/upload tools (lintian, dupload, etc.)

Changes:
- Decouple git binary lookup from its output entry so repo appears first.
- Add ok("config", configFile) success entry; file name comes from new
  Options.ConfigFile (defaults to ".gogogo.conf").
- Add Entry.Separator / Report.sep() for blank-line group dividers;
  PrintTo() emits a blank line for separator entries.
- Suppress silent ok entries from checkModulePath and checkOutdirIgnored
  — only failures/warnings are shown, keeping the happy path clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ius:ai:claude-sonnet-4-6
- TestPrintTo_SeparatorEmitsBlankLine: separator entries produce blank lines.
- TestCheck_ConfigSuccessEntry: ok("config", …) appears when validation passes.
- TestCheck_ConfigSuccessEntryUsesOptsConfigFile: ConfigFile option is honoured.
- TestCheck_OutputGroupOrder: repo/license before first separator; config/git/go
  between separators; at least two group separators present.
- Update TestCheck_ModulePathMatches and TestCheck_OutdirIgnoredOK to
  reflect that silent-OK entries are no longer emitted for those checks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ius:ai:claude-sonnet-4-6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ius:ai:claude-sonnet-4-6
Remove Entry.Separator, Report.sep(), and the separator handling in
PrintTo. Update TestCheck_OutputGroupOrder to verify ordering by entry
index rather than separator positions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ius:ai:claude-sonnet-4-6
- Publish route comments now name the actual default destination and
  reference the API type ("via the Forgejo release API" / "packages API")
  instead of the vague "default".
- Document "none" as the future keyword for suppressing publishing
  alongside the existing [] notation.
- Doctor dupload entry now shows the effective host or "<auto>" when
  no host is configured, so the user can confirm the target at a glance.
- runDupload logs the effective host (or "<auto>") at verbose level.

ius:ai:claude-sonnet-4-6
Adds Repo.IsClean() to the git package (runs git status --porcelain)
and a checkWorkdirClean check to the doctor VCS group. The check emits
a warning when the tree is dirty, skipping silently when a non-HEAD
commit is specified (--commit) since local state is irrelevant there.

ius:ai:claude-sonnet-4-6
Reads maintainer scripts, conffiles, and dependency fields from a
debian/ directory in the source tree, merging them with config-derived
values. Generated files (control, rules, changelog, copyright,
source/format) are never overwritten by the overlay.

ius:ai:claude-sonnet-4-6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The publish subcommand is the canonical name; release was a legacy
alias. Remove newReleaseCmd, its registration in root.go, and all
references in the test suite.

ius:ai:claude-sonnet-4-6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
reprepro rejects uploads where the .changes Architecture header does not
list all types present in Files. When a .changes bundles both binary debs
and source files (.dsc, tarball), the field must be e.g. "amd64 source"
not just "amd64".

Replace the single-arch detection with changesArchField, which collects
all binary architectures and appends "source" when any .dsc or tarball
is listed.

ius:ai:claude-sonnet-4-6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolves all 41+ wsl_v5, revive, errcheck, and unused violations
across the codebase so golangci-lint reports 0 issues.

ius:ai:claude-sonnet-4-6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Splice a fixed-width green/grey bar between the upload message and the
byte counter when stderr is a colour-capable TTY. Honours NO_COLOR and
TERM=dumb; non-TTY output (pipes, log captures) is byte-identical to
the previous plain-text format.

ius:ai:claude-opus-4-7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spell out the rule directly in CLAUDE.md so it surfaces in the active
context window — no detour to the linter config or wsl_v5 README.

ius:ai:claude-opus-4-7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add pointer to README.md for user-facing docs.
- Drop the removed "release" alias from the subcommand list and note
  that build/pack/publish all live in cmd/release.
- Rewrite the Progress section to match current code: stderr (not
  stdout), no spinner, Group for stacked concurrent rows, Transfer
  for byte counter, and the new colour-bar gate ColorEnabled.

ius:ai:claude-opus-4-7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two follow-ups to the pre-merge review of #54:

* Code comment on the mod-cache mtime fallback explains why now() is
  used instead of the unix epoch (lintian rejects 1970-01-01), records
  the reproducibility trade-off, and points at a future fix using
  `go list -m -json` whose Time field is the proxy's publish timestamp.
* README mirrors the trade-off in the "How it works" section so users
  know the clone-based default remains reproducible while the
  --local / no-repo-config path doesn't.
* New TestNewDebSpec_OverlaysDebianDir asserts the debian/ overlay
  pipes both the maintainer script (postinst) and the runtime
  relationship fields (Depends, Recommends) through to the final
  deb.Spec, merged with YAML-declared values.

ius:ai:claude-opus-4-7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
heiko left a comment

Self-merge after pre-merge review.

Follow-ups added before merge:

  • TestNewDebSpec_OverlaysDebianDir covers the new debian/ overlay end-to-end (postinst script + Depends merge).
  • mod-cache mtime fallback now documented in code (with a go list -m -json future-fix note) and in README.

Known follow-up not in this PR: the blobs → blob config rename should get a release-note entry or a deprecated-alias UnmarshalYAML; tracked separately.

Self-merge after pre-merge review. Follow-ups added before merge: - TestNewDebSpec_OverlaysDebianDir covers the new debian/ overlay end-to-end (postinst script + Depends merge). - mod-cache mtime fallback now documented in code (with a `go list -m -json` future-fix note) and in README. Known follow-up not in this PR: the `blobs → blob` config rename should get a release-note entry or a deprecated-alias UnmarshalYAML; tracked separately.
heiko merged commit caa8b3e4fe into master 2026-05-17 00:15:24 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
heiko/gogogo!54
No description provided.