Adopt scripts-to-rule-them-all: script/ entrypoints, Makefile shims
This commit is contained in:
35
Makefile
35
Makefile
@@ -6,19 +6,42 @@ GIT_REVISION_SHORT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "un
|
|||||||
VERSION_PKG := git.eeqj.de/sneak/routewatch/internal/version
|
VERSION_PKG := git.eeqj.de/sneak/routewatch/internal/version
|
||||||
LDFLAGS := -X $(VERSION_PKG).GitRevision=$(GIT_REVISION) -X $(VERSION_PKG).GitRevisionShort=$(GIT_REVISION_SHORT)
|
LDFLAGS := -X $(VERSION_PKG).GitRevision=$(GIT_REVISION) -X $(VERSION_PKG).GitRevisionShort=$(GIT_REVISION_SHORT)
|
||||||
|
|
||||||
.PHONY: test fmt lint build clean run asupdate
|
.PHONY: bootstrap setup check test lint fmt fmt-check docker hooks build clean run asupdate
|
||||||
|
|
||||||
all: test
|
all: test
|
||||||
|
|
||||||
test: lint
|
# Install all development dependencies.
|
||||||
go test -v ./...
|
bootstrap:
|
||||||
|
@script/bootstrap
|
||||||
|
|
||||||
|
# Prepare a fresh clone: bootstrap plus pre-commit hook.
|
||||||
|
setup:
|
||||||
|
@script/setup
|
||||||
|
|
||||||
|
# Combined pre-commit/CI gate: tests, lint, format check.
|
||||||
|
check:
|
||||||
|
@script/check
|
||||||
|
|
||||||
|
test:
|
||||||
|
@script/test
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
go fmt ./...
|
@script/fmt
|
||||||
|
|
||||||
|
# Check if code is formatted (read-only).
|
||||||
|
fmt-check:
|
||||||
|
@script/fmt-check
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
go vet ./...
|
@script/lint
|
||||||
golangci-lint run
|
|
||||||
|
# Build Docker image.
|
||||||
|
docker:
|
||||||
|
@script/docker
|
||||||
|
|
||||||
|
# Install pre-commit hook.
|
||||||
|
hooks:
|
||||||
|
@script/install-precommit
|
||||||
|
|
||||||
build:
|
build:
|
||||||
CGO_ENABLED=1 go build -ldflags "$(LDFLAGS)" -o bin/routewatch cmd/routewatch/main.go
|
CGO_ENABLED=1 go build -ldflags "$(LDFLAGS)" -o bin/routewatch cmd/routewatch/main.go
|
||||||
|
|||||||
29
README.md
29
README.md
@@ -189,6 +189,35 @@ make lint
|
|||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 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 development dependencies (go,
|
||||||
|
golangci-lint, Go module download)
|
||||||
|
- `script/setup` — make a fresh clone ready for development: runs
|
||||||
|
`script/bootstrap`, then `script/install-precommit`
|
||||||
|
- `script/projectname` — print the project name (used for the Docker
|
||||||
|
image tag)
|
||||||
|
- `script/test` — run the test suite
|
||||||
|
(`go test -timeout 30s -race -cover ./...`, verbose rerun on failure)
|
||||||
|
- `script/lint` — run `go vet ./...` and `golangci-lint run`
|
||||||
|
- `script/fmt` — format all code (writes)
|
||||||
|
- `script/fmt-check` — check formatting (read-only)
|
||||||
|
- `script/check` — run `script/test`, `script/lint`, and
|
||||||
|
`script/fmt-check`
|
||||||
|
- `script/docker` — build the Docker image tagged via
|
||||||
|
`script/projectname`
|
||||||
|
- `script/cibuild` — CI entrypoint: `docker build .`
|
||||||
|
- `script/precommit` — pre-commit gate: `go mod tidy` + `go fmt` (must
|
||||||
|
not change files), then `script/check`
|
||||||
|
- `script/install-precommit` — install the git pre-commit hook that
|
||||||
|
runs `script/precommit`
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
See LICENSE file.
|
See LICENSE file.
|
||||||
|
|||||||
2
TODO.md
2
TODO.md
@@ -23,6 +23,8 @@ runs make check on main.
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints,
|
||||||
|
Makefile shims, README Entrypoints section
|
||||||
- 2026-02-22: repo policy compliance: required policy files, .gitignore
|
- 2026-02-22: repo policy compliance: required policy files, .gitignore
|
||||||
update, Makefile fmt-check/check/docker/hooks targets, gofmt pass
|
update, Makefile fmt-check/check/docker/hooks targets, gofmt pass
|
||||||
(repo-policies-compliance, unmerged)
|
(repo-policies-compliance, unmerged)
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ const (
|
|||||||
statsWindow = time.Hour
|
statsWindow = time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// ASNFetcher handles background WHOIS lookups for ASNs.
|
// ASNFetcher handles background WHOIS lookups for ASNs.
|
||||||
type ASNFetcher struct {
|
type ASNFetcher struct {
|
||||||
db database.Store
|
db database.Store
|
||||||
@@ -55,9 +54,9 @@ type ASNFetcher struct {
|
|||||||
consecutiveFails int
|
consecutiveFails int
|
||||||
|
|
||||||
// hourly stats tracking
|
// hourly stats tracking
|
||||||
statsMu sync.Mutex
|
statsMu sync.Mutex
|
||||||
successTimes []time.Time
|
successTimes []time.Time
|
||||||
errorTimes []time.Time
|
errorTimes []time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewASNFetcher creates a new ASN fetcher.
|
// NewASNFetcher creates a new ASN fetcher.
|
||||||
|
|||||||
@@ -330,19 +330,19 @@ func (s *Server) handleStats() http.HandlerFunc {
|
|||||||
|
|
||||||
// GCStats represents garbage collection statistics
|
// GCStats represents garbage collection statistics
|
||||||
type GCStats struct {
|
type GCStats struct {
|
||||||
NumGC uint32 `json:"num_gc"`
|
NumGC uint32 `json:"num_gc"`
|
||||||
TotalPauseMs uint64 `json:"total_pause_ms"`
|
TotalPauseMs uint64 `json:"total_pause_ms"`
|
||||||
LastPauseMs float64 `json:"last_pause_ms"`
|
LastPauseMs float64 `json:"last_pause_ms"`
|
||||||
HeapAllocBytes uint64 `json:"heap_alloc_bytes"`
|
HeapAllocBytes uint64 `json:"heap_alloc_bytes"`
|
||||||
HeapSysBytes uint64 `json:"heap_sys_bytes"`
|
HeapSysBytes uint64 `json:"heap_sys_bytes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StreamStats represents stream statistics including announcements/withdrawals
|
// StreamStats represents stream statistics including announcements/withdrawals
|
||||||
type StreamStats struct {
|
type StreamStats struct {
|
||||||
Announcements uint64 `json:"announcements"`
|
Announcements uint64 `json:"announcements"`
|
||||||
Withdrawals uint64 `json:"withdrawals"`
|
Withdrawals uint64 `json:"withdrawals"`
|
||||||
RouteChurnPerSec float64 `json:"route_churn_per_sec"`
|
RouteChurnPerSec float64 `json:"route_churn_per_sec"`
|
||||||
BGPPeerCount int `json:"bgp_peer_count"`
|
BGPPeerCount int `json:"bgp_peer_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatsResponse represents the API statistics response
|
// StatsResponse represents the API statistics response
|
||||||
@@ -499,19 +499,19 @@ func (s *Server) handleStats() http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stats := StatsResponse{
|
stats := StatsResponse{
|
||||||
Uptime: uptime,
|
Uptime: uptime,
|
||||||
TotalMessages: metrics.TotalMessages,
|
TotalMessages: metrics.TotalMessages,
|
||||||
TotalBytes: metrics.TotalBytes,
|
TotalBytes: metrics.TotalBytes,
|
||||||
TotalWireBytes: metrics.TotalWireBytes,
|
TotalWireBytes: metrics.TotalWireBytes,
|
||||||
MessagesPerSec: metrics.MessagesPerSec,
|
MessagesPerSec: metrics.MessagesPerSec,
|
||||||
MbitsPerSec: metrics.BitsPerSec / bitsPerMegabit,
|
MbitsPerSec: metrics.BitsPerSec / bitsPerMegabit,
|
||||||
WireMbitsPerSec: metrics.WireBitsPerSec / bitsPerMegabit,
|
WireMbitsPerSec: metrics.WireBitsPerSec / bitsPerMegabit,
|
||||||
Connected: metrics.Connected,
|
Connected: metrics.Connected,
|
||||||
ConnectionDuration: connectionDuration,
|
ConnectionDuration: connectionDuration,
|
||||||
ReconnectCount: metrics.ReconnectCount,
|
ReconnectCount: metrics.ReconnectCount,
|
||||||
GoVersion: runtime.Version(),
|
GoVersion: runtime.Version(),
|
||||||
Goroutines: runtime.NumGoroutine(),
|
Goroutines: runtime.NumGoroutine(),
|
||||||
MemoryUsage: humanize.Bytes(memStats.Alloc),
|
MemoryUsage: humanize.Bytes(memStats.Alloc),
|
||||||
GC: GCStats{
|
GC: GCStats{
|
||||||
NumGC: memStats.NumGC,
|
NumGC: memStats.NumGC,
|
||||||
TotalPauseMs: memStats.PauseTotalNs / uint64(nanosecondsPerMillisecond),
|
TotalPauseMs: memStats.PauseTotalNs / uint64(nanosecondsPerMillisecond),
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ type Streamer struct {
|
|||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
running bool
|
running bool
|
||||||
metrics *metrics.Tracker
|
metrics *metrics.Tracker
|
||||||
totalDropped uint64 // Total dropped messages across all handlers
|
totalDropped uint64 // Total dropped messages across all handlers
|
||||||
random *rand.Rand // Random number generator for backpressure drops
|
random *rand.Rand // Random number generator for backpressure drops
|
||||||
bgpPeers map[string]bool // Track active BGP peers by peer IP
|
bgpPeers map[string]bool // Track active BGP peers by peer IP
|
||||||
bgpPeersMu sync.RWMutex // Protects bgpPeers map
|
bgpPeersMu sync.RWMutex // Protects bgpPeers map
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
hoursPerDay = 24
|
hoursPerDay = 24
|
||||||
daysPerMonth = 30
|
daysPerMonth = 30
|
||||||
cidrPartCount = 2 // A CIDR has two parts: prefix and length
|
cidrPartCount = 2 // A CIDR has two parts: prefix and length
|
||||||
)
|
)
|
||||||
|
|
||||||
// timeSince returns a human-readable duration since the given time
|
// timeSince returns a human-readable duration since the given time
|
||||||
@@ -109,16 +109,16 @@ func initTemplates() {
|
|||||||
|
|
||||||
// Create common template functions
|
// Create common template functions
|
||||||
funcs := template.FuncMap{
|
funcs := template.FuncMap{
|
||||||
"timeSince": timeSince,
|
"timeSince": timeSince,
|
||||||
"urlEncode": url.QueryEscape,
|
"urlEncode": url.QueryEscape,
|
||||||
"prefixURL": prefixURL,
|
"prefixURL": prefixURL,
|
||||||
"appName": func() string { return version.Name },
|
"appName": func() string { return version.Name },
|
||||||
"appAuthor": func() string { return version.Author },
|
"appAuthor": func() string { return version.Author },
|
||||||
"appAuthorURL": func() string { return version.AuthorURL },
|
"appAuthorURL": func() string { return version.AuthorURL },
|
||||||
"appLicense": func() string { return version.License },
|
"appLicense": func() string { return version.License },
|
||||||
"appRepoURL": func() string { return version.RepoURL },
|
"appRepoURL": func() string { return version.RepoURL },
|
||||||
"appGitRevision": func() string { return version.GitRevisionShort },
|
"appGitRevision": func() string { return version.GitRevisionShort },
|
||||||
"appGitCommitURL": func() string { return version.CommitURL() },
|
"appGitCommitURL": func() string { return version.CommitURL() },
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse index template
|
// Parse index template
|
||||||
|
|||||||
73
script/bootstrap
Executable file
73
script/bootstrap
Executable file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/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 (not git,
|
||||||
|
# make, or go).
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
|
|
||||||
|
PKGMGR=""
|
||||||
|
SUDO=""
|
||||||
|
|
||||||
|
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) $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
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
cd "$ROOT"
|
||||||
|
|
||||||
|
# Base tooling (every repo)
|
||||||
|
if missing git; then pkg_install git git git git; fi
|
||||||
|
if missing make; then pkg_install gnumake make make make; fi
|
||||||
|
|
||||||
|
# Go toolchain
|
||||||
|
if missing go; then pkg_install go golang go go; fi
|
||||||
|
|
||||||
|
# golangci-lint: packaged in nix, brew, and apk. There is no apt
|
||||||
|
# package; on apt systems install it manually from a hash-verified
|
||||||
|
# GitHub release archive (never curl | sh).
|
||||||
|
if missing golangci-lint; then
|
||||||
|
pkg_install golangci-lint golangci-lint golangci-lint golangci-lint
|
||||||
|
fi
|
||||||
|
|
||||||
|
go mod download
|
||||||
|
|
||||||
|
echo "bootstrap complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
15
script/check
Executable file
15
script/check
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/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.
|
||||||
|
# Generic: usually needs no adaptation.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
||||||
|
|
||||||
|
main() {
|
||||||
|
"$SCRIPT_DIR/test"
|
||||||
|
"$SCRIPT_DIR/lint"
|
||||||
|
"$SCRIPT_DIR/fmt-check"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
14
script/cibuild
Executable file
14
script/cibuild
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# script/cibuild: run the CI build. The Dockerfile runs script/check
|
||||||
|
# (via make check), so a successful build implies all checks pass.
|
||||||
|
# Generic: needs no adaptation. The Gitea workflow runs this on push.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
|
|
||||||
|
main() {
|
||||||
|
cd "$ROOT"
|
||||||
|
docker build .
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
15
script/docker
Executable file
15
script/docker
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# script/docker: build the Docker image tagged with the project name.
|
||||||
|
# Identical in all repos; the tag comes from script/projectname.
|
||||||
|
# Generic: needs no adaptation.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
||||||
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
|
||||||
|
|
||||||
|
main() {
|
||||||
|
cd "$ROOT"
|
||||||
|
docker build -t "$("$SCRIPT_DIR/projectname")" .
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
12
script/fmt
Executable file
12
script/fmt
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# script/fmt: format all files (writes).
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
|
|
||||||
|
main() {
|
||||||
|
cd "$ROOT"
|
||||||
|
go fmt ./...
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
18
script/fmt-check
Executable file
18
script/fmt-check
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/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"
|
||||||
|
unformatted="$(gofmt -l .)"
|
||||||
|
if [ -n "$unformatted" ]; then
|
||||||
|
echo "Files not formatted:" >&2
|
||||||
|
echo "$unformatted" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
17
script/install-precommit
Executable file
17
script/install-precommit
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# script/install-precommit: install the git pre-commit hook that runs
|
||||||
|
# script/precommit. Our own extension to scripts-to-rule-them-all.
|
||||||
|
# Generic: needs no adaptation.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
|
|
||||||
|
main() {
|
||||||
|
cd "$ROOT"
|
||||||
|
hook=".git/hooks/pre-commit"
|
||||||
|
printf '#!/bin/sh\nset -e\nscript/precommit\n' > .git/hooks/pre-commit
|
||||||
|
chmod +x .git/hooks/pre-commit
|
||||||
|
echo "pre-commit hook installed: runs script/precommit"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
13
script/lint
Executable file
13
script/lint
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# script/lint: run the linters.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
|
|
||||||
|
main() {
|
||||||
|
cd "$ROOT"
|
||||||
|
go vet ./...
|
||||||
|
golangci-lint run
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
21
script/precommit
Executable file
21
script/precommit
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/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. Go repo
|
||||||
|
# extras: go mod tidy and go fmt must leave the tree unchanged.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
||||||
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
|
||||||
|
|
||||||
|
main() {
|
||||||
|
cd "$ROOT"
|
||||||
|
go mod tidy
|
||||||
|
go fmt ./...
|
||||||
|
git diff --exit-code -- go.mod go.sum || {
|
||||||
|
echo "go mod tidy changed files; please stage and retry"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
"$SCRIPT_DIR/check"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
12
script/projectname
Executable file
12
script/projectname
Executable 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 "routewatch"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
14
script/setup
Executable file
14
script/setup
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# script/setup: set up the repo for development after a fresh clone:
|
||||||
|
# installs dependencies (script/bootstrap) and the git pre-commit hook.
|
||||||
|
# Add any repo-specific initialization (db init, .env template) here.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
||||||
|
|
||||||
|
main() {
|
||||||
|
"$SCRIPT_DIR/bootstrap"
|
||||||
|
"$SCRIPT_DIR/install-precommit"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
17
script/test
Executable file
17
script/test
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# script/test: run the test suite. On failure, rerun verbosely so the
|
||||||
|
# failing tests are visible in the output.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
|
|
||||||
|
main() {
|
||||||
|
cd "$ROOT"
|
||||||
|
go test -timeout 30s -race -cover ./... || {
|
||||||
|
echo "--- Rerunning with -v for details ---"
|
||||||
|
go test -timeout 30s -race -v ./...
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user