From 205bb32febd5e4949bd7684c01d888e9c5c97fbc Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Thu, 7 May 2026 02:00:08 +0000 Subject: [PATCH 1/2] fix(web): enforce asset compilation at build-time and fix runtime 404s - Use a sentinel //go:embed for assets/main.js to force compile-time failure if UI is not built - Update hasWebAssets() to verify entry point existence, preventing 404s on empty asset dirs - Update install.md to recommend 'make all' for automated web build and installation - Fix web_test.go to provision dummy assets for stable test execution --- .../content/docs/getting-started/install.md | 10 ++++++---- pkg/hub/web.go | 15 +++++++++++++-- pkg/hub/web_test.go | 19 +++++++++++++++++-- web/embed.go | 10 ++++++++++ 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/docs-site/src/content/docs/getting-started/install.md b/docs-site/src/content/docs/getting-started/install.md index ea4610dc1..3f98a5f28 100644 --- a/docs-site/src/content/docs/getting-started/install.md +++ b/docs-site/src/content/docs/getting-started/install.md @@ -64,12 +64,14 @@ Ensure your `$GOPATH/bin` (typically `~/go/bin`) is in your system `$PATH`. If you have the repository cloned, you can use the provided `Makefile`: ```bash -make build -# This creates a 'scion' binary in the build directory. -# You can move it to a directory in your PATH: -sudo mv ./build/scion /usr/local/bin/ +make all +# This builds the web frontend and installs the 'scion' binary +# to ~/.local/bin/ with the UI assets embedded. +# You can add ~/.local/bin/ to your PATH, or `mv` the binary to another directory in your PATH ``` +Alternatively, if you don't need the web UI, you can use `make build` to only compile the CLI into `./build/scion` (the web UI will show a placeholder page). + To verify your installation, run: ```bash diff --git a/pkg/hub/web.go b/pkg/hub/web.go index 142643b3f..9a5739535 100644 --- a/pkg/hub/web.go +++ b/pkg/hub/web.go @@ -870,9 +870,20 @@ func (ws *WebServer) prefetchPageData(r *http.Request) template.JS { } // hasWebAssets reports whether the server has web assets available to serve, -// either from an embedded FS or a filesystem directory. +// either from an embedded FS or a filesystem directory. It verifies the +// presence of the core entry point (assets/main.js) to ensure the UI is +// actually built and ready to serve. func (ws *WebServer) hasWebAssets() bool { - return ws.assets != nil || ws.assetsDisk != "" + if ws.assetsDisk != "" { + p := filepath.Join(ws.assetsDisk, "assets", "main.js") + _, err := os.Stat(p) + return err == nil + } + if ws.assets != nil { + _, err := fs.Stat(ws.assets, "assets/main.js") + return err == nil + } + return false } // spaHandler returns the SPA shell HTML for any route not matched by other handlers. diff --git a/pkg/hub/web_test.go b/pkg/hub/web_test.go index 9ce099995..c66004af2 100644 --- a/pkg/hub/web_test.go +++ b/pkg/hub/web_test.go @@ -27,6 +27,7 @@ import ( "time" "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/web" "github.com/gorilla/securecookie" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -40,6 +41,15 @@ type mockWebStore struct { func newTestWebServer(t *testing.T, cfg WebServerConfig) *WebServer { t.Helper() + // Ensure hasWebAssets() returns true for tests unless they explicitly want + // to test the "no assets" case. + if cfg.AssetsDir == "" && !web.AssetsEmbedded { + tmpDir := t.TempDir() + assetsDir := filepath.Join(tmpDir, "assets") + os.MkdirAll(assetsDir, 0755) + os.WriteFile(filepath.Join(assetsDir, "main.js"), []byte("// test"), 0644) + cfg.AssetsDir = tmpDir + } return NewWebServer(cfg) } @@ -53,7 +63,7 @@ func newDevAuthWebServer(t *testing.T, overrides ...func(*WebServerConfig)) *Web for _, fn := range overrides { fn(&cfg) } - return NewWebServer(cfg) + return newTestWebServer(t, cfg) } func TestSPAShellHandler(t *testing.T) { @@ -286,8 +296,13 @@ func TestSPAHandler_NoAssets_APIStillWorks(t *testing.T) { } func TestSPAHandler_WithAssets_ServesNormalShell(t *testing.T) { + tmpDir := t.TempDir() + assetsDir := filepath.Join(tmpDir, "assets") + os.MkdirAll(assetsDir, 0755) + os.WriteFile(filepath.Join(assetsDir, "main.js"), []byte("// test"), 0644) + ws := newDevAuthWebServer(t, func(cfg *WebServerConfig) { - cfg.AssetsDir = t.TempDir() + cfg.AssetsDir = tmpDir }) req := httptest.NewRequest("GET", "/", nil) diff --git a/web/embed.go b/web/embed.go index b3ade490b..eae414cf9 100644 --- a/web/embed.go +++ b/web/embed.go @@ -25,5 +25,15 @@ import "embed" //go:embed all:dist/client var ClientAssets embed.FS +// sentinel forces a compile-time error if the web assets have not been built. +// If you see an error here, run 'make web' or build with '-tags no_embed_web'. +// +//go:embed dist/client/assets/main.js +var sentinel []byte + // AssetsEmbedded indicates whether client assets are embedded in the binary. var AssetsEmbedded = true + +func init() { + _ = sentinel // Mark as used +} From 79eb3cdccf30ecf459f08580242d0a03edb7a669 Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Wed, 6 May 2026 22:09:03 -0400 Subject: [PATCH 2/2] Update docs-site/src/content/docs/getting-started/install.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- docs-site/src/content/docs/getting-started/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-site/src/content/docs/getting-started/install.md b/docs-site/src/content/docs/getting-started/install.md index 3f98a5f28..0b55534ea 100644 --- a/docs-site/src/content/docs/getting-started/install.md +++ b/docs-site/src/content/docs/getting-started/install.md @@ -70,7 +70,7 @@ make all # You can add ~/.local/bin/ to your PATH, or `mv` the binary to another directory in your PATH ``` -Alternatively, if you don't need the web UI, you can use `make build` to only compile the CLI into `./build/scion` (the web UI will show a placeholder page). +Alternatively, if you don't need the web UI, you can build the CLI-only version using the no_embed_web tag: go build -tags no_embed_web -o ./build/scion ./cmd/scion (the web UI will show a placeholder page). To verify your installation, run: