Files
abuckit 0801e31164 refactor(packaging): merge the per-platform binary installers (#28)
install-linux-binary.sh and install-mac.sh were 443 lines that differed
in three places: the OS check and architecture allowlist, the platform
token in the pointer URL and asset name, and clearing the macOS
quarantine attribute. The other ~190 lines were identical.

That duplication already cost something. Hardening the installers meant
applying five fixes twice, by hand, in parallel -- pin-bypass path
traversal, pipeline masking, digest normalisation, directory
destination, and the cross-origin digest check. The next fix would have
had the same shape, and eventually one would land in only one file.

Merge them into install-binary.sh, which detects Linux or macOS and
validates the architecture against what is published for that platform:
Linux ships amd64 and arm64, macOS ships Apple Silicon only.

The release workflow publishes it under the old names as well, so URLs
already in the wild, in the docs, and in the blog post keep working and
pick up the merged behaviour on every release. Nothing needs to change
on the reader's side, and there is no window where a documented command
404s.

Two benign consequences: install-mac.sh now also works on Linux and
install-linux-binary.sh on macOS, and error messages self-identify as
install-binary.sh whichever URL was fetched.
2026-08-04 20:57:01 -04:00

558 lines
28 KiB
YAML

name: Release
on:
push:
tags:
- 'RELEASE.*'
permissions:
contents: write
packages: write
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- goos: linux
goarch: amd64
- goos: linux
goarch: arm64
- goos: windows
goarch: amd64
ext: .exe
- goos: darwin
goarch: arm64
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version: 1.25.x
- name: Build binary
env:
CGO_ENABLED: 0
BUCKIT_RELEASE: RELEASE
run: |
# Extract timestamp from tag (e.g., RELEASE.2026-05-10T21-48-00Z.rc1 -> 2026-05-10T21-48-00Z)
VERSION=$(echo "${{ github.ref_name }}" | sed 's/^RELEASE\.//' | sed 's/\.rc[0-9]*$//')
# Pass RC suffix via BUCKIT_HOTFIX so it's embedded in the binary's ReleaseTag
if [[ "${{ github.ref_name }}" =~ \.(rc[0-9]+)$ ]]; then
export BUCKIT_HOTFIX="${BASH_REMATCH[1]}"
fi
LDFLAGS=$(go run buildscripts/gen-ldflags.go "${VERSION}")
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -tags kqueue -trimpath --ldflags "${LDFLAGS}" -o buckit-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }}.${{ github.ref_name }}
- name: Generate checksum
run: |
sha256sum buckit-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }}.${{ github.ref_name }} > buckit-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }}.${{ github.ref_name }}.sha256sum
- name: Sign with minisign
env:
MINISIGN_PRIVATE_KEY: ${{ secrets.MINISIGN_PRIVATE_KEY }}
MINISIGN_PASSWORD: ${{ secrets.MINISIGN_PASSWORD }}
run: |
go install aead.dev/minisign/cmd/minisign@v0.2.1
echo "$MINISIGN_PRIVATE_KEY" | base64 -d > /tmp/minisign.key
echo "$MINISIGN_PASSWORD" | minisign -Sm buckit-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }}.${{ github.ref_name }} -s /tmp/minisign.key
shred -u /tmp/minisign.key
- name: Build packages
if: matrix.goos == 'linux'
env:
NFPM_VERSION: 2.46.3
PKG_ARCH: ${{ matrix.goarch }}
run: |
wget -q "https://github.com/goreleaser/nfpm/releases/download/v${NFPM_VERSION}/nfpm_${NFPM_VERSION}_amd64.deb"
sudo apt install -y "./nfpm_${NFPM_VERSION}_amd64.deb"
mkdir -p dist
cp "buckit-linux-${PKG_ARCH}.${{ github.ref_name }}" dist/buckit
# Convert RELEASE.2026-05-11T17-20-40Z[.rcN] -> 20260511172040.0.0
PKG_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^RELEASE\.//' | sed 's/\.rc[0-9]*$//' | tr -d '\-:TZ').0.0
export PKG_VERSION
for pkg in rpm deb apk; do
nfpm package -f packaging/nfpm.yaml -p "$pkg" -t dist/
done
- name: Generate package checksums
if: matrix.goos == 'linux'
run: |
for pkg in dist/*.deb dist/*.rpm dist/*.apk; do
sha256sum "$pkg" > "${pkg}.sha256sum"
done
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.goos }}-${{ matrix.goarch }}
path: |
buckit-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }}.${{ github.ref_name }}
buckit-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }}.${{ github.ref_name }}.sha256sum
buckit-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }}.${{ github.ref_name }}.minisig
dist/*.deb
dist/*.rpm
dist/*.apk
dist/*.deb.sha256sum
dist/*.rpm.sha256sum
dist/*.apk.sha256sum
docker:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Prepare binaries for Docker
run: |
cp artifacts/buckit-linux-amd64.${{ github.ref_name }} buckit-amd64.${{ github.ref_name }}
cp artifacts/buckit-linux-arm64.${{ github.ref_name }} buckit-arm64.${{ github.ref_name }}
cp artifacts/buckit-linux-amd64.${{ github.ref_name }}.minisig buckit-amd64.${{ github.ref_name }}.minisig
cp artifacts/buckit-linux-arm64.${{ github.ref_name }}.minisig buckit-arm64.${{ github.ref_name }}.minisig
chmod +x buckit-*.${{ github.ref_name }}
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Determine tags
id: tags
run: |
TAGS="ghcr.io/buckit-io/buckit:${{ github.ref_name }}"
TAGS="${TAGS},docker.io/buckitio/buckit:${{ github.ref_name }}"
if [[ "${{ github.ref_name }}" != *".rc"* ]]; then
TAGS="${TAGS},ghcr.io/buckit-io/buckit:latest"
TAGS="${TAGS},docker.io/buckitio/buckit:latest"
fi
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
- uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.tags.outputs.tags }}
build-args: |
RELEASE=${{ github.ref_name }}
publish:
needs: [build, docker]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Prepare release assets
run: |
mkdir -p release-assets
find artifacts -type f \
! -name 'buckit.rpm' \
! -name 'buckit.deb' \
! -name 'buckit.apk' \
! -name 'buckit.rpm.sha256sum' \
! -name 'buckit.deb.sha256sum' \
! -name 'buckit.apk.sha256sum' \
-exec cp {} release-assets/ \;
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
prerelease: ${{ contains(github.ref_name, '.rc') }}
files: release-assets/*
update-gh-pages:
if: "!contains(github.ref_name, '.rc')"
needs: [build, publish]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: gh-pages
path: pages
- uses: actions/checkout@v4
with:
path: src
- uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Publish install scripts
run: |
cp src/packaging/install-linux.sh pages/install-linux.sh
cp src/packaging/install-binary.sh pages/install-binary.sh
cp src/packaging/install-windows.ps1 pages/install-windows.ps1
# install-binary.sh replaced the per-platform binary installers.
# Publish it under the old names too so URLs already in the wild,
# in docs, and in the blog keep working.
cp src/packaging/install-binary.sh pages/install-linux-binary.sh
cp src/packaging/install-binary.sh pages/install-mac.sh
chmod +x pages/install-linux.sh pages/install-binary.sh \
pages/install-linux-binary.sh pages/install-mac.sh
- name: Write self-update checksum files
run: |
tag="${{ github.ref_name }}"
# Rebuild the release tree from scratch to remove any stale assets.
rm -rf pages/server/buckit/release pages/server/buckit/archives
mkdir -p \
pages/server/buckit/release/linux-amd64 \
pages/server/buckit/release/linux-arm64 \
pages/server/buckit/release/windows-amd64 \
pages/server/buckit/release/darwin-arm64 \
pages/server/buckit/archives
sha=$(awk '{print $1}' "artifacts/buckit-linux-amd64.${tag}.sha256sum")
echo "${sha} buckit.${tag}" > "pages/server/buckit/release/linux-amd64/buckit.sha256sum"
sha=$(awk '{print $1}' "artifacts/buckit-linux-arm64.${tag}.sha256sum")
echo "${sha} buckit.${tag}" > "pages/server/buckit/release/linux-arm64/buckit.sha256sum"
sha=$(awk '{print $1}' "artifacts/buckit-windows-amd64.exe.${tag}.sha256sum")
echo "${sha} buckit.exe.${tag}" > "pages/server/buckit/release/windows-amd64/buckit.sha256sum"
sha=$(awk '{print $1}' "artifacts/buckit-darwin-arm64.${tag}.sha256sum")
echo "${sha} buckit.${tag}" > "pages/server/buckit/release/darwin-arm64/buckit.sha256sum"
- name: Generate release index page
run: |
tag="${{ github.ref_name }}"
base="https://github.com/buckit-io/buckit/releases/download/${tag}"
PKG_VERSION=$(echo "${tag}" | sed 's/^RELEASE\.//' | sed 's/\.rc[0-9]*$//' | tr -d '\-:TZ').0.0
cat > pages/server/buckit/release/index.html <<HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Buckit ${tag}</title>
<style>
:root { color-scheme: light; --bg: #f3f5f9; --surface: #ffffff; --surface-alt: #f8fafc; --border: #d8dee9; --text: #0f172a; --muted: #475569; --link: #0f766e; --link-hover: #115e59; --accent: #dcfce7; --accent-text: #166534; --shadow: 0 18px 40px rgba(15, 23, 42, 0.08); }
* { box-sizing: border-box; }
body { margin: 0; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: var(--bg); color: var(--text); }
.page { max-width: 1080px; margin: 0 auto; padding: 40px 20px 64px; }
.hero, .panel { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow); }
.hero { padding: 28px; margin-bottom: 24px; }
.eyebrow { display: inline-block; padding: 6px 10px; border-radius: 999px; background: var(--accent); color: var(--accent-text); font-size: 12px; font-weight: 700; letter-spacing: 0; text-transform: uppercase; }
h1 { margin: 14px 0 10px; font-size: 34px; line-height: 1.1; }
.subhead { margin: 0 0 20px; max-width: 760px; color: var(--muted); font-size: 16px; line-height: 1.6; }
.hero-actions, .meta { display: flex; flex-wrap: wrap; gap: 12px; }
.meta { margin-top: 18px; }
.meta-item { padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface-alt); min-width: 160px; }
.meta-label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 4px; }
.meta-value { font-size: 14px; font-weight: 600; }
.button, .text-link { color: var(--link); text-decoration: none; font-weight: 600; }
.button { display: inline-flex; align-items: center; justify-content: center; min-height: 42px; padding: 0 16px; border-radius: 10px; border: 1px solid #99f6e4; background: #f0fdfa; }
.button:hover, .text-link:hover { color: var(--link-hover); }
.panel { padding: 24px; margin-bottom: 24px; }
.panel:last-child { margin-bottom: 0; }
.section-title { margin: 0 0 8px; font-size: 24px; }
.section-copy { margin: 0 0 20px; color: var(--muted); line-height: 1.6; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; }
.card { border: 1px solid var(--border); border-radius: 12px; background: var(--surface-alt); padding: 18px; }
.card h3 { margin: 0 0 8px; font-size: 18px; }
.card p { margin: 0 0 16px; color: var(--muted); font-size: 14px; line-height: 1.5; }
.card-links { display: grid; gap: 10px; }
.download { display: inline-flex; align-items: center; justify-content: center; min-height: 40px; padding: 0 14px; border-radius: 10px; background: #0f172a; color: #ffffff; text-decoration: none; font-weight: 600; }
.download:hover { background: #1e293b; }
.aux-links { display: flex; flex-wrap: wrap; gap: 12px; }
.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 12px; background: var(--surface-alt); }
table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 14px 16px; border-bottom: 1px solid var(--border); vertical-align: top; }
th { font-size: 12px; text-transform: uppercase; color: var(--muted); background: #eef2f7; letter-spacing: 0.04em; }
tbody tr:last-child td { border-bottom: 0; }
td strong { display: block; margin-bottom: 4px; }
.footer-note { margin-top: 18px; color: var(--muted); font-size: 14px; line-height: 1.6; }
a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }
@media (max-width: 720px) {
.page { padding: 24px 16px 48px; }
.hero, .panel { padding: 20px; }
h1 { font-size: 28px; }
}
</style>
</head>
<body>
<div class="page">
<section class="hero">
<span class="eyebrow">Latest Stable Release</span>
<h1>Buckit</h1>
<p class="subhead">Signed release binaries for the Buckit S3-compatible object storage server.</p>
<div class="hero-actions">
<a class="button" href="../archives/">Browse release archive</a>
</div>
<div class="meta">
<div class="meta-item">
<span class="meta-label">Release tag</span>
<span class="meta-value">${tag}</span>
</div>
<div class="meta-item">
<span class="meta-label">Repository</span>
<span class="meta-value">buckit-io/buckit</span>
</div>
</div>
</section>
<section class="panel">
<h2 class="section-title">Install</h2>
<p class="section-copy">One-line installers that download the right artifact for your platform, verify its SHA-256 checksum, and print the command to finish. They do not install for you — the privileged step is yours to review and run.</p>
<div class="table-wrap">
<table>
<thead>
<tr><th>Platform</th><th>Command</th></tr>
</thead>
<tbody>
<tr>
<td><strong>Linux</strong>rpm / deb / apk</td>
<td><code style="font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px">curl -fsSL https://buckit-io.github.io/buckit/install-linux.sh | sh</code></td>
</tr>
<tr>
<td><strong>Linux</strong>standalone binary</td>
<td><code style="font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px">curl -fsSL https://buckit-io.github.io/buckit/install-linux-binary.sh | sh</code></td>
</tr>
<tr>
<td><strong>macOS</strong>Apple Silicon</td>
<td><code style="font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px">curl -fsSL https://buckit-io.github.io/buckit/install-mac.sh | sh</code></td>
</tr>
<tr>
<td><strong>Windows</strong>x86_64</td>
<td><code style="font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px">irm https://buckit-io.github.io/buckit/install-windows.ps1 | iex</code></td>
</tr>
</tbody>
</table>
</div>
</section>
<section class="panel">
<h2 class="section-title">Downloads</h2>
<p class="section-copy">Choose the binary for your OS and architecture. Each platform card includes the signed artifact, checksum, and minisign signature.</p>
<div class="cards">
<article class="card">
<h3>Linux x86_64</h3>
<p>Standard Linux build for x86_64 hosts.</p>
<div class="card-links">
<a class="download" href="${base}/buckit-linux-amd64.${tag}">Download binary</a>
<div class="aux-links">
<a href="${base}/buckit-linux-amd64.${tag}.sha256sum">Checksum</a>
<a href="${base}/buckit-linux-amd64.${tag}.minisig">Signature</a>
</div>
</div>
</article>
<article class="card">
<h3>Linux arm64</h3>
<p>Linux build for ARM64 systems and cloud hosts.</p>
<div class="card-links">
<a class="download" href="${base}/buckit-linux-arm64.${tag}">Download binary</a>
<div class="aux-links">
<a href="${base}/buckit-linux-arm64.${tag}.sha256sum">Checksum</a>
<a href="${base}/buckit-linux-arm64.${tag}.minisig">Signature</a>
</div>
</div>
</article>
<article class="card">
<h3>macOS arm64</h3>
<p>Native Apple Silicon build for modern Macs.</p>
<div class="card-links">
<a class="download" href="${base}/buckit-darwin-arm64.${tag}">Download binary</a>
<div class="aux-links">
<a href="${base}/buckit-darwin-arm64.${tag}.sha256sum">Checksum</a>
<a href="${base}/buckit-darwin-arm64.${tag}.minisig">Signature</a>
</div>
</div>
</article>
<article class="card">
<h3>Windows x86_64</h3>
<p>Windows build for 64-bit desktop and server environments.</p>
<div class="card-links">
<a class="download" href="${base}/buckit-windows-amd64.exe.${tag}">Download binary</a>
<div class="aux-links">
<a href="${base}/buckit-windows-amd64.exe.${tag}.sha256sum">Checksum</a>
<a href="${base}/buckit-windows-amd64.exe.${tag}.minisig">Signature</a>
</div>
</div>
</article>
</div>
<p class="footer-note">Need the raw artifact names for automation or mirrors? The manifest below keeps the exact filenames and verification links in one place.</p>
<div class="table-wrap">
<table>
<thead>
<tr><th>Platform</th><th>Binary</th><th>Checksum</th><th>Signature</th></tr>
</thead>
<tbody>
<tr>
<td><strong>Linux x86_64</strong>Standard Linux</td>
<td><a href="${base}/buckit-linux-amd64.${tag}">buckit-linux-amd64.${tag}</a></td>
<td><a href="${base}/buckit-linux-amd64.${tag}.sha256sum">.sha256sum</a></td>
<td><a href="${base}/buckit-linux-amd64.${tag}.minisig">.minisig</a></td>
</tr>
<tr>
<td><strong>Linux arm64</strong>ARM64 Linux</td>
<td><a href="${base}/buckit-linux-arm64.${tag}">buckit-linux-arm64.${tag}</a></td>
<td><a href="${base}/buckit-linux-arm64.${tag}.sha256sum">.sha256sum</a></td>
<td><a href="${base}/buckit-linux-arm64.${tag}.minisig">.minisig</a></td>
</tr>
<tr>
<td><strong>macOS arm64</strong>Apple Silicon</td>
<td><a href="${base}/buckit-darwin-arm64.${tag}">buckit-darwin-arm64.${tag}</a></td>
<td><a href="${base}/buckit-darwin-arm64.${tag}.sha256sum">.sha256sum</a></td>
<td><a href="${base}/buckit-darwin-arm64.${tag}.minisig">.minisig</a></td>
</tr>
<tr>
<td><strong>Windows x86_64</strong>64-bit Windows</td>
<td><a href="${base}/buckit-windows-amd64.exe.${tag}">buckit-windows-amd64.exe.${tag}</a></td>
<td><a href="${base}/buckit-windows-amd64.exe.${tag}.sha256sum">.sha256sum</a></td>
<td><a href="${base}/buckit-windows-amd64.exe.${tag}.minisig">.minisig</a></td>
</tr>
</tbody>
</table>
</div>
</section>
<section class="panel">
<h2 class="section-title">Linux packages</h2>
<p class="section-copy">Native packages for Debian/Ubuntu, RPM, and Alpine distributions.</p>
<div class="table-wrap">
<table>
<thead>
<tr><th>Format</th><th>amd64</th><th>arm64</th></tr>
</thead>
<tbody>
<tr>
<td><strong>Debian/Ubuntu</strong>.deb</td>
<td><a href="${base}/buckit_${PKG_VERSION}_amd64.deb">buckit_${PKG_VERSION}_amd64.deb</a></td>
<td><a href="${base}/buckit_${PKG_VERSION}_arm64.deb">buckit_${PKG_VERSION}_arm64.deb</a></td>
</tr>
<tr>
<td><strong>RPM</strong>.rpm</td>
<td><a href="${base}/buckit-${PKG_VERSION}.x86_64.rpm">buckit-${PKG_VERSION}.x86_64.rpm</a></td>
<td><a href="${base}/buckit-${PKG_VERSION}.aarch64.rpm">buckit-${PKG_VERSION}.aarch64.rpm</a></td>
</tr>
<tr>
<td><strong>Alpine</strong>.apk</td>
<td><a href="${base}/buckit_${PKG_VERSION}_x86_64.apk">buckit_${PKG_VERSION}_x86_64.apk</a></td>
<td><a href="${base}/buckit_${PKG_VERSION}_aarch64.apk">buckit_${PKG_VERSION}_aarch64.apk</a></td>
</tr>
</tbody>
</table>
</div>
</section>
</div>
</body>
</html>
HTML
- name: Generate archives index page
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api repos/buckit-io/buckit/releases --paginate \
--jq '.[] | select(.prerelease == false) | {tag: .tag_name, date: .published_at[:10], url: .html_url}' \
> /tmp/releases.jsonl
python3 - /tmp/releases.jsonl pages/server/buckit/archives/index.html <<'PYEOF'
import json, html, sys
releases = [json.loads(line) for line in open(sys.argv[1]) if line.strip()]
rows = '\n'.join(
f'<tr>'
f'<td>{html.escape(r["date"])}</td>'
f'<td><a href="{html.escape(r["url"])}">{html.escape(r["tag"])}</a></td>'
f'<td><a href="{html.escape(r["url"])}">Assets &rarr;</a></td>'
f'</tr>'
for r in releases
)
page = f'''<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Buckit Releases</title>
<style>
:root {{ color-scheme: light; --bg: #f3f5f9; --surface: #ffffff; --surface-alt: #f8fafc; --border: #d8dee9; --text: #0f172a; --muted: #475569; --link: #0f766e; --shadow: 0 18px 40px rgba(15, 23, 42, 0.08); }}
* {{ box-sizing: border-box; }}
body {{ margin: 0; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: var(--bg); color: var(--text); }}
.page {{ max-width: 1080px; margin: 0 auto; padding: 40px 20px 64px; }}
.hero, .panel {{ background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow); }}
.hero {{ padding: 28px; margin-bottom: 24px; }}
.eyebrow {{ display: inline-block; padding: 6px 10px; border-radius: 999px; background: #dcfce7; color: #166534; font-size: 12px; font-weight: 700; text-transform: uppercase; }}
h1 {{ margin: 14px 0 10px; font-size: 34px; line-height: 1.1; }}
.subhead {{ margin: 0; max-width: 720px; color: var(--muted); line-height: 1.6; }}
.actions {{ margin-top: 18px; }}
.button {{ display: inline-flex; align-items: center; justify-content: center; min-height: 42px; padding: 0 16px; border-radius: 10px; border: 1px solid #99f6e4; background: #f0fdfa; color: var(--link); text-decoration: none; font-weight: 600; }}
.panel {{ padding: 24px; }}
.table-wrap {{ overflow-x: auto; border: 1px solid var(--border); border-radius: 12px; background: var(--surface-alt); }}
table {{ border-collapse: collapse; width: 100%; }}
th, td {{ text-align: left; padding: 14px 16px; border-bottom: 1px solid var(--border); }}
th {{ background: #eef2f7; font-weight: 600; font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }}
tbody tr:last-child td {{ border-bottom: 0; }}
a {{ color: var(--link); text-decoration: none; }}
a:hover {{ text-decoration: underline; }}
@media (max-width: 720px) {{
.page {{ padding: 24px 16px 48px; }}
.hero, .panel {{ padding: 20px; }}
h1 {{ font-size: 28px; }}
}}
</style>
</head>
<body>
<div class="page">
<section class="hero">
<span class="eyebrow">Release Archive</span>
<h1>Buckit Releases</h1>
<p class="subhead">Stable Buckit releases published to GitHub. Open any release to inspect artifacts, checksums, and signed binaries, or jump back to the current release landing page.</p>
<div class="actions">
<a class="button" href="../release/">Open latest release</a>
</div>
</section>
<section class="panel">
<div class="table-wrap">
<table>
<thead>
<tr><th>Date</th><th>Release</th><th>Downloads</th></tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
</section>
</div>
</body>
</html>'''
with open(sys.argv[2], 'w') as f:
f.write(page)
PYEOF
- name: Deploy to gh-pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./pages
force_orphan: true