Files
lora.vegas/README.md
clawbot 25b6c0a9de
All checks were successful
check / check (push) Successful in 1m23s
Run the lint inside Docker via Dockerfile.lint (closes #38)
Add a root Dockerfile.lint that runs `hugo --minify --printPathWarnings`
as a build step, so a successful build IS a clean lint, and reduce
script/lint to building that file. There is no host lint path and
deliberately no "am I already inside a container?" branch, which would
be a host lint path in disguise.

The containerisation boundary is lint only, per the owner ruling on the
issue: formatting is not a lint, so script/fmt and script/fmt-check stay
on the host, unchanged in version, scope and flags. That also removes
the forced duplication of prettier's settings between a script and a
Dockerfile, and with it the keep-in-sync notes that duplication needed.

Dockerfile.lint has exactly one stage on purpose. A whole-file
`docker build -f Dockerfile.lint .` builds only the file's last stage,
and sibling stages off a shared base carry no ordering edge, so a second
stage beside the lint would be silently skipped by exactly the
invocation the canonical org-wide script/lint uses -- a green that
linted nothing, which the per-stage CHECK_EPOCH guard cannot catch
because the stage that did run satisfies it. With one stage there is
nothing to skip and script/lint needs no --target. A comment in the file
says that any second check added here must be chained or carry an
explicit ordering edge, never left as a sibling.

Its first four instructions are byte-identical to the main Dockerfile's
and in the same order, so the expensive `RUN script/bootstrap` layer
that compiles the pinned Hugo from source is shared between the two
images rather than paid twice.

Resolve the recursion by direction, not detection. `make check` calls
script/lint, and script/lint is now a `docker build`, so `RUN make
check` in an image would attempt a docker build inside a build step
where there is no daemon. The main Dockerfile therefore runs the
individual non-lint checks -- script/test and script/fmt-check, as
separate RUN lines under the CHECK_EPOCH guard -- matching the canonical
shape, and only the lint is absent from it. script/cibuild runs
script/lint first, for fail-fast feedback: on a runner with no cached
bootstrap layer a lint failure should not wait behind a Hugo build from
source. CI coverage is therefore unchanged, and it runs the same scripts
a developer runs.

Caching is waived for the lint in the shape this repo already settled:
ARG CHECK_EPOCH with no default, guarded with
`[ -n "$CHECK_EPOCH" ] || exit 1`, and the value expanded into the
linted command as well as the guard, so invalidation never rests on
BuildKit's treatment of an unreferenced ARG. Every image-building
entrypoint generates and passes it -- script/cibuild, script/docker,
script/lint -- each as a whole assignment rather than inline, for the
`set -e` reason script/cibuild documents.

script/lint builds with `--output type=cacheonly`: the build is run for
its exit status, not for an image, and because the lint layer is
cache-busted on every invocation an exporting build leaves one dangling
image per lint run. On a host shared with other work that accumulates.
The build cache is unaffected, so script/bootstrap still hits, and
failures still propagate.

Two divergences from REPO_POLICIES.md, stated rather than buried:

  - REPO_POLICIES.md:92, "all Dockerfiles must run `make check`". That
    rule and "every lint run happens in Docker" cannot both hold once
    `make check` contains the lint.
  - REPO_POLICIES.md:102-168, which requires a separate lint stage whose
    result the build stage depends on through
    `COPY --from=lint /src/go.sum /dev/null`, on the stated grounds that
    without the edge "the build stage would not wait for lint to finish
    and a lint failure might not fail the overall build". No such edge
    exists here: the lint is its own file and its own build, sequenced
    by script/cibuild rather than by BuildKit. Both sections are
    superseded upstream by 12e8db8 in sneak/prompts, which deletes the
    Go multistage lint stage and its ordering trick for the same reason
    -- that stage ran `make lint`, which is now a docker build.

Verified: two consecutive script/lint runs on an unchanged tree both
executed hugo for real, distinct epochs echoed, script/bootstrap CACHED,
second run 0.85s; a whole-file `docker build -f Dockerfile.lint .` with
the argument and no --target ran the lint for real; a bare build with no
argument failed closed on the guard; a planted template error failed the
lint with hugo's own render error and made script/cibuild exit non-zero
in 0.6s with the main image build never starting; a planted over-long
line failed the host script/fmt-check; both reverted and re-run clean;
`make check`, script/docker and script/cibuild all green with every
check layer observed executing rather than served from cache, and the
bootstrap layer CACHED in both images. The deploy path is byte-identical
to main: .gitea/, script/bootstrap, script/test and .dockerignore are
untouched.
2026-08-10 13:20:40 +00:00

140 lines
5.6 KiB
Markdown

# lora.vegas
`lora.vegas` is the website of the Las Vegas Meshtastic and LoRa community: an
MIT-licensed single-page static site, built with Hugo, by
[@sneak](https://sneak.berlin).
It publishes what the local mesh needs in one linkable place:
- Mesh channel configurations
- Community coordination links (Discord, Signal)
- Meetup information
- Local resources
## Getting Started
From a fresh clone, `make setup` installs every build dependency (git, make, go,
the pinned Hugo, node/npm) and the git pre-commit hook, and `make serve` starts
the Hugo development server:
```bash
git clone git@git.eeqj.de:sneak/lora.vegas.git
cd lora.vegas
make setup
make serve
```
Then open <http://localhost:1313> to preview the site.
To produce the production build, which writes the rendered site to `public/`:
```bash
make test
```
Before committing, run the full check suite — the production build, the lint
build, and the formatting check:
```bash
make check
```
The lint runs inside Docker, so `make check` needs a working Docker daemon;
there is no host fallback. On a machine that has never built the image, the
first run compiles the pinned Hugo from source, which takes minutes; later runs
reuse that cached layer.
`make fmt` rewrites the repo's markdown and CSS to the project's prettier
settings; run it if `make check` fails on formatting.
To contribute to this site, contact **sneak@sneak.berlin** for git repository
access.
## Entrypoints
This repository adheres to the
[Scripts to Rule Them All](https://github.com/github/scripts-to-rule-them-all)
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, 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
build
- `script/lint` — a clean build that surfaces broken links and path collisions,
run inside Docker: it builds `Dockerfile.lint`, where the lint is a build
step, so a successful build is a clean lint
- `script/fmt` — format every markdown and CSS file in the repo with prettier;
the exclusions live in `.prettierignore` with the reason for each
- `script/fmt-check` — check that formatting (read-only)
- `script/check` — run `script/test`, `script/lint`, then `script/fmt-check`;
modifies no tracked files
- `script/docker` — build the Docker image tagged with the project name
- `script/cibuild` — the CI build: `script/lint` first, for fail-fast feedback,
then the main image, which runs the non-lint checks
- `script/install-precommit` — install the git pre-commit hook that runs
`script/check`
Every lint run for this repo happens inside a container, and only the lint does.
`script/lint` has no host path and no "already inside a container?" branch, so
what a developer runs and what CI runs are the same build. `script/fmt` and
`script/fmt-check` run on the host: a formatting check is not a lint.
That is also why the main `Dockerfile` runs `script/test` and `script/fmt-check`
rather than `make check`. `make check` calls `script/lint`, which is itself a
`docker build`, so a `make check` inside an image would attempt a docker build
in a bare Alpine with no docker client and no daemon socket. The lint is not
skipped — `script/cibuild` runs it first, in its own container, before the main
image build starts.
Build any image through `script/cibuild`, `script/docker` or `script/lint` only.
All three pass a per-invocation `CHECK_EPOCH` build argument that the
Dockerfiles require, so a check layer can never be served from cache — without
it Docker returns a green it did not earn. A bare `docker build` fails closed on
the `CHECK_EPOCH` guard rather than caching its way to a false success.
A convenience `make serve` target runs `hugo server` for local preview.
## Rationale
The Las Vegas Meshtastic and LoRa community needs one durable, linkable place
for its channel configurations and group links. Those details otherwise live
inside a Discord or Signal thread, where they scroll away, cannot be linked to
from outside, are invisible to anyone who has not already joined, and quietly go
stale. A static site at a stable domain is the opposite of that: one URL to hand
to a newcomer, and one place to correct when a channel changes.
## Design
The site is a single page. All of its content is one Hugo content file,
`content/_index.md`, rendered by a minimal theme vendored in-repo at
`themes/loravega/` — there is no upstream theme dependency and no submodule.
The theme's `layouts/_default/baseof.html` inlines
`themes/loravega/static/css/style.css` into a `<style>` block with Hugo's
`readFile`, so the whole site ships as a single HTML document with no external
CSS request and no second round trip.
`hugo --minify` builds the site into `public/`. Deployment is automatic: on push
to `main`, the Gitea Actions workflow `.gitea/workflows/deploy.yml` builds the
site and publishes `public/` to Cloudflare Pages.
## TODO
The live task list is in [TODO.md](TODO.md).
## License
MIT. See [LICENSE](LICENSE). This covers everything in the repository — the Hugo
configuration, the `script/` entrypoints, the vendored `themes/loravega/`
templates and CSS, and the site content in `content/`.
## Author
[@sneak](https://sneak.berlin)