diff --git a/.github/workflows/go-int.yml b/.github/workflows/go-int.yml index 076c7b481..970eb8c57 100644 --- a/.github/workflows/go-int.yml +++ b/.github/workflows/go-int.yml @@ -29,9 +29,9 @@ jobs: - name: Deps run: | just go::deps - just react::deps + just react-deps - name: Build UI - run: just react::build + run: just react-build - name: Linux tuning run: just linux-tune - name: Integration diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index d4e804ba3..2a1666731 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -30,7 +30,7 @@ jobs: run: | just go::deps just go::mod - just react::deps + just react-deps - name: Test run: just test - name: Upload coverage reports to Codecov diff --git a/.gitignore b/.gitignore index 989e65452..7a79256a7 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ examples/** !examples/**/*.go !examples/**/*.mod !examples/**/*.sum + +# Host network state written by `netplan status --format=json` +netplanStatus.txt diff --git a/.just/remote/react.mod.just b/.just/remote/react.mod.just deleted file mode 100644 index 02b64b06f..000000000 --- a/.just/remote/react.mod.just +++ /dev/null @@ -1,4 +0,0 @@ -# Local override: run react recipes in the ui/ directory. -set working-directory := '../../ui' - -import? 'react.just' diff --git a/CLAUDE.md b/CLAUDE.md index a0eeef192..3fdb54417 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -32,10 +32,10 @@ just go::vet # Run golangci-lint just go::fmt # Auto-format (gofumpt + golines) go test -run TestName -v ./internal/job/... # Run a single test -just react::dev # Start UI dev server (http://localhost:5173) -just react::build # Production UI build -just react::lint # Run ESLint on UI -just react::fmt # Format UI with Prettier +just react-dev # Start UI dev server (http://localhost:5173) +just react-build # Production UI build +just react-lint # Run ESLint on UI +just react-fmt # Format UI with Prettier ``` ## Architecture (Quick Reference) @@ -66,7 +66,7 @@ just react::fmt # Format UI with Prettier - **`ui/embed.go`** - `//go:embed dist/*` directive exposing `ui.Assets`. - **`internal/controller/api/ui/`** - SPA serving handler (static files + `index.html` fallback for client-side routing). - Config: `controller.ui.enabled` in `osapi.yaml` (default `true`). -- `//go:embed dist/*` requires `ui/dist/` to have files at compile time. Always use `just build` / `just test` / `just ready` — these run `just react::build` first. Running `go build` / `go test` directly without a prior UI build will fail. +- `//go:embed dist/*` requires `ui/dist/` to have files at compile time. Always use `just build` / `just test` / `just ready` — these run `just react-build` first. Running `go build` / `go test` directly without a prior UI build will fail. ## UI Conventions (MANDATORY) diff --git a/docs/docs/sidebar/architecture/ui.md b/docs/docs/sidebar/architecture/ui.md index ac67d51e4..050325e36 100644 --- a/docs/docs/sidebar/architecture/ui.md +++ b/docs/docs/sidebar/architecture/ui.md @@ -214,7 +214,7 @@ in order: `internal/controller/api/gen/api.yaml`. 2. `go generate` regenerates Go server and SDK code. 3. The combined spec is copied to `ui/src/sdk/gen/api.yaml`. -4. `just react::generate` runs orval against the copied spec to regenerate typed +4. `just react-generate` runs orval against the copied spec to regenerate typed fetch functions and schema types under `ui/src/sdk/gen/`. ### Fetch mutator diff --git a/docs/docs/sidebar/development/development.md b/docs/docs/sidebar/development/development.md index 12e786599..689445439 100644 --- a/docs/docs/sidebar/development/development.md +++ b/docs/docs/sidebar/development/development.md @@ -76,7 +76,7 @@ just build # Builds React UI + Go binary Use `just build` (not `go build` directly). The `//go:embed` directive for the UI assets requires `ui/dist/` to be populated at compile time — `just build` -runs `just react::build` first to satisfy this. The same applies to tests: use +runs `just react-build` first to satisfy this. The same applies to tests: use `just test`, not `go test ./...`. ::: @@ -85,7 +85,7 @@ runs `just react::build` first to satisfy this. The same applies to tests: use The embedded React management dashboard has its own development workflow. See the [UI Development](ui-development.md) guide for prerequisites, the development -server (`just react::dev`), code style, and component conventions. +server (`just react-dev`), code style, and component conventions. ## Documentation diff --git a/docs/docs/sidebar/development/ui-development.md b/docs/docs/sidebar/development/ui-development.md index 36f5ea289..f8627092f 100644 --- a/docs/docs/sidebar/development/ui-development.md +++ b/docs/docs/sidebar/development/ui-development.md @@ -35,13 +35,13 @@ This installs Go modules, Docusaurus dependencies, and the UI's Bun packages. To install only the UI dependencies: ```bash -just react::deps +just react-deps ``` ## Development server ```bash -just react::dev +just react-dev ``` Opens at `http://localhost:5173`. Hot-reloads on file changes. @@ -69,7 +69,7 @@ page. If not set, users paste their token on the sign-in page. just build ``` -This is the top-level build recipe. It runs `just react::build` first (to +This is the top-level build recipe. It runs `just react-build` first (to populate `ui/dist/` with static assets), then `just go::build` to produce the Go binary with the assets embedded via the `//go:embed` directive in `ui/embed.go`. The controller API serves these assets at runtime from the embedded filesystem — @@ -104,8 +104,8 @@ TypeScript and CSS are formatted by [Prettier][] and linted using [ESLint][]. This style is enforced by CI. ```bash -just react::fmt # Auto-fix formatting -just react::lint # Run ESLint +just react-fmt # Auto-fix formatting +just react-lint # Run ESLint ``` ### Component conventions diff --git a/justfile b/justfile index ea084ccb3..e53e7f32c 100644 --- a/justfile +++ b/justfile @@ -8,11 +8,16 @@ # .github/codecov.yml — change both together. export JUST_COVERAGE_TARGET := "99.9" +# The React application lives in ui/, not at the repository root. The flat +# react module requires the importing justfile to declare it. +react_dir := "ui" + mod? go '.just/remote/go.mod.just' mod? docs '.just/remote/docs.mod.just' mod? just '.just/remote/just.mod.just' mod? docker '.just/remote/docker.mod.just' -mod? react '.just/remote/react.mod.just' + +import? '.just/remote/react.just' # --- Fetch --- @@ -27,7 +32,7 @@ fetch: curl -sSfL https://raw.githubusercontent.com/osapi-io/osapi-justfiles/refs/heads/main/just.just -o .just/remote/just.just curl -sSfL https://raw.githubusercontent.com/osapi-io/osapi-justfiles/refs/heads/main/docker.mod.just -o .just/remote/docker.mod.just curl -sSfL https://raw.githubusercontent.com/osapi-io/osapi-justfiles/refs/heads/main/docker.just -o .just/remote/docker.just - curl -sSfL https://raw.githubusercontent.com/osapi-io/osapi-justfiles/refs/heads/main/react.just -o .just/remote/react.just + curl -sSfL https://raw.githubusercontent.com/osapi-io/osapi-justfiles/refs/heads/main/react/react.just -o .just/remote/react.just # --- Top-level orchestration --- @@ -36,17 +41,17 @@ deps: just go::deps just go::mod just docs::deps - just react::deps + just react-deps # Build production binary (includes embedded UI) build: - just react::build + just react-build go build -o osapi . # Run all tests test: linux-tune just just::fmt-check - just react::build + just react-build just go::test # Generate code @@ -55,7 +60,7 @@ generate: just go::generate just docs::generate cp internal/controller/api/gen/api.yaml ui/src/sdk/gen/api.yaml - just react::generate + just react-generate # Format, lint, and generate before committing ready: @@ -64,9 +69,9 @@ ready: just docs::fmt just go::fmt just go::vet - just react::fmt - just react::lint - just react::build + just react-fmt + just react-lint + just react-build [linux] linux-tune: diff --git a/netplanStatus.txt b/netplanStatus.txt deleted file mode 100644 index d13db0f3b..000000000 --- a/netplanStatus.txt +++ /dev/null @@ -1 +0,0 @@ -{"netplan-global-state": {"online": true, "nameservers": {"addresses": ["127.0.0.53"], "search": ["."], "mode": "stub"}}, "lo": {"index": 1, "adminstate": "UP", "operstate": "UNKNOWN", "type": "ethernet", "macaddress": "00:00:00:00:00:00", "addresses": [{"127.0.0.1": {"prefix": 8}}, {"::1": {"prefix": 128}}], "routes": [{"to": "127.0.0.0/8", "family": 2, "from": "127.0.0.1", "type": "local", "scope": "host", "protocol": "kernel", "table": "local"}, {"to": "127.0.0.1", "family": 2, "from": "127.0.0.1", "type": "local", "scope": "host", "protocol": "kernel", "table": "local"}, {"to": "127.255.255.255", "family": 2, "from": "127.0.0.1", "type": "broadcast", "scope": "link", "protocol": "kernel", "table": "local"}, {"to": "::1", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}]}, "wlp0s20f3": {"index": 3, "adminstate": "UP", "operstate": "UP", "type": "wifi", "backend": "networkd", "id": "wlp0s20f3", "macaddress": "b0:a4:60:17:cb:90", "vendor": "Intel Corporation", "addresses": [{"192.168.0.241": {"prefix": 16, "flags": ["dhcp"]}}, {"2600:6c50:4900:c740:b2a4:60ff:fe17:cb90": {"prefix": 64}}, {"fdd6:733a:3f9:1:b2a4:60ff:fe17:cb90": {"prefix": 64}}, {"fe80::b2a4:60ff:fe17:cb90": {"prefix": 64, "flags": ["link"]}}], "dns_addresses": ["192.168.0.247", "2001:1998:f00:1::1", "2001:1998:f00:3::1", "2001:1998:f00:2::1"], "routes": [{"to": "default", "family": 2, "via": "192.168.0.1", "from": "192.168.0.241", "metric": 600, "type": "unicast", "scope": "global", "protocol": "dhcp", "table": "main"}, {"to": "192.168.0.0/16", "family": 2, "from": "192.168.0.241", "metric": 600, "type": "unicast", "scope": "link", "protocol": "kernel", "table": "main"}, {"to": "192.168.0.1", "family": 2, "from": "192.168.0.241", "metric": 600, "type": "unicast", "scope": "link", "protocol": "dhcp", "table": "main"}, {"to": "192.168.0.247", "family": 2, "from": "192.168.0.241", "metric": 600, "type": "unicast", "scope": "link", "protocol": "dhcp", "table": "main"}, {"to": "192.168.0.241", "family": 2, "from": "192.168.0.241", "type": "local", "scope": "host", "protocol": "kernel", "table": "local"}, {"to": "192.168.255.255", "family": 2, "from": "192.168.0.241", "type": "broadcast", "scope": "link", "protocol": "kernel", "table": "local"}, {"to": "2600:6c50:4900:c740::/64", "family": 10, "metric": 600, "type": "unicast", "scope": "global", "protocol": "ra", "table": "main"}, {"to": "fd56:aa96:7e46::/64", "family": 10, "via": "fe80::1c2d:b4a4:4f6f:85a", "metric": 600, "type": "unicast", "scope": "global", "protocol": "ra", "table": "main"}, {"to": "fdd6:733a:3f9:1::/64", "family": 10, "metric": 600, "type": "unicast", "scope": "global", "protocol": "ra", "table": "main"}, {"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "default", "family": 10, "via": "fe80::1a90:88ff:fe6b:f272", "metric": 600, "type": "unicast", "scope": "global", "protocol": "ra", "table": "main"}, {"to": "2600:6c50:4900:c740::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "2600:6c50:4900:c740:b2a4:60ff:fe17:cb90", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fdd6:733a:3f9:1::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fdd6:733a:3f9:1:b2a4:60ff:fe17:cb90", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::b2a4:60ff:fe17:cb90", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}]}, "flannel.1": {"index": 4, "adminstate": "UP", "operstate": "UNKNOWN", "type": "tunnel", "tunnel_mode": "vxlan", "macaddress": "5a:46:ac:96:96:1c", "addresses": [{"10.42.0.0": {"prefix": 32}}, {"fe80::5846:acff:fe96:961c": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "10.42.0.0", "family": 2, "from": "10.42.0.0", "type": "local", "scope": "host", "protocol": "kernel", "table": "local"}, {"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::5846:acff:fe96:961c", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}]}, "cni0": {"index": 5, "adminstate": "UP", "operstate": "UP", "type": "bridge", "macaddress": "06:12:3b:bd:0e:d9", "addresses": [{"10.42.0.1": {"prefix": 24}}, {"fe80::4c5f:e5ff:fe41:e658": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "10.42.0.0/24", "family": 2, "from": "10.42.0.1", "type": "unicast", "scope": "link", "protocol": "kernel", "table": "main"}, {"to": "10.42.0.1", "family": 2, "from": "10.42.0.1", "type": "local", "scope": "host", "protocol": "kernel", "table": "local"}, {"to": "10.42.0.255", "family": 2, "from": "10.42.0.1", "type": "broadcast", "scope": "link", "protocol": "kernel", "table": "local"}, {"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::4c5f:e5ff:fe41:e658", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}], "interfaces": ["veth38cc21af", "vethaab83896", "vethab727056", "veth9109f820", "vethddba31e1", "veth9c33a87b", "veth4b36aba2", "vethb103745e", "veth319c9203", "veth0ad2b054"]}, "veth38cc21af": {"index": 6, "adminstate": "UP", "operstate": "UP", "type": "virtual-ethernet", "macaddress": "8a:14:6b:33:c7:5c", "addresses": [{"fe80::8814:6bff:fe33:c75c": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::8814:6bff:fe33:c75c", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}], "bridge": "cni0"}, "vethaab83896": {"index": 7, "adminstate": "UP", "operstate": "UP", "type": "virtual-ethernet", "macaddress": "2e:79:14:b6:69:d9", "addresses": [{"fe80::2c79:14ff:feb6:69d9": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::2c79:14ff:feb6:69d9", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}], "bridge": "cni0"}, "vethab727056": {"index": 8, "adminstate": "UP", "operstate": "UP", "type": "virtual-ethernet", "macaddress": "6a:61:f5:39:fd:79", "addresses": [{"fe80::6861:f5ff:fe39:fd79": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::6861:f5ff:fe39:fd79", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}], "bridge": "cni0"}, "veth9109f820": {"index": 9, "adminstate": "UP", "operstate": "UP", "type": "virtual-ethernet", "macaddress": "56:5b:2b:dd:82:18", "addresses": [{"fe80::545b:2bff:fedd:8218": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::545b:2bff:fedd:8218", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}], "bridge": "cni0"}, "vethddba31e1": {"index": 10, "adminstate": "UP", "operstate": "UP", "type": "virtual-ethernet", "macaddress": "16:cb:e7:7d:e8:ff", "addresses": [{"fe80::14cb:e7ff:fe7d:e8ff": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::14cb:e7ff:fe7d:e8ff", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}], "bridge": "cni0"}, "veth9c33a87b": {"index": 11, "adminstate": "UP", "operstate": "UP", "type": "virtual-ethernet", "macaddress": "f2:dc:88:d3:d3:95", "addresses": [{"fe80::f0dc:88ff:fed3:d395": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::f0dc:88ff:fed3:d395", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}], "bridge": "cni0"}, "veth4b36aba2": {"index": 12, "adminstate": "UP", "operstate": "UP", "type": "virtual-ethernet", "macaddress": "66:aa:63:0f:3d:4d", "addresses": [{"fe80::64aa:63ff:fe0f:3d4d": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::64aa:63ff:fe0f:3d4d", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}], "bridge": "cni0"}, "vethb103745e": {"index": 13, "adminstate": "UP", "operstate": "UP", "type": "virtual-ethernet", "macaddress": "0a:9b:c3:1d:11:7a", "addresses": [{"fe80::89b:c3ff:fe1d:117a": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::89b:c3ff:fe1d:117a", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}], "bridge": "cni0"}, "veth319c9203": {"index": 14, "adminstate": "UP", "operstate": "UP", "type": "virtual-ethernet", "macaddress": "5a:ae:b0:ae:24:6b", "addresses": [{"fe80::58ae:b0ff:feae:246b": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::58ae:b0ff:feae:246b", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}], "bridge": "cni0"}, "veth0ad2b054": {"index": 15, "adminstate": "UP", "operstate": "UP", "type": "virtual-ethernet", "macaddress": "ee:9a:08:43:14:ca", "addresses": [{"fe80::ec9a:8ff:fe43:14ca": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::ec9a:8ff:fe43:14ca", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}], "bridge": "cni0"}, "lxdbr0": {"index": 16, "adminstate": "UP", "operstate": "UP", "type": "bridge", "macaddress": "00:16:3e:f8:f4:f3", "addresses": [{"10.95.130.1": {"prefix": 24}}, {"fd42:94ae:4418:e857::1": {"prefix": 64}}, {"fe80::216:3eff:fef8:f4f3": {"prefix": 64, "flags": ["link"]}}], "routes": [{"to": "10.95.130.0/24", "family": 2, "from": "10.95.130.1", "type": "unicast", "scope": "link", "protocol": "kernel", "table": "main"}, {"to": "10.95.130.1", "family": 2, "from": "10.95.130.1", "type": "local", "scope": "host", "protocol": "kernel", "table": "local"}, {"to": "10.95.130.255", "family": 2, "from": "10.95.130.1", "type": "broadcast", "scope": "link", "protocol": "kernel", "table": "local"}, {"to": "fd42:94ae:4418:e857::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fe80::/64", "family": 10, "metric": 256, "type": "unicast", "scope": "global", "protocol": "kernel", "table": "main"}, {"to": "fd42:94ae:4418:e857::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fd42:94ae:4418:e857::1", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::", "family": 10, "type": "anycast", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "fe80::216:3eff:fef8:f4f3", "family": 10, "type": "local", "scope": "global", "protocol": "kernel", "table": "local"}, {"to": "ff00::/8", "family": 10, "metric": 256, "type": "multicast", "scope": "global", "protocol": "kernel", "table": "local"}], "interfaces": ["tapbd419562"]}, "tapbd419562": {"index": 17, "adminstate": "UP", "operstate": "UP", "type": "tunnel", "tunnel_mode": "tun", "macaddress": "1e:ce:32:d6:c8:b9", "bridge": "lxdbr0"}} diff --git a/ui/docs/development.md b/ui/docs/development.md index 13d37a789..aade1f4a4 100644 --- a/ui/docs/development.md +++ b/ui/docs/development.md @@ -80,8 +80,8 @@ TypeScript and CSS should be formatted by [Prettier][] and linted using [ESLint][]. This style is enforced by CI. ```bash -just react::fmt # Auto-fix formatting -just react::lint # Run ESLint +just react-fmt # Auto-fix formatting +just react-lint # Run ESLint ``` ### Component conventions @@ -116,7 +116,7 @@ repo and regenerate the TypeScript SDK: cp /internal/controller/api/gen/api.yaml src/sdk/gen/api.yaml # Regenerate typed fetch functions from the spec: -just react::generate +just react-generate ``` See the [Architecture](architecture.md) docs for the full generation flow. diff --git a/ui/justfile b/ui/justfile index a64311643..ef9507c88 100644 --- a/ui/justfile +++ b/ui/justfile @@ -1,14 +1,17 @@ +# The React application is this directory. +react_dir := "." + # Optional modules: mod? allows `just fetch` to work before .just/remote/ exists. -mod? react '.just/remote/react.mod.just' mod? just '.just/remote/just.mod.just' +import? '.just/remote/react.just' + # --- Fetch --- # Fetch shared justfiles from osapi-justfiles fetch: mkdir -p .just/remote - curl -sSfL https://raw.githubusercontent.com/osapi-io/osapi-justfiles/refs/heads/main/react.mod.just -o .just/remote/react.mod.just - curl -sSfL https://raw.githubusercontent.com/osapi-io/osapi-justfiles/refs/heads/main/react.just -o .just/remote/react.just + curl -sSfL https://raw.githubusercontent.com/osapi-io/osapi-justfiles/refs/heads/main/react/react.just -o .just/remote/react.just curl -sSfL https://raw.githubusercontent.com/osapi-io/osapi-justfiles/refs/heads/main/just.mod.just -o .just/remote/just.mod.just curl -sSfL https://raw.githubusercontent.com/osapi-io/osapi-justfiles/refs/heads/main/just.just -o .just/remote/just.just @@ -16,15 +19,15 @@ fetch: # Install all dependencies deps: - just react::deps + just react-deps # Run all tests test: - just react::test + just react-test # Format, lint, generate, and build before committing ready: - just react::generate - just react::fmt - just react::lint - just react::build + just react-generate + just react-fmt + just react-lint + just react-build