@@ -9,7 +9,11 @@ FROM alpine@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4
|
||||
WORKDIR /src
|
||||
|
||||
# Install build dependencies first so the layer caches until the
|
||||
# scripts change (script/bootstrap installs git, make, hugo, node/npm).
|
||||
# scripts change (script/bootstrap installs git, make, go, hugo,
|
||||
# node/npm). Hugo is not an apk package here: script/bootstrap builds
|
||||
# the exact pinned version with `go install`, hash-verified against
|
||||
# sum.golang.org, so the published artifact does not depend on whatever
|
||||
# hugo this base image's repos happen to serve.
|
||||
COPY script/ script/
|
||||
RUN script/bootstrap
|
||||
|
||||
|
||||
@@ -46,8 +46,10 @@ standard: normalized scripts in `script/` are the entrypoints for the
|
||||
development workflow, and the Makefile targets are thin shims that call them. We
|
||||
provide:
|
||||
|
||||
- `script/bootstrap` — install all build dependencies (git, make, hugo,
|
||||
node/npm) idempotently
|
||||
- `script/bootstrap` — install all build dependencies (git, make, go, hugo,
|
||||
node/npm) idempotently. Hugo is pinned to an exact version and installed with
|
||||
`go install`, which verifies it against `sum.golang.org`; the version is the
|
||||
`HUGO_VERSION` constant at the top of the script
|
||||
- `script/setup` — prepare a fresh clone: run `script/bootstrap` and install the
|
||||
git pre-commit hook
|
||||
- `script/test` — the correctness check: a clean `hugo --minify` production
|
||||
|
||||
27
TODO.md
27
TODO.md
@@ -15,7 +15,9 @@ pre-1.0
|
||||
No git tags. The site is live and now has the scripts-to-rule-them-all scaffold
|
||||
(`Makefile`, `script/`, `Dockerfile`, `check.yml`); still missing `LICENSE` and
|
||||
policy files. Every external reference in the repo is now pinned by
|
||||
cryptographic hash (or, for the wrangler CLI install, an exact version).
|
||||
cryptographic hash (or, for the wrangler CLI install, an exact version), and the
|
||||
Hugo that builds the published site is a deliberate pinned version rather than
|
||||
whatever the base image's package repo serves.
|
||||
|
||||
# Next Step
|
||||
|
||||
@@ -25,6 +27,29 @@ Update `README.md` accordingly.
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-08-09: replaced `hugo.toml`'s deprecated `languageCode` key with `locale`
|
||||
(closes #18). Hugo deprecated `languageCode` in v0.158.0, so the Hugo pinned
|
||||
in the preceding commit warns about it; left alone it would become a third
|
||||
routinely-ignored warning, and a latent breakage when the key is removed.
|
||||
Deliberately sequenced **after** the Hugo version move and in the same branch:
|
||||
under the apk hugo 0.139.0 that CI ran until now, `locale` is an unknown key
|
||||
that is silently ignored, which downgrades the generated RSS from
|
||||
`<language>en-us</language>` to `<language>en</language>` with no warning and
|
||||
exit 0. Verified on hugo v0.164.0 that the RSS `<language>` still reads
|
||||
`en-us`, the `lang` attribute is unchanged, and `public/` is byte-identical to
|
||||
the preceding commit's output
|
||||
- 2026-08-09: installed Hugo at a deliberate, hash-verified version instead of
|
||||
taking whatever alpine ships (closes #26). `script/bootstrap` no longer does
|
||||
`pkg_install hugo`; it installs `github.com/gohugoio/hugo@v0.164.0` with
|
||||
`go install`, which verifies the module against `sum.golang.org`. The version
|
||||
is a commented constant, as is the Go toolchain (`go1.26.5`) — hugo v0.164.0
|
||||
requires go >= 1.26.0 and alpine 3.21 ships go 1.23.9 with
|
||||
`GOTOOLCHAIN=local`, so a bare `go install` refuses to run. `CGO_ENABLED=0` is
|
||||
deliberate: standard Hugo, not extended, because this site has no SCSS, no
|
||||
`resources.ToCSS`, no PostCSS and no image processing. This moves the build
|
||||
off apk's hugo 0.139.0, about two years behind, onto the current stable.
|
||||
Rendered output across the whole `public/` tree is unchanged except the
|
||||
`meta name=generator` version string
|
||||
- 2026-08-09: made `script/check` run `script/lint` (closes #9). It previously
|
||||
ran only `fmt-check` then `test`, so `script/lint` executed nowhere — not in
|
||||
`make check`, not in the pre-commit hook, and not in CI, even though the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
baseURL = 'https://lora.vegas/'
|
||||
languageCode = 'en-us'
|
||||
locale = 'en-us'
|
||||
title = 'LoRa Vegas — Las Vegas Meshtastic Community'
|
||||
theme = 'loravega'
|
||||
|
||||
|
||||
115
script/bootstrap
115
script/bootstrap
@@ -2,9 +2,10 @@
|
||||
# script/bootstrap: install all dependencies needed to build and develop
|
||||
# this Hugo site, idempotently. Base tooling comes from nix, apt, brew,
|
||||
# or apk (detected in that order); assumes NOTHING is present (not git,
|
||||
# make, hugo, or node). Installs hugo (the site build) and node/npm
|
||||
# (prettier, used to format the repo's own markdown docs). Every install
|
||||
# is guarded by a check so already-installed tools are skipped.
|
||||
# make, go, hugo, or node). Installs hugo (the site build, at the exact
|
||||
# version pinned below) and node/npm (prettier, used to format the
|
||||
# repo's own markdown docs). Every install is guarded by a check so
|
||||
# already-installed tools are skipped.
|
||||
set -eu
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
@@ -12,6 +13,50 @@ ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
PKGMGR=""
|
||||
SUDO=""
|
||||
|
||||
# --- Hugo -------------------------------------------------------------
|
||||
#
|
||||
# Hugo produces the published artifact, so its version is a property of
|
||||
# the site's output, not of the build environment. It is therefore
|
||||
# pinned here rather than taken from whatever the distro serves: before
|
||||
# this, alpine 3.21's apk supplied hugo 0.139.0 -- a version chosen by
|
||||
# nobody, roughly two years behind upstream, and liable to change
|
||||
# silently whenever the base image digest moves.
|
||||
#
|
||||
# `go install` is the hash-verified mechanism: Go checks the module
|
||||
# against the sum.golang.org checksum database. That is the mechanism
|
||||
# REPO_POLICIES.md already names for Go, and it needs no hand-maintained
|
||||
# sha256. The other tools this script installs stay on the package
|
||||
# manager, which #19 settled is fine for build-time conveniences.
|
||||
#
|
||||
# hugo v0.164.0, 2026-07-06
|
||||
HUGO_VERSION="v0.164.0"
|
||||
|
||||
# hugo v0.164.0's go.mod requires go >= 1.26.0, and alpine 3.21's `go`
|
||||
# package is 1.23.9 built with GOTOOLCHAIN=local, so a bare `go install`
|
||||
# refuses to run at all. Naming the toolchain explicitly makes Go fetch
|
||||
# it through the module proxy and verify it against sum.golang.org like
|
||||
# any other module, so the chain stays hash-verified end to end -- and
|
||||
# the Go version that compiles hugo becomes deliberate too, instead of
|
||||
# being inherited from whatever the base image happens to ship.
|
||||
# go1.26.5, 2026-08-09
|
||||
HUGO_GOTOOLCHAIN="go1.26.5"
|
||||
|
||||
# Standard hugo, not hugo extended: CGO_ENABLED=0 is deliberate.
|
||||
# Verified that this site uses nothing extended provides -- there are no
|
||||
# .scss/.sass files, no resources.ToCSS, no PostCSS, and no image
|
||||
# processing (.Resize/.Fill/.Fit/images.* are all absent). The CSS is
|
||||
# plain and inlined by `readFile` in baseof.html. The `+extended` on the
|
||||
# apk build this replaces was incidental, not a requirement, so do not
|
||||
# assume a future change needs it without rechecking the above.
|
||||
HUGO_CGO_ENABLED="0"
|
||||
|
||||
# Where the hugo binary lands. It has to be on the default PATH of a
|
||||
# *fresh* shell, not just of this script: the Dockerfile's `RUN make
|
||||
# check` and deploy.yml's `script/test` step each start their own shell
|
||||
# and would never see a GOPATH bin directory. Overridable so an
|
||||
# unprivileged install can point somewhere writable.
|
||||
HUGO_BIN_DIR="${HUGO_BIN_DIR:-/usr/local/bin}"
|
||||
|
||||
detect_pkgmgr() {
|
||||
[ -n "$PKGMGR" ] && return 0
|
||||
if command -v nix-env >/dev/null 2>&1; then
|
||||
@@ -28,13 +73,17 @@ detect_pkgmgr() {
|
||||
fi
|
||||
if [ "$PKGMGR" = "apt" ]; then
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
SUDO="sudo"
|
||||
fi
|
||||
detect_sudo
|
||||
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get update
|
||||
fi
|
||||
}
|
||||
|
||||
detect_sudo() {
|
||||
if [ -z "$SUDO" ] && [ "$(id -u)" != "0" ]; then
|
||||
SUDO="sudo"
|
||||
fi
|
||||
}
|
||||
|
||||
# pkg_install <nix-attr> <apt-pkg> <brew-formula> <apk-pkg>
|
||||
pkg_install() {
|
||||
detect_pkgmgr
|
||||
@@ -50,6 +99,56 @@ missing() {
|
||||
! command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# True when the hugo already on PATH is the pinned version. Unlike the
|
||||
# other tools, mere presence is not good enough here: an older hugo has
|
||||
# to be replaced, not accepted, or the pin means nothing.
|
||||
hugo_pinned() {
|
||||
command -v hugo >/dev/null 2>&1 || return 1
|
||||
# `hugo version` prints e.g. "hugo v0.164.0 linux/amd64 ..." for a
|
||||
# `go install` build, or "hugo v0.164.0-ce2470e+extended ..." for an
|
||||
# official release binary. Compare only the vX.Y.Z part: a build of
|
||||
# the same version that happens to be extended renders this site
|
||||
# identically (see HUGO_CGO_ENABLED above), so there is no reason to
|
||||
# overwrite a developer's existing matching install.
|
||||
have="$(hugo version 2>/dev/null | awk '{print $2}' | sed 's/[-+].*//')"
|
||||
[ "$have" = "$HUGO_VERSION" ]
|
||||
}
|
||||
|
||||
install_hugo() {
|
||||
detect_sudo
|
||||
|
||||
# The Go toolchain is a build-time convenience like git and make, so
|
||||
# it comes from the package manager; the thing that must be
|
||||
# deliberate is what it builds, which HUGO_VERSION and
|
||||
# HUGO_GOTOOLCHAIN pin.
|
||||
if missing go; then pkg_install go golang-go go go; fi
|
||||
|
||||
# Build as the invoking user into a scratch GOBIN, then place the
|
||||
# binary with `install`. Running the whole `go install` under sudo
|
||||
# would work but would populate root's module cache instead of the
|
||||
# user's, which is needlessly slow and surprising on a workstation.
|
||||
gobin="$(mktemp -d)"
|
||||
CGO_ENABLED="$HUGO_CGO_ENABLED" \
|
||||
GOTOOLCHAIN="$HUGO_GOTOOLCHAIN" \
|
||||
GOBIN="$gobin" \
|
||||
go install "github.com/gohugoio/hugo@${HUGO_VERSION}"
|
||||
$SUDO install -d "$HUGO_BIN_DIR"
|
||||
$SUDO install -m 0755 "$gobin/hugo" "$HUGO_BIN_DIR/hugo"
|
||||
rm -rf "$gobin"
|
||||
|
||||
# Drop any cached PATH lookup of the hugo we just replaced, so the
|
||||
# check below tests the new binary and not the old one.
|
||||
hash -r 2>/dev/null || true
|
||||
|
||||
# Fail loudly rather than let a later build run on a shadowing hugo
|
||||
# from somewhere earlier in PATH.
|
||||
if ! hugo_pinned; then
|
||||
echo "bootstrap: installed $HUGO_VERSION into $HUGO_BIN_DIR but" \
|
||||
"'hugo' on PATH is still $(hugo version 2>/dev/null || echo absent)" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
cd "$ROOT"
|
||||
|
||||
@@ -63,8 +162,8 @@ main() {
|
||||
git submodule update --init --recursive
|
||||
fi
|
||||
|
||||
# Site build.
|
||||
if missing hugo; then pkg_install hugo hugo hugo hugo; fi
|
||||
# Site build. Pinned and hash-verified -- see the HUGO_* constants.
|
||||
if ! hugo_pinned; then install_hugo; fi
|
||||
|
||||
# node/npm provide prettier (via npx) for formatting the docs.
|
||||
if missing node; then pkg_install nodejs nodejs node nodejs; fi
|
||||
|
||||
Reference in New Issue
Block a user