Skip to content

Replace GoReleaser with Make + snapcraft pipeline (keep Snap publishing)#776

Draft
Copilot wants to merge 8 commits into
masterfrom
copilot/introduce-makefile-remove-external
Draft

Replace GoReleaser with Make + snapcraft pipeline (keep Snap publishing)#776
Copilot wants to merge 8 commits into
masterfrom
copilot/introduce-makefile-remove-external

Conversation

Copilot AI commented May 4, 2026

Copy link
Copy Markdown
Contributor

Drops GoReleaser in favor of a plain Make + Bash release pipeline, while preserving the Snap publishing flow that was previously driven by goreleaser's snapcrafts: block.

Release pipeline (scripts/build-release.sh)

  • make release-bins — linux binaries come from the Dockerfile's bin-export stage via make docker-bins (byte-identical to the runtime image); windows/darwin are cross-compiled with -trimpath -ldflags "-s -w".
  • make release-archives — preserves the legacy sshpiperd_with_plugins_<os>_<arch>.{tar.gz,zip} naming so existing download URLs keep working; archives include README/LICENSE for parity with goreleaser's defaults.
  • make release-checksums — writes dist/checksums.txt covering archives and .snap files.

Snap (snap/snapcraft.yaml.in, snap/hooks/configure, scripts/build-snap.sh)

  • make snap — for each arch, builds a per-arch prime dir from the same docker-extracted linux binaries plus a cross-compiled launcher (with go generate running configgen for configentry.txt), renders the snapcraft.yaml template, and runs snapcraft pack --destructive-mode.
  • make snap-push — uploads via snapcraft upload --release=<channels> using SNAPCRAFT_STORE_CREDENTIALS.
  • The configure hook now lives at the snapcraft-conventional snap/hooks/configure (snapcraft auto-wraps it with the declared plugs); the old copy under cmd/sshpiperd/snap/hooks/ is removed.

Workflows

  • release.yaml rewritten: drops goreleaser/goreleaser-action, runs make releasemake snap → (conditionally) make snap-pushmake docker-push. The snap-push step is gated on SNAPCRAFT_STORE_CREDENTIALS so forks without store access still succeed.
  • test.yml: replaces the goreleaser release --snapshot --clean smoke test with make release + make snap.

Removed / updated

  • Deleted .goreleaser.yaml, scripts/goreleaser-replace-with-docker-bin.sh, and the duplicate cmd/sshpiperd/snap/hooks/configure.
  • Updated comments in Dockerfile, Makefile, .github/copilot-instructions.md; expanded .gitignore for snap build state (snap/prime-*, snap/snapcraft.yaml, snap/parts/, snap/stage/).

Linux binaries continue to be the single source of truth produced by the Dockerfile, so the bytes shipped in the runtime image, the GH release archives, and the .snap files remain identical.

Copilot AI and others added 2 commits May 4, 2026 17:15
@tg123

tg123 commented May 4, 2026

Copy link
Copy Markdown
Owner

@copilot could you please make binaries exactly the same in docker image and gh release? e.g. cp from docker or other solution

Copilot AI commented May 4, 2026

Copy link
Copy Markdown
Contributor Author

@copilot could you please make binaries exactly the same in docker image and gh release? e.g. cp from docker or other solution

Done in 51b0afb. Restored the EXTERNAL=1 branch in Dockerfile so the image cps GoReleaser's dist/ binaries in (instead of building from source separately), and brought back dockers: / docker_manifests: in .goreleaser.yaml so the same artifacts feed both the GH release archives and the multi-arch image push. Verified locally with goreleaser release --snapshot --clean: sshpiperd and plugins/fixed extracted from farmer1992/sshpiperd:full-v0.0.1-amd64 have byte-identical sha256s to the ones in dist/sshpiperd_linux_amd64_v1/ and dist/plugin_fixed_linux_amd64_v1/.

Copilot AI changed the title Make Dockerfile the single source of truth for images; add Makefile and quickstart demo Add Makefile and quickstart demo (keep GoReleaser-built binaries as the single image source) May 4, 2026
Copilot finished work on behalf of tg123 May 4, 2026 22:28
Copilot AI requested a review from tg123 May 4, 2026 22:28
Copilot finished work on behalf of tg123 May 4, 2026 23:02
@tg123

tg123 commented May 12, 2026

Copy link
Copy Markdown
Owner

@copilot resolve the merge conflicts in this pull request

Co-authored-by: tg123 <170430+tg123@users.noreply.github.com>

Copilot AI commented May 12, 2026

Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts in this pull request

Resolved in beb0759. Conflicts were in .gitignore, Dockerfile, and .goreleaser.yaml — the new sshpiperd-admin binary from master is automatically picked up by the Dockerfile's go build ./cmd/..., and I added the matching *replace_with_docker_bin post-hook + sshpiperd_admin archive id in .goreleaser.yaml. Verified end-to-end: extracted sshpiperd-admin from a docker-bins build is byte-identical (sha256 cf1090db…) to dist/sshpiperd_admin_linux_amd64_v1/sshpiperd-admin.

Copilot finished work on behalf of tg123 May 12, 2026 23:19
Comment thread .goreleaser.yaml Outdated
binary: sshpiperd
hooks:
post:
- &replace_with_docker_bin 'scripts/goreleaser-replace-with-docker-bin.sh "{{ .Path }}" {{ .Os }} {{ .Arch }} "{{ .Name }}"'

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot help me understand, is there any other way to avoid replace after build

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — the cleanest alternative is GoReleaser's builder: prebuilt mode. make docker-bins would still produce the linux binaries via the Dockerfile's bin-export stage, but for the linux entries we'd set:

