Adopt scripts-to-rule-them-all: script/ entrypoints, Makefile shims

This commit is contained in:
2026-07-07 00:21:25 +02:00
parent 247a3c33fd
commit d7d18babc4
16 changed files with 364 additions and 40 deletions

View File

@@ -6,5 +6,5 @@ jobs:
steps: steps:
# actions/checkout v4.2.2, 2026-02-22 # actions/checkout v4.2.2, 2026-02-22
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- run: docker build . - run: script/cibuild
- run: docker build -f Dockerfile.backend . - run: docker build -f Dockerfile.backend .

View File

@@ -1,21 +1,35 @@
.PHONY: dev test lint fmt fmt-check check docker .PHONY: bootstrap setup dev test lint fmt fmt-check check docker hooks
# Standard targets are thin shims; the implementations live in script/
# per the scripts-to-rule-them-all pattern (see the Entrypoints section
# of README.md).
bootstrap:
@script/bootstrap
setup:
@script/setup
dev: dev:
yarn dev yarn dev
test: test:
timeout 30 yarn build @script/test
lint: lint:
yarn prettier --check . @script/lint
fmt: fmt:
yarn prettier --write . @script/fmt
fmt-check: fmt-check:
yarn prettier --check . @script/fmt-check
check: test lint fmt-check check:
@script/check
docker: docker:
timeout 300 docker build -t netwatch . @script/docker
hooks:
@script/install-precommit

View File

@@ -23,6 +23,29 @@ docker build -t netwatch .
docker run -p 8080:8080 netwatch docker run -p 8080:8080 netwatch
``` ```
## 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 dependencies (pinned node via nvm if needed,
yarn via corepack, `yarn install --frozen-lockfile`)
- `script/setup` — make a fresh clone ready for development: bootstrap plus the
git pre-commit hook
- `script/projectname` — print the project name (used for the Docker image tag)
- `script/test` — run the production build as the test (no unit tests yet)
- `script/lint` — run prettier in check mode
- `script/fmt` — format all files (writes)
- `script/fmt-check` — check formatting (read-only)
- `script/check` — run test, lint, and fmt-check
- `script/docker` — build the Docker image tagged via `script/projectname`
- `script/cibuild` — CI entrypoint: plain `docker build .`
- `script/precommit` — run by the git pre-commit hook; runs `script/check`
- `script/install-precommit` — install the git pre-commit hook
## Rationale ## Rationale
When debugging network issues, it's useful to have a persistent at-a-glance view When debugging network issues, it's useful to have a persistent at-a-glance view

64
TODO.md
View File

@@ -1,47 +1,47 @@
# Workflow # Workflow
* branch (from `main`) - branch (from `main`)
* do the work in Next Step - do the work in Next Step
* move Next Step to the top of Completed Steps - move Next Step to the top of Completed Steps
* move the top item of Future Steps into Next Step - move the top item of Future Steps into Next Step
* commit (`TODO.md` changes in the same commit as the work) - commit (`TODO.md` changes in the same commit as the work)
* merge to `main` if the branch is not protected, otherwise open a PR - merge to `main` if the branch is not protected, otherwise open a PR
* push - push
# Status # Status
pre-1.0. No git tags. Backend work in flight on feat/reportbuf-storage pre-1.0. No git tags. Backend work in flight on feat/reportbuf-storage (dirty:
(dirty: src/main.js). Frontend is functional; backend is new and unmerged. src/main.js). Frontend is functional; backend is new and unmerged.
# Next Step # Next Step
Land feat/reportbuf-storage: finish the in-progress src/main.js change, Land feat/reportbuf-storage: finish the in-progress src/main.js change, get make
get make check green, and merge the branch to main. The branch adds the check green, and merge the branch to main. The branch adds the backend (buffered
backend (buffered zstd-compressed report storage), the CI workflow, and zstd-compressed report storage), the CI workflow, and backend repo standard
backend repo standard files, so merging it also closes most compliance files, so merging it also closes most compliance gaps.
gaps.
# Completed Steps # Completed Steps
- 2026-02-27: backend with buffered zstd-compressed report storage; CI - 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints, Makefile
workflow and backend repo standard files; backend Dockerfile fixed (Go shims, README Entrypoints section
1.25, golangci-lint) and moved to repo root (feat/reportbuf-storage, - 2026-02-27: backend with buffered zstd-compressed report storage; CI workflow
unmerged) and backend repo standard files; backend Dockerfile fixed (Go 1.25,
- 2026-02-26: host row layout redesigned with CSS grid; overflow and golangci-lint) and moved to repo root (feat/reportbuf-storage, unmerged)
spacing fixes; nginx config extracted; port hardcoded to 8080 - 2026-02-26: host row layout redesigned with CSS grid; overflow and spacing
- 2026-02-26: debug log panel, median stats, recovery probe, Docker fixes; nginx config extracted; port hardcoded to 8080
build fix, S3 Singapore endpoint added - 2026-02-26: debug log panel, median stats, recovery probe, Docker build fix,
- 2026-02-23: summary box redesign, host pinning, local and UTC clocks, S3 Singapore endpoint added
checks counter - 2026-02-23: summary box redesign, host pinning, local and UTC clocks, checks
- 2026-02-23: hosts sorted by latency; GET instead of HEAD for latency; counter
timeout derived from interval; Hetzner regional endpoints; 3s interval - 2026-02-23: hosts sorted by latency; GET instead of HEAD for latency; timeout
derived from interval; Hetzner regional endpoints; 3s interval
- 2026-01-29: initial NetWatch network latency monitor - 2026-01-29: initial NetWatch network latency monitor
# Future Steps # Future Steps
- Compliance top-up as one small commit: add .editorconfig and add the - Compliance top-up as one small commit: add .editorconfig and add the hooks
hooks target to the Makefile target to the Makefile
- After merge, confirm .gitea/workflows/check.yml is on main and CI is - After merge, confirm .gitea/workflows/check.yml is on main and CI is green
green (main always green policy) (main always green policy)
- Decide what to do with untracked resume.sh: commit it, gitignore it, - Decide what to do with untracked resume.sh: commit it, gitignore it, or delete
or delete it it

143
script/bootstrap Executable file
View File

@@ -0,0 +1,143 @@
#!/bin/sh
# script/bootstrap: install all dependencies needed to build and develop
# this repo. Idempotent: every install is guarded by a check so already
# installed tools are skipped. Base tooling comes from nix, apt, brew,
# or apk (detected in that order); assumes nothing is present. Node is
# used directly if installed; otherwise it is installed at a pinned
# version via nvm (installing nvm itself first, from a hash-verified
# release archive, never curl | sh).
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# Pinned versions, 2026-07-07
NODE_VERSION="22.17.0"
NVM_VERSION="0.40.3"
# sha256 of https://github.com/nvm-sh/nvm/archive/refs/tags/v0.40.3.tar.gz
NVM_SHA256="5f4d6aaa04a177dc93c985e31dbc411ab6b8c6e1e21d8015dbc1372625fcd1d0"
YARN_VERSION="1.22.22"
PKGMGR=""
SUDO=""
APT_UPDATED=""
detect_pkgmgr() {
[ -n "$PKGMGR" ] && return 0
if command -v nix-env >/dev/null 2>&1; then
PKGMGR="nix"
elif command -v apt-get >/dev/null 2>&1; then
PKGMGR="apt"
elif command -v brew >/dev/null 2>&1; then
PKGMGR="brew"
elif command -v apk >/dev/null 2>&1; then
PKGMGR="apk"
else
echo "bootstrap: no supported package manager (nix, apt, brew, apk)" >&2
exit 1
fi
if [ "$PKGMGR" = "apt" ]; then
export DEBIAN_FRONTEND=noninteractive
if [ "$(id -u)" != "0" ]; then
SUDO="sudo"
fi
fi
}
# pkg_install <nix-attr> <apt-pkg> <brew-formula> <apk-pkg>
pkg_install() {
detect_pkgmgr
case "$PKGMGR" in
nix) nix-env -iA "nixpkgs.$1" ;;
apt)
if [ -z "$APT_UPDATED" ]; then
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get update
APT_UPDATED=1
fi
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y "$2"
;;
brew) brew install "$3" ;;
apk) apk add --no-cache "$4" ;;
esac
}
missing() {
! command -v "$1" >/dev/null 2>&1
}
# verify_sha256 <file> <expected-hash>
verify_sha256() {
if command -v sha256sum >/dev/null 2>&1; then
actual="$(sha256sum "$1" | cut -d' ' -f1)"
else
actual="$(shasum -a 256 "$1" | cut -d' ' -f1)"
fi
if [ "$actual" != "$2" ]; then
echo "bootstrap: sha256 mismatch for $1" >&2
echo " expected: $2" >&2
echo " actual: $actual" >&2
exit 1
fi
}
# nvm is a bash script; run a command in a bash with nvm loaded
nvm_sh() {
bash -c ". \"\$HOME/.nvm/nvm.sh\" && $*"
}
ensure_nvm() {
[ -s "$HOME/.nvm/nvm.sh" ] && return 0
# nvm prerequisites; nvm itself requires bash
if missing bash; then pkg_install bash bash bash bash; fi
if missing curl; then pkg_install curl curl curl curl; fi
if missing git; then pkg_install git git git git; fi
tmp="$(mktemp -d)"
curl -fsSL -o "$tmp/nvm.tar.gz" \
"https://github.com/nvm-sh/nvm/archive/refs/tags/v${NVM_VERSION}.tar.gz"
verify_sha256 "$tmp/nvm.tar.gz" "$NVM_SHA256"
mkdir -p "$HOME/.nvm"
tar -xzf "$tmp/nvm.tar.gz" -C "$HOME/.nvm" --strip-components=1
rm -rf "$tmp"
}
ensure_node() {
if ! missing node; then return 0; fi
ensure_nvm
nvm_sh "nvm install $NODE_VERSION"
}
ensure_yarn() {
if ! missing yarn; then return 0; fi
if ! missing corepack; then
corepack enable
corepack prepare "yarn@$YARN_VERSION" --activate
elif [ -s "$HOME/.nvm/nvm.sh" ]; then
nvm_sh "nvm use $NODE_VERSION >/dev/null && corepack enable && \
corepack prepare yarn@$YARN_VERSION --activate"
else
npm install -g "yarn@$YARN_VERSION"
fi
}
install_js_deps() {
if missing yarn && [ -s "$HOME/.nvm/nvm.sh" ]; then
nvm_sh "nvm use $NODE_VERSION >/dev/null && cd \"$ROOT\" && \
yarn install --frozen-lockfile"
else
yarn install --frozen-lockfile
fi
}
main() {
cd "$ROOT"
if missing make; then pkg_install gnumake make make make; fi
if missing git; then pkg_install git git git git; fi
ensure_node
ensure_yarn
install_js_deps
echo "bootstrap complete"
}
main "$@"

14
script/check Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
# script/check: run all checks (test, lint, fmt-check). Our own
# extension to scripts-to-rule-them-all. Must not modify any files.
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
main() {
"$SCRIPT_DIR/test"
"$SCRIPT_DIR/lint"
"$SCRIPT_DIR/fmt-check"
}
main "$@"

13
script/cibuild Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
# script/cibuild: run the CI build. The Dockerfile runs make check, so
# a successful build implies all checks pass.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
docker build .
}
main "$@"

14
script/docker Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
# script/docker: build the Docker image tagged with the project name.
# The tag comes from script/projectname.
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
main() {
cd "$ROOT"
timeout 300 docker build -t "$("$SCRIPT_DIR/projectname")" .
}
main "$@"

12
script/fmt Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/sh
# script/fmt: format all files (writes).
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
yarn prettier --write .
}
main "$@"

13
script/fmt-check Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
# script/fmt-check: check formatting (read-only). Same scope as
# script/fmt, but fails instead of writing.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
yarn prettier --check .
}
main "$@"

16
script/install-precommit Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
# script/install-precommit: install the git pre-commit hook that runs
# script/precommit. Our own extension to scripts-to-rule-them-all.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
hook=".git/hooks/pre-commit"
printf '#!/bin/sh\nset -e\nscript/precommit\n' > "$hook"
chmod +x "$hook"
echo "pre-commit hook installed: runs script/precommit"
}
main "$@"

12
script/lint Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/sh
# script/lint: run the linter (prettier in check mode).
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
yarn prettier --check .
}
main "$@"

12
script/precommit Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/sh
# script/precommit: run by the git pre-commit hook; fails the commit if
# checks fail. Our own extension to scripts-to-rule-them-all.
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
main() {
"$SCRIPT_DIR/check"
}
main "$@"

12
script/projectname Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/sh
# script/projectname: output the name of this project. Our own
# extension to scripts-to-rule-them-all. Other scripts that need the
# name (e.g. script/docker) call this, so they can stay identical
# across all repos.
set -eu
main() {
echo "netwatch"
}
main "$@"

13
script/setup Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
# script/setup: set up the repo for development after a fresh clone:
# installs dependencies and the git pre-commit hook.
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
main() {
"$SCRIPT_DIR/bootstrap"
"$SCRIPT_DIR/install-precommit"
}
main "$@"

13
script/test Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
# script/test: run the test suite. This repo has no unit tests; the
# production build serves as the test (fails on broken code).
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
timeout 30 yarn build
}
main "$@"