Compare commits
5
Commits
a5b9d0a9a6
...
next
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db5eb507c1 | ||
|
|
6e4c44b3d6 | ||
|
|
91eb3d6771 | ||
|
|
edcf7f8c3c | ||
|
|
5b01b50bc7 |
+3
-2
@@ -11,7 +11,8 @@ insert_final_newline = true
|
|||||||
[Makefile]
|
[Makefile]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|
||||||
# This repository's own entries, after the shared content above.
|
|
||||||
|
|
||||||
[*.go]
|
[*.go]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|
||||||
|
# This repository's own sections, such as one for another language it
|
||||||
|
# uses, go below this comment, and a re-vendor keeps them.
|
||||||
|
|||||||
+4
-2
@@ -27,7 +27,7 @@ node_modules/
|
|||||||
# Environment files. `*.env` covers bare `.env` and the `prod.env`
|
# Environment files. `*.env` covers bare `.env` and the `prod.env`
|
||||||
# convention. Only the templates `example.env` and `sample.env` are
|
# convention. Only the templates `example.env` and `sample.env` are
|
||||||
# re-included below. A repository that commits any other template adds
|
# re-included below. A repository that commits any other template adds
|
||||||
# its own negation after these lines, for example `!.env.example`.
|
# its own negation at the end of this file, for example `!.env.example`.
|
||||||
*.[eE][nN][vV]
|
*.[eE][nN][vV]
|
||||||
.[eE][nN][vV].*
|
.[eE][nN][vV].*
|
||||||
.[eE][nN][vV][rR][cC]
|
.[eE][nN][vV][rR][cC]
|
||||||
@@ -46,7 +46,9 @@ node_modules/
|
|||||||
[iI][dD]_[eE][dD]25519
|
[iI][dD]_[eE][dD]25519
|
||||||
[iI][dD]_[eE][dD]25519_[sS][kK]
|
[iI][dD]_[eE][dD]25519_[sS][kK]
|
||||||
|
|
||||||
# This repository's own entries, after the shared content above.
|
# This repository's own entries, such as its build outputs, go below
|
||||||
|
# this comment, and a re-vendor keeps them. Anchor a binary built at the
|
||||||
|
# root: `/myapp`, never `myapp`, which also ignores `cmd/myapp/`.
|
||||||
|
|
||||||
# Build output
|
# Build output
|
||||||
dist/
|
dist/
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ linters:
|
|||||||
# silenced by disabling that name, not by enabling the successor.
|
# silenced by disabling that name, not by enabling the successor.
|
||||||
- wsl # Deprecated, replaced by wsl_v5
|
- wsl # Deprecated, replaced by wsl_v5
|
||||||
- gomodguard # Deprecated, replaced by gomodguard_v2
|
- gomodguard # Deprecated, replaced by gomodguard_v2
|
||||||
|
# Misses findings at random in v2.14.0; back once a pinned release fixes it
|
||||||
|
- canonicalheader
|
||||||
settings:
|
settings:
|
||||||
lll:
|
lll:
|
||||||
line-length: 88
|
line-length: 88
|
||||||
|
|||||||
@@ -220,7 +220,8 @@ packages are in `internal/`:
|
|||||||
|
|
||||||
- **`cli`**: Runs `netwatch-server` from its command line: the `check-cidr` and
|
- **`cli`**: Runs `netwatch-server` from its command line: the `check-cidr` and
|
||||||
`prepare-data-dir` subcommands, which `bin/entrypoint.sh` runs before it
|
`prepare-data-dir` subcommands, which `bin/entrypoint.sh` runs before it
|
||||||
starts the server, and otherwise the server, with the `fx` wiring.
|
starts the server, and, given no arguments, the server, with the `fx` wiring.
|
||||||
|
It refuses any other arguments with a usage message and exit code 2.
|
||||||
- **`config`**: Loads configuration from environment variables and config files
|
- **`config`**: Loads configuration from environment variables and config files
|
||||||
via Viper.
|
via Viper.
|
||||||
- **`handlers`**: HTTP request handlers for the API (health check, report
|
- **`handlers`**: HTTP request handlers for the API (health check, report
|
||||||
|
|||||||
@@ -9,11 +9,12 @@
|
|||||||
|
|
||||||
# Status
|
# Status
|
||||||
|
|
||||||
pre-1.0. No git tags. `main` is the stable branch and `next` the development
|
pre-1.0. Release candidates are tagged on `next`, the first being `v1.0.0-rc1`;
|
||||||
branch, which every PR targets. The frontend and the Go backend ship as one
|
`git tag --merged origin/next` lists them. `main` is the stable branch and
|
||||||
Docker image, and the Gitea workflow `.gitea/workflows/check.yml` runs
|
`next` the development branch, which every PR targets. The frontend and the Go
|
||||||
`script/cibuild` on every push, and cancels the older run on the same branch.
|
backend ship as one Docker image, and the Gitea workflow
|
||||||
Working toward 1.0.0.
|
`.gitea/workflows/check.yml` runs `script/cibuild` on every push, and cancels
|
||||||
|
the older run on the same branch. Working toward 1.0.0.
|
||||||
|
|
||||||
# Next Step
|
# Next Step
|
||||||
|
|
||||||
@@ -22,6 +23,36 @@ Run `make frontend-viewport-test` in CI as its own step; it is not part of
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-10-19: the server's `OnStart` hook in `internal/server/server.go` starts
|
||||||
|
`listenAndServe` with `go func() { s.listenAndServe() }()`, as the Go style
|
||||||
|
guide asks ([#162](https://git.eeqj.de/sneak/netwatch/issues/162)); it was
|
||||||
|
`go s.listenAndServe()`. No other `go` statement in the Go code called a
|
||||||
|
function directly. Nothing the server does changed
|
||||||
|
- 2026-10-19: `.gitignore`, `.editorconfig` and `.golangci.yml` are the
|
||||||
|
sneak/prompts copies at `0653579`
|
||||||
|
([#174](https://git.eeqj.de/sneak/netwatch/issues/174)). `.gitignore` is the
|
||||||
|
standard file followed by this repository's own entries; `.editorconfig` is
|
||||||
|
the standard file alone, which now carries the `[*.go]` section.
|
||||||
|
`.golangci.yml` disables `canonicalheader`, as the canonical copy does,
|
||||||
|
because golangci-lint v2.14.0 misses its findings at random
|
||||||
|
- 2026-10-19: `package.json` has no `version` field and is marked
|
||||||
|
`"private": true` ([#158](https://git.eeqj.de/sneak/netwatch/issues/158)). It
|
||||||
|
said `1.0.0`, a release that has not been made, and nothing read it; the git
|
||||||
|
tag is the one version, which the image and `make build` take from
|
||||||
|
`git describe --tags`. The package is never published
|
||||||
|
- 2026-10-19: Status names `v1.0.0-rc1`, the first release candidate, and says
|
||||||
|
release candidates are tagged on `next`, where it said there were no git tags
|
||||||
|
([#157](https://git.eeqj.de/sneak/netwatch/issues/157)). The rest of Status
|
||||||
|
matches the tree
|
||||||
|
- 2026-10-19: `netwatch-server` starts the server only when given no arguments
|
||||||
|
([#179](https://git.eeqj.de/sneak/netwatch/issues/179)). `check-cidr CIDR` and
|
||||||
|
`prepare-data-dir DATA_DIR` behave as before; any other arguments, such as a
|
||||||
|
subcommand with no value or two, or a misspelt one, print a one-line usage
|
||||||
|
message naming both forms to stderr and exit 2. They used to start the server.
|
||||||
|
Unit tests call `cli.Main` with no arguments and check that it starts the
|
||||||
|
server, and with each subcommand given no value and two values and with a
|
||||||
|
misspelt subcommand, and check the exit code, the message and that the server
|
||||||
|
does not start
|
||||||
- 2026-10-19: `main()` in `cmd/netwatch-server/main.go` is one call into a new
|
- 2026-10-19: `main()` in `cmd/netwatch-server/main.go` is one call into a new
|
||||||
package, `internal/cli`, as the Go style guide asks
|
package, `internal/cli`, as the Go style guide asks
|
||||||
([#160](https://git.eeqj.de/sneak/netwatch/issues/160)). `internal/cli` runs
|
([#160](https://git.eeqj.de/sneak/netwatch/issues/160)). `internal/cli` runs
|
||||||
|
|||||||
+25
-11
@@ -1,7 +1,7 @@
|
|||||||
// Package cli runs netwatch-server from its command line: the
|
// Package cli runs netwatch-server from its command line: the
|
||||||
// check-cidr and prepare-data-dir subcommands, which bin/entrypoint.sh
|
// check-cidr and prepare-data-dir subcommands, which bin/entrypoint.sh
|
||||||
// runs before it starts the server, and otherwise the server, with the
|
// runs before it starts the server, and, given no arguments, the
|
||||||
// fx wiring.
|
// server, with the fx wiring.
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -22,16 +22,21 @@ import (
|
|||||||
"go.uber.org/fx/fxevent"
|
"go.uber.org/fx/fxevent"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// usageExitCode is what Main returns for a command line it does not
|
||||||
|
// understand, the usual exit code for a usage error.
|
||||||
|
const usageExitCode = 2
|
||||||
|
|
||||||
// Main runs netwatch-server with args, its command line after the
|
// Main runs netwatch-server with args, its command line after the
|
||||||
// program's name, and returns the exit code; the subcommands write
|
// program's name, and returns the exit code. With no args, Main runs
|
||||||
// their errors to stderr. Unless args are "check-cidr CIDR" or
|
// the server by calling serve with appname and version, the name and
|
||||||
// "prepare-data-dir DATA_DIR", Main runs the server by calling serve
|
// version the server logs and reports. With "check-cidr CIDR" or
|
||||||
// with appname and version, the name and version the server logs and
|
// "prepare-data-dir DATA_DIR" it runs that subcommand, which writes its
|
||||||
// reports. main passes os.Stderr and RunServer; the tests pass a
|
// errors to stderr. Any other args get a one-line usage message on
|
||||||
// buffer and a function that fails them, so that no test starts the
|
// stderr and exit code 2. main passes os.Stderr and RunServer; the
|
||||||
// server. When serve is RunServer and the server fails to start, to
|
// tests pass a buffer and a function of their own, so that no test
|
||||||
// listen or to stop, fx's Run exits the process with status 1 and Main
|
// starts the server. When serve is RunServer and the server fails to
|
||||||
// never returns.
|
// start, to listen or to stop, fx's Run exits the process with status 1
|
||||||
|
// and Main never returns.
|
||||||
func Main(
|
func Main(
|
||||||
stderr io.Writer,
|
stderr io.Writer,
|
||||||
appname, version string,
|
appname, version string,
|
||||||
@@ -53,6 +58,15 @@ func Main(
|
|||||||
return prepareDataDir(stderr, "/data", args[1], "netwatch")
|
return prepareDataDir(stderr, "/data", args[1], "netwatch")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Any other command line is refused, so that a mistyped one does
|
||||||
|
// not start the server.
|
||||||
|
if len(args) > 0 {
|
||||||
|
_, _ = fmt.Fprintln(stderr,
|
||||||
|
"usage: netwatch-server [check-cidr CIDR | prepare-data-dir DATA_DIR]")
|
||||||
|
|
||||||
|
return usageExitCode
|
||||||
|
}
|
||||||
|
|
||||||
serve(appname, version)
|
serve(appname, version)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -61,6 +61,61 @@ func TestMainRunsPrepareDataDir(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMainRunsServerWithNoArguments: Main given no arguments starts the
|
||||||
|
// server with the name and version it was given, writes nothing and
|
||||||
|
// returns 0.
|
||||||
|
func TestMainRunsServerWithNoArguments(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var (
|
||||||
|
stderr bytes.Buffer
|
||||||
|
gotAppname, gotVersion string
|
||||||
|
)
|
||||||
|
|
||||||
|
code := cli.Main(&stderr, "netwatch", "test", []string{},
|
||||||
|
func(appname, version string) {
|
||||||
|
gotAppname, gotVersion = appname, version
|
||||||
|
})
|
||||||
|
if code != 0 || stderr.Len() != 0 {
|
||||||
|
t.Errorf("exit %d, stderr %q; want exit 0 and no output",
|
||||||
|
code, stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotAppname != "netwatch" || gotVersion != "test" {
|
||||||
|
t.Errorf("server started with %q %q; want %q %q",
|
||||||
|
gotAppname, gotVersion, "netwatch", "test")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMainRefusesOtherCommandLines: Main given a subcommand with too
|
||||||
|
// few or too many values, or a misspelt subcommand, writes the usage
|
||||||
|
// message to stderr, returns 2 and does not start the server.
|
||||||
|
func TestMainRefusesOtherCommandLines(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
want := "usage: netwatch-server " +
|
||||||
|
"[check-cidr CIDR | prepare-data-dir DATA_DIR]\n"
|
||||||
|
|
||||||
|
for _, commandLine := range []string{
|
||||||
|
"netwatch-server check-cidr",
|
||||||
|
"netwatch-server check-cidr 192.0.2.0/24 198.51.100.0/24",
|
||||||
|
"netwatch-server prepare-data-dir",
|
||||||
|
"netwatch-server prepare-data-dir /data/reports /data/other",
|
||||||
|
"netwatch-server check-cdir 192.0.2.0/24",
|
||||||
|
} {
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
|
||||||
|
// Main takes the command line after the program's name.
|
||||||
|
args := strings.Fields(commandLine)[1:]
|
||||||
|
|
||||||
|
code := cli.Main(&stderr, "netwatch", "test", args, failIfServerStarts(t))
|
||||||
|
if code != 2 || stderr.String() != want {
|
||||||
|
t.Errorf("%q: exit %d, stderr %q; want exit 2 and %q",
|
||||||
|
commandLine, code, stderr.String(), want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestCheckCIDRTakesCIDR: check-cidr exits 0, writing nothing, for a
|
// TestCheckCIDRTakesCIDR: check-cidr exits 0, writing nothing, for a
|
||||||
// CIDR the server takes in its TRUSTED_PROXIES.
|
// CIDR the server takes in its TRUSTED_PROXIES.
|
||||||
func TestCheckCIDRTakesCIDR(t *testing.T) {
|
func TestCheckCIDRTakesCIDR(t *testing.T) {
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ func New(
|
|||||||
s.SetupRoutes()
|
s.SetupRoutes()
|
||||||
s.httpServer = s.newHTTPServer()
|
s.httpServer = s.newHTTPServer()
|
||||||
|
|
||||||
go s.listenAndServe()
|
go func() { s.listenAndServe() }()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "netwatch",
|
"name": "netwatch",
|
||||||
"version": "1.0.0",
|
"private": true,
|
||||||
"description": "Real-time network latency monitor SPA",
|
"description": "Real-time network latency monitor SPA",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Reference in New Issue
Block a user