internal/site/dl.go
Ref: Size: 2.6 KiB History
package site
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
)
// downloadsMarkdown renders the /dl/ page body from a dist/<version> dir:
// every artifact linked at its stable /dl/<version>/ URL with size and
// sha256, plus the verification snippet. An empty distDir renders a
// docs-only preview note instead.
func downloadsMarkdown(distDir string) (string, error) {
if distDir == "" {
return "# downloads\n\nNo release is staged in this build. Release artifacts live under\n`/dl/<version>/`, with `/dl/latest/` pointing at the newest.\n\n" + apiSpecLine, nil
}
version := filepath.Base(distDir)
sums, err := parseSums(filepath.Join(distDir, "SHA256SUMS"))
if err != nil {
return "", fmt.Errorf("dist %s: %w", distDir, err)
}
entries, err := os.ReadDir(distDir)
if err != nil {
return "", err
}
var b strings.Builder
fmt.Fprintf(&b, "# downloads — %s\n\n", version)
b.WriteString("| file | size | sha256 |\n|---|---|---|\n")
names := make([]string, 0, len(entries))
for _, e := range entries {
if !e.IsDir() {
names = append(names, e.Name())
}
}
sort.Strings(names)
for _, name := range names {
info, err := os.Stat(filepath.Join(distDir, name))
if err != nil {
return "", err
}
// SHA256SUMS cannot list itself, so its own row has no checksum; an
// empty code span would render as literal backticks.
sha := "—"
if s := sums[name]; s != "" {
sha = "`" + s + "`"
}
fmt.Fprintf(&b, "| [%s](/dl/%s/%s) | %s | %s |\n",
name, version, name, humanSize(info.Size()), sha)
}
fmt.Fprintf(&b, "\nVerify after downloading (checksums: [SHA256SUMS](/dl/%s/SHA256SUMS)):\n\n", version)
b.WriteString(" sha256sum -c SHA256SUMS --ignore-missing\n")
b.WriteString("\n" + apiSpecLine)
return b.String(), nil
}
// apiSpecLine links the served API contract from the downloads page; the spec
// publishes at the site root in every build, dist or not.
const apiSpecLine = "[openapi.json](/openapi.json) — the server HTTP API, OpenAPI 3.1\n"
// parseSums reads sha256sum output: "<hex> <name>" per line.
func parseSums(path string) (map[string]string, error) {
raw, err := os.ReadFile(path)
if err != nil {
return nil, err
}
sums := map[string]string{}
for _, line := range strings.Split(strings.TrimSpace(string(raw)), "\n") {
fields := strings.Fields(line)
if len(fields) == 2 {
sums[strings.TrimPrefix(fields[1], "*")] = fields[0]
}
}
return sums, nil
}
func humanSize(n int64) string {
switch {
case n >= 1<<20:
return fmt.Sprintf("%.1f MiB", float64(n)/(1<<20))
case n >= 1<<10:
return fmt.Sprintf("%.1f KiB", float64(n)/(1<<10))
default:
return fmt.Sprintf("%d B", n)
}
}