From 969662b88d348767a794537fc8aadc2255abfa76 Mon Sep 17 00:00:00 2001
From: Eugene Blikh
Date: Tue, 26 May 2026 00:14:30 +0300
Subject: [PATCH] Add goreleaser + CHANGELOG.md; cross-platform downloads on
site
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* .goreleaser.yml — builds linux/{amd64,arm64} and darwin/{amd64,arm64}
as raw binaries (formats: binary) with CGO_ENABLED=0, trimpath, version
ldflag injection. GitHub release disabled; we publish via pages.sr.ht.
* CHANGELOG.md — Keep-a-Changelog format. v0.1.0 entry summarizes the
full surface (init/doctor, single-file/docker/dir caching, --hash-from,
Garage compat, e2e suite, CI manifests).
* .builds/publish.yml — installs goreleaser binary release (pinned to
v2.7.0), runs `goreleaser release --clean --skip=publish`, flattens
the 4 dist/cacher___v/cacher paths into
pages/cacher--, regenerates checksums.txt to match the
published filenames, renders CHANGELOG.md to HTML via cmark with
heading demotion (h1→drop, h2→h3, h3→h4), substitutes per-platform
sha256s into docs/index.html, and ships everything to
bigbes.pages.srht.bigb.es/ci-cacher/ plus the build artifacts list.
* docs/index.html — Download section is now a 4-row table with per-
platform sha256s, plus a Changelog section that embeds the rendered
CHANGELOG.md inline.
---
.builds/publish.yml | 102 ++++++++++++++++++++++++++++++--------------
.goreleaser.yml | 52 ++++++++++++++++++++++
CHANGELOG.md | 59 +++++++++++++++++++++++++
docs/index.html | 60 +++++++++++++++++++++++---
4 files changed, 234 insertions(+), 39 deletions(-)
create mode 100644 .goreleaser.yml
create mode 100644 CHANGELOG.md
diff --git a/.builds/publish.yml b/.builds/publish.yml
index a3d9f3d7cb99f7fe3302b43ffac4c743bb2b8a62..c7cd21719d8880904d03f57814e68e1f0ffbba7c 100644
--- a/.builds/publish.yml
+++ b/.builds/publish.yml
@@ -1,26 +1,25 @@
-# Publish a linux-amd64 binary on every tag push. Two destinations:
+# Build cross-platform binaries via goreleaser, ship them three ways on
+# every tag push:
#
-# 1. Build artifact (cacher-linux-amd64 visible on the job page,
-# pruned by builds.sr.ht after 90 days).
-#
-# 2. pages.sr.ht under bigbes.pages.srht.bigb.es/ci-cacher/, so
-# downstream projects can `wget` from a stable URL. We publish
-# with `hut pages publish -s /ci-cacher` so this manifest only
-# touches files under that subpath — other projects sharing the
-# user-level pages domain stay intact. Latest tag overwrites the
-# previous publish under /ci-cacher; historical versions remain
-# available via the artifact link during the 90-day window.
+# 1. Build artifacts (visible on the job page, 90-day TTL).
+# 2. pages.sr.ht under bigbes.pages.srht.bigb.es/ci-cacher/ via
+# `hut pages publish -s /ci-cacher` (subpath-scoped, so other
+# projects sharing the user-level pages domain stay intact).
+# 3. A landing page rendered from docs/index.html with CHANGELOG.md
+# embedded inline.
#
# Auto-submission is restricted to tag refs only.
image: ubuntu/noble
packages:
- curl
- ca-certificates
+ - cmark # CHANGELOG.md → HTML
oauth: pages.sr.ht/PAGES:RW
sources:
- https://git.srht.bigb.es/~bigbes/ci-cacher
environment:
GO_VERSION: "1.26.3"
+ GORELEASER_VERSION: "v2.7.0"
PATH: /home/build/.local/go/bin:/home/build/.local/bin:/home/build/go/bin:/usr/local/bin:/usr/bin:/bin
GOPATH: /home/build/go
PAGES_DOMAIN: bigbes.pages.srht.bigb.es
@@ -39,38 +38,77 @@ tasks:
rm "/tmp/$GO_TARBALL"
go version
- install_hut: |
- # hut isn't in ubuntu/noble's repos; build it from source with the
- # Go we just installed. ~5s on a warm GOPATH, ~30s cold.
go install git.sr.ht/~xenrox/hut@latest
hut --version
+ - install_goreleaser: |
+ # Pinned binary release, not `go install` — goreleaser's release
+ # binaries are stripped + statically linked and ~10x smaller than
+ # a from-source build.
+ curl -sSL "https://github.com/goreleaser/goreleaser/releases/download/${GORELEASER_VERSION}/goreleaser_Linux_x86_64.tar.gz" \
+ | tar -xz -C ~/.local/bin goreleaser
+ goreleaser --version
- build: |
cd ci-cacher
- VERSION=$(git describe --tags --abbrev=0 2>/dev/null || cat VERSION)
- echo "Building $VERSION"
- GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
- go build -ldflags "-s -w -X go.bigb.es/cacher/internal/version.version=${VERSION}" \
- -o /home/build/cacher-linux-amd64 .
- /home/build/cacher-linux-amd64 version
- sha256sum /home/build/cacher-linux-amd64
+ goreleaser release --clean --skip=publish
+ ls dist/
- package_pages: |
- # pages.sr.ht expects a tarball whose top-level contents map to the
- # publish path — so `tar -C dist .`, not `tar dist/`. With
- # `-s /ci-cacher` the binary lands at
- # https://$PAGES_DOMAIN/ci-cacher/cacher-linux-amd64. docs/index.html
- # is the project landing page; the three {{PLACEHOLDERS}} get
- # substituted at publish time with the values from this build.
+ # Goreleaser writes binaries under dist/cacher___v/cacher.
+ # Flatten + rename to the pages-facing /ci-cacher/cacher--
+ # naming. Regenerate checksums.txt against the renamed files so the
+ # published file matches the URLs people will wget.
VERSION=$(cd ci-cacher && (git describe --tags --abbrev=0 2>/dev/null || cat VERSION))
- SUM=$(sha256sum /home/build/cacher-linux-amd64 | awk '{print $1}')
BUILT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
- mkdir -p /home/build/dist
- cp /home/build/cacher-linux-amd64 /home/build/dist/cacher-linux-amd64
+ mkdir -p /home/build/pages
+ cp ci-cacher/dist/cacher_linux_amd64_v1/cacher /home/build/pages/cacher-linux-amd64
+ cp ci-cacher/dist/cacher_linux_arm64_v8.0/cacher /home/build/pages/cacher-linux-arm64
+ cp ci-cacher/dist/cacher_darwin_amd64_v1/cacher /home/build/pages/cacher-darwin-amd64
+ cp ci-cacher/dist/cacher_darwin_arm64_v8.0/cacher /home/build/pages/cacher-darwin-arm64
+ chmod +x /home/build/pages/cacher-*
+ ( cd /home/build/pages && sha256sum cacher-* > checksums.txt )
+ cat /home/build/pages/checksums.txt
+
+ SHA_LA=$( awk '/cacher-linux-amd64$/ {print $1}' /home/build/pages/checksums.txt)
+ SHA_LAR=$(awk '/cacher-linux-arm64$/ {print $1}' /home/build/pages/checksums.txt)
+ SHA_DA=$( awk '/cacher-darwin-amd64$/ {print $1}' /home/build/pages/checksums.txt)
+ SHA_DAR=$(awk '/cacher-darwin-arm64$/ {print $1}' /home/build/pages/checksums.txt)
+
+ # Render CHANGELOG.md to an HTML fragment for inline embedding.
+ # Drop the file's top-level `# Changelog` h1 (we already have an
+ # Changelog
in the template) and demote remaining
+ # headings one level so they nest under the outer h2.
+ cmark --to html ci-cacher/CHANGELOG.md \
+ | sed '/Changelog<\/h1>/d' \
+ | sed -e 's|||g; s||
|g' \
+ > /tmp/changelog.html
+
+ # Substitute scalar placeholders, then insert the changelog at the
+ # {{CHANGELOG}} marker via sed's read+delete trick.
sed -e "s|{{VERSION}}|$VERSION|g" \
- -e "s|{{SHA256}}|$SUM|g" \
-e "s|{{BUILT}}|$BUILT|g" \
- ci-cacher/docs/index.html > /home/build/dist/index.html
- cd /home/build/dist
+ -e "s|{{SHA_LINUX_AMD64}}|$SHA_LA|g" \
+ -e "s|{{SHA_LINUX_ARM64}}|$SHA_LAR|g" \
+ -e "s|{{SHA_DARWIN_AMD64}}|$SHA_DA|g" \
+ -e "s|{{SHA_DARWIN_ARM64}}|$SHA_DAR|g" \
+ ci-cacher/docs/index.html \
+ | sed -e '/{{CHANGELOG}}/r /tmp/changelog.html' -e '/{{CHANGELOG}}/d' \
+ > /home/build/pages/index.html
+
+ cd /home/build/pages
tar -czvf /home/build/site.tar.gz .
- publish_pages: |
hut pages publish -d "$PAGES_DOMAIN" -s "$PAGES_SUBPATH" /home/build/site.tar.gz
+ - stage_artifacts: |
+ # `artifacts:` paths resolve relative to /home/build. Copy the four
+ # binaries + checksums.txt into the top level so they're easy to
+ # reference (and to keep the page tarball clean of duplicates).
+ cp /home/build/pages/cacher-linux-amd64 /home/build/
+ cp /home/build/pages/cacher-linux-arm64 /home/build/
+ cp /home/build/pages/cacher-darwin-amd64 /home/build/
+ cp /home/build/pages/cacher-darwin-arm64 /home/build/
+ cp /home/build/pages/checksums.txt /home/build/
artifacts:
- cacher-linux-amd64
+ - cacher-linux-arm64
+ - cacher-darwin-amd64
+ - cacher-darwin-arm64
+ - checksums.txt
diff --git a/.goreleaser.yml b/.goreleaser.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a91b4cab550566a21754a9ffe9f558b779777139
--- /dev/null
+++ b/.goreleaser.yml
@@ -0,0 +1,52 @@
+# Goreleaser config for cross-platform cacher builds.
+#
+# Invoked from .builds/publish.yml as `goreleaser release --clean --skip=publish`
+# on tag pushes — we only want the binaries + checksums, not a GitHub
+# release (we publish to pages.sr.ht and the build artifacts list).
+version: 2
+
+project_name: cacher
+
+before:
+ hooks:
+ - go mod tidy
+
+builds:
+ - id: cacher
+ main: .
+ binary: cacher
+ env:
+ - CGO_ENABLED=0
+ flags:
+ - -trimpath
+ ldflags:
+ - -s -w
+ - -X go.bigb.es/cacher/internal/version.version={{ .Version }}
+ goos: [linux, darwin]
+ goarch: [amd64, arm64]
+
+archives:
+ # Ship raw binaries rather than tarballs so the wget URL
+ # `…/cacher-linux-amd64` works directly. The {{ .Version }} prefix lives
+ # in the goreleaser dist/ filenames but we strip it when copying into
+ # the pages tree (see .builds/publish.yml).
+ - id: binary
+ formats: [binary]
+ name_template: "cacher_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
+
+checksum:
+ name_template: "checksums.txt"
+ algorithm: sha256
+
+snapshot:
+ version_template: "{{ .Tag }}-snapshot"
+
+changelog:
+ # We maintain CHANGELOG.md by hand. Don't autogenerate from git log.
+ disable: true
+
+release:
+ # We publish via pages.sr.ht + build artifacts; there's no GitHub repo to
+ # release to. `goreleaser release --skip=publish` already inhibits the
+ # release step, but disabling here keeps `goreleaser check` happy.
+ disable: true
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..0d453b551cf87f25f0e177d74c6c3c5f864716a2
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,59 @@
+# Changelog
+
+All notable changes to this project will be documented in this file. The format
+is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this
+project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [0.1.0] — 2026-05-26
+
+First public release. Replaces the `s3_cache_or_curl` /
+`s3_cache_docker_image` shell helpers in `tarantool-protobuf/.builds/lib/ci-lib.sh`
+with a single static Go binary.
+
+### Added
+- `cacher init` / `cacher doctor` — persist config to
+ `~/.config/cacher/config.toml` and smoke-test S3 credentials (HEAD bucket
+ + 1-byte canary write/read/delete).
+- `cacher download` / `upload` / `exists` / `list` / `delete` for single
+ files. Download falls back to `--url` on cache miss and back-fills the
+ cache. Optional `--sha256` verifies the fetched content.
+- `cacher docker {exists,download,upload}` — streamed `docker save | zstd`
+ → S3 multipart upload (and inverse). Pure-Go zstd via
+ [klauspost/compress](https://github.com/klauspost/compress), no external
+ `zstd` binary on the host.
+- `cacher dir {download,upload}` — tar+zstd of a directory tree keyed by
+ content hash. Closes the gap left by the shell version, which only
+ cached single files.
+- `cacher key` — resolve a key template (substituting `{hash}`) for shell
+ scripting.
+- `--hash-from ` (repeatable; files or directories) on every command.
+ For a single file path the digest exactly matches `sha256sum file | cut -c1-N`,
+ so existing keys migrate without recomputation.
+- `--arch-suffix` opt-in to suffix every key with `--`.
+- `list --recursive` for flat listing; `list --root` to ignore the
+ configured prefix and list at bucket root. Default output style mirrors
+ `aws s3 ls` (delimited by `/`).
+- Garage-compatible defaults: path-style addressing, signature v4,
+ request/response checksum calculation set to `when_required` (Garage
+ doesn't implement boto3 1.36+ trailing CRC32 checksums).
+- End-to-end test suite (`go test -tags=e2e`) against a real Garage
+ container via `testcontainers-go` using `dxflrs/garage:v2.3.0`'s
+ `--single-node --default-bucket` mode.
+- builds.sr.ht CI: `unit.yml` (every push), `e2e.yml` (every push, with
+ Docker), `publish.yml` (tags only, ships cross-platform binaries via
+ goreleaser to pages.sr.ht and as build artifacts).
+- Static landing page at
+ [bigbes.pages.srht.bigb.es/ci-cacher/](https://bigbes.pages.srht.bigb.es/ci-cacher/).
+
+### Fixed
+- `doctor` uses ListObjectsV2 (1-key) instead of HeadBucket — Garage
+ rejects HeadBucket with 403 even for valid credentials.
+- Config path is `~/.config/cacher/config.toml` on every platform
+ (previously fell into `~/Library/Application Support/cacher` on macOS
+ via `os.UserConfigDir`). cacher is a CI tool; dev-macs and Linux runners
+ must look in the same place.
+
+[Unreleased]: https://git.srht.bigb.es/~bigbes/ci-cacher/log/master
+[0.1.0]: https://git.srht.bigb.es/~bigbes/ci-cacher/refs/v0.1.0
diff --git a/docs/index.html b/docs/index.html
index 919087e96139edb6b4543e3213e7522ec4f05d66..983271549cf52857d69eb91770e67aef2abc695f 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -31,10 +31,19 @@
padding: 0 1.25rem 4rem;
font: 16px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
}
- h1, h2, h3 { line-height: 1.25; }
+ h1, h2, h3, h4 { line-height: 1.25; }
h1 { font-size: 1.7rem; margin-bottom: 0.15rem; }
h2 { font-size: 1.15rem; margin-top: 2.2rem; padding-bottom: .2rem; border-bottom: 1px solid var(--rule); }
h3 { font-size: 1rem; margin-top: 1.4rem; }
+ h4 { font-size: .95rem; margin-top: 1rem; color: var(--muted); }
+ table { border-collapse: collapse; width: 100%; margin: .7rem 0; font-size: .92em; }
+ th, td { text-align: left; padding: .35rem .6rem; border-bottom: 1px solid var(--rule); }
+ th { color: var(--muted); font-weight: 600; }
+ td code { font-size: .85em; word-break: break-all; }
+ .changelog ul { padding-left: 1.25rem; }
+ .changelog li { margin: .25rem 0; }
+ .changelog h2 { font-size: 1.05rem; margin-top: 1.4rem; }
+ .changelog h3 { font-size: .95rem; margin-top: 1rem; color: var(--muted); border-bottom: none; }
p, ul, ol { margin: .7rem 0; }
a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }
@@ -66,13 +75,46 @@
anywhere you can run a binary and reach an S3 endpoint.
-Install
-wget https://bigbes.pages.srht.bigb.es/ci-cacher/cacher-linux-amd64 \
- -O ~/.local/bin/cacher
-chmod +x ~/.local/bin/cacher
+Download
+
+ Pre-built binaries for {{VERSION}}. The shipping URL is stable; the
+ SHA-256s below are specific to this build — paste them into your
+ pin if you care.
+
+
- Verify the binary against the sha256 below before using it.
+ All four hashes plus filenames are also available in a single
+ checksums.txt for piping into sha256sum -c:
+wget https://bigbes.pages.srht.bigb.es/ci-cacher/cacher-linux-amd64 \
+ -O ~/.local/bin/cacher
+chmod +x ~/.local/bin/cacher
+wget -qO- https://bigbes.pages.srht.bigb.es/ci-cacher/checksums.txt \
+ | sha256sum -c --ignore-missing
The loop it replaces
# before — install awscli, write ~/.aws/config, then in every task:
@@ -112,10 +154,14 @@ cacher download "$key" "$out" --url "$url"
the shell version only ever handled single files.
+Changelog
+
+
This build
- Version
- {{VERSION}}
- - SHA-256
{{SHA256}}
- Built
- {{BUILT}}
- Source
- git.srht.bigb.es/~bigbes/ci-cacher
- License
- BSD-2-Clause