builds:
  - id: sshpiperd-linux
    builder: prebuilt
    goos: [linux]
    goarch: [amd64, arm64]
    prebuilt:
      path: .docker-bins/{{ .Os }}_{{ .Arch }}/sshpiperd
    binary: sshpiperd
  - id: sshpiperd-cross   # windows/darwin only, still compiled by goreleaser
    goos: [windows, darwin]
    ...

No go build runs for linux, so no replace step is needed. Trade-offs vs the current post-hook:

  • Pro: no "build then overwrite" — goreleaser just packages what docker produced.
  • Con: every binary (sshpiperd, each plugin, webadmin, admin…) has to be split into two build entries (linux-prebuilt + cross-compiled), roughly doubling the builds: section, and the same split repeats in archives.ids.

Two alternatives I considered but didn't pick:

  1. Pure reproducible builds (drop docker-bins, trust -trimpath -s -w): fragile in practice — host Go (whatever's on the runner) vs container Go (pinned golang:1.26-bookworm) embed different build info, so sha256s drift unless every runner pins the exact same Go version.
  2. Docker COPY from dist/ (the previous EXTERNAL=1 path): you asked me to drop it in the earlier review.

Happy to switch to builder: prebuilt if you prefer the doubled-up builds: section over the one-line post-hook — let me know.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot builder: prebuilt

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried it locally and hit a blocker: builder: prebuilt is a GoReleaser Pro–only feature, not in the OSS version this repo uses.

Reproduction with v2.15.4 (latest OSS):

$ goreleaser check
⨯ command failed
  error=
  │ yaml: unmarshal errors:
  │   line 29: field prebuilt not found in type config.Build
  │   line 36: field prebuilt not found in type config.Build
  │   ...

A grep through the v2.15.4 module source confirms there's no prebuilt field on config.Build — the builder only exists in goreleaser-pro.

So the options are:

  1. Stay on the current post-hook (scripts/goreleaser-replace-with-docker-bin.sh) — works on OSS.
  2. Adopt GoReleaser Pro and switch to builder: prebuilt — needs a Pro license for the project.
  3. Drop docker-bins, rely on -trimpath -s -w reproducibility — would need every CI runner to pin the exact same Go toolchain version as the Dockerfile (golang:1.26-bookworm) for sha256s to match.

Reverted my prebuilt experiment; the current .goreleaser.yaml post-hook is unchanged. Let me know which direction you'd like to take.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot help me understand how hard to remove goreleaser ? snap?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick scoping — these are separable, so I'll cover them independently.

What GoReleaser does for this repo today

Looking at .goreleaser.yaml + release.yaml:

  1. Cross-compiles ~12 binaries (sshpiperd, 9 plugins, webadmin, admin) × {linux, windows, darwin} × {amd64, arm64}. Linux is already produced by the Dockerfile — make docker-bins extracts it and the post-hook swaps it in.
  2. Packages per-OS-arch archives (tar.gz for linux/darwin, zip for windows) with allow_different_binary_count.
  3. Generates dist/checksums.txt.
  4. Uploads everything to the GH release (the workflow then re-uploads via gh release upload).
  5. Builds & publishes the Snap (snapcrafts: block).

Removing GoReleaser only (keeping Snap)

Effort: medium (~1 day). Mechanical but touches several files. Roughly:

  • New Makefile targets release-bins / release-archives / release-checksums that loop GOOS/GOARCH and call go build -trimpath -ldflags "-s -w -X main.mainver=…" + tar/zip + sha256sum. For linux they'd just package the binaries make docker-bins already produces — no replace hook needed, which incidentally answers the original "avoid replace after build" question.
  • release.yaml: drop the goreleaser action, call make release-archives + make docker-push + gh release upload.
  • Snap would have to be built directly with snapcraft pack (and snapcraft upload), which means re-creating a snap/snapcraft.yaml by hand from the current snapcrafts: block (~60 lines, mostly the parts/apps/plugs already in .goreleaser.yaml). Doable, but the Snap CI step gets longer than what goreleaser-action gives you for free.

Net trade-off: ~150 lines of shell/Make replacing ~280 lines of yaml + a third-party action; gain full control + the ability to drop the prebuilt/replace hack; lose the snapcraft integration GoReleaser ships out of the box.

Removing Snap (independent of GoReleaser)

Effort: low (~1 hour), but it's a user-facing breaking change.

In code/build it's just:

  • Drop snapcrafts: from .goreleaser.yaml (or its equivalent if goreleaser is gone).
  • Delete cmd/sshpiperd/snap/ (launcher/, configgen/, hooks/, README — the snaplauncher build entry too).
  • Drop the Setup Snap step + SNAPCRAFT_STORE_CREDENTIALS from release.yaml.
  • Update root README to remove the Snap install instructions.

The real cost is product: sudo snap install sshpiperd stops working for existing users on the stable/beta channels. The Snap currently advertises itself prominently (badge in cmd/sshpiperd/snap/README.md). If you're OK breaking that, the code change is trivial.

Combined cheapest path

Drop Snap first (1 hr), then dropping GoReleaser becomes much easier (no snapcrafts: block to replace, no snapcraft CLI in CI) — closer to half a day for the goreleaser removal.

Happy to do either or both in this PR (or a follow-up) if you give the go-ahead. My recommendation, given the scope of this PR is already "Makefile + image-binary parity", would be a follow-up PR per concern so it's easy to review and easy to revert if needed.

Copilot AI commented May 24, 2026

Copy link
Copy Markdown
Contributor Author

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • goreleaser.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node /home/REDACTED/work/_temp/ghcca-node/node/bin/node --enable-source-maps /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title Add Makefile and quickstart demo (keep GoReleaser-built binaries as the single image source) Replace GoReleaser with Make + snapcraft pipeline (keep Snap publishing) May 27, 2026
Copilot finished work on behalf of tg123 May 27, 2026 01:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants