This is an automated email from the ASF dual-hosted git repository.

wu-sheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git


The following commit(s) were added to refs/heads/main by this push:
     new c80a2e7  ci: add unit-test job + harden bundled_templates path for prod
c80a2e7 is described below

commit c80a2e7044e1f1517a8f2f10e8f7c34e135e7fd1
Author: Wu Sheng <[email protected]>
AuthorDate: Sun May 17 07:55:05 2026 +0800

    ci: add unit-test job + harden bundled_templates path for prod
    
    CI:
      - New `Unit tests (workspaces)` job runs `pnpm -r run test:unit`
        on PR + push to main; rolled into the `Required` gate so branch
        protection blocks on test failures, not just type-check + build.
      - test:unit scripts now bake in `vitest run` (one-shot); a new
        test:unit:watch script keeps the dev-watch entry point. Avoids
        fragile `--` flag forwarding through pnpm in CI.
    
    Loaders (prod-bundle fix):
      The earlier `'..', '..'` fix worked in dev (tsx-watched .ts files)
      but broke under esbuild — every import collapses into
      `/app/dist/server.js`, so `__dirname` is `/app/dist/` and two `..`
      lands at `/bundled_templates` (does not exist; Dockerfile copies
      templates to `/app/bundled_templates`).
    
      logic/layers/loader.ts and logic/overview/loader.ts now probe
      candidate directories in order and use the first that exists:
        1. `<__dirname>/../../bundled_templates/<sub>`  (dev: src layout)
        2. `<__dirname>/../bundled_templates/<sub>`     (prod: dist sibling)
        3. `<cwd>/bundled_templates/<sub>`              (CLI-style override)
    
      Same behavior on disk for both environments, no Dockerfile change
      needed. 40 BFF + 67 UI tests still green; tsc / vue-tsc clean.
---
 .github/workflows/ci.yaml             | 25 ++++++++++++++++++++++-
 apps/bff/package.json                 |  3 ++-
 apps/bff/src/logic/layers/loader.ts   | 37 +++++++++++++++++++++++++++++------
 apps/bff/src/logic/overview/loader.ts | 25 +++++++++++++++++------
 apps/ui/package.json                  |  3 ++-
 5 files changed, 78 insertions(+), 15 deletions(-)

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index ab7b0f8..9823ddb 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -92,13 +92,35 @@ jobs:
       - run: pnpm install --frozen-lockfile
       - run: pnpm --filter @skywalking-horizon-ui/bff build
 
+  test:
+    name: Unit tests (workspaces)
+    runs-on: ubuntu-latest
+    timeout-minutes: 15
+    steps:
+      - uses: actions/checkout@v6
+        with:
+          persist-credentials: false
+      - uses: pnpm/action-setup@v4
+      - uses: actions/setup-node@v4
+        with:
+          node-version: '20'
+          cache: 'pnpm'
+      - run: pnpm install --frozen-lockfile
+      # `pnpm -r` walks every workspace package and runs `test:unit` —
+      # currently @skywalking-horizon-ui/ui (vitest + jsdom) and
+      # @skywalking-horizon-ui/bff (vitest, node env). Packages without
+      # the script (api-client, design-tokens) are skipped automatically.
+      # The scripts already include `vitest run`, so no `--run` forwarding
+      # needed — keeps the CI line robust against pnpm-flag parsing quirks.
+      - run: pnpm -r run test:unit
+
   required:
     # Status-check name pinned to "Required" so Apache branch protection
     # (`.asf.yaml: protected_branches.main.required_status_checks.contexts:
     # [Required]`) can gate on a single rolled-up signal.
     if: always() && !cancelled()
     name: Required
-    needs: [license-header, type-check, build-ui, build-bff]
+    needs: [license-header, type-check, build-ui, build-bff, test]
     runs-on: ubuntu-latest
     timeout-minutes: 5
     steps:
@@ -108,3 +130,4 @@ jobs:
           [[ ${{ needs.type-check.result }} == 'success' ]] || exit 1
           [[ ${{ needs.build-ui.result }} == 'success' ]] || exit 1
           [[ ${{ needs.build-bff.result }} == 'success' ]] || exit 1
+          [[ ${{ needs.test.result }} == 'success' ]] || exit 1
diff --git a/apps/bff/package.json b/apps/bff/package.json
index de0a505..1760b8e 100644
--- a/apps/bff/package.json
+++ b/apps/bff/package.json
@@ -10,7 +10,8 @@
     "cli:hash": "tsx src/cli/hash.ts",
     "type-check": "tsc --noEmit",
     "lint": "eslint . --ext .ts,.cjs,.mjs --fix --ignore-path 
../../.gitignore",
-    "test:unit": "vitest --root src/"
+    "test:unit": "vitest run --root src/",
+    "test:unit:watch": "vitest --root src/"
   },
   "dependencies": {
     "@fastify/cookie": "^11.0.1",
diff --git a/apps/bff/src/logic/layers/loader.ts 
b/apps/bff/src/logic/layers/loader.ts
index 979a382..b95990e 100644
--- a/apps/bff/src/logic/layers/loader.ts
+++ b/apps/bff/src/logic/layers/loader.ts
@@ -306,12 +306,37 @@ export const BOOSTER_ENDPOINT_DEP_DEFAULTS: 
EndpointDependencyConfig = {
 };
 
 const __dirname = dirname(fileURLToPath(import.meta.url));
-// Bundled layer JSONs live alongside other static templates under
-// `apps/bff/src/bundled_templates/`. In the long run these should be
-// served by OAP; for now they ship inside the BFF so a fresh deploy
-// renders something sensible without any operator setup. Two levels
-// up from logic/layers/ to reach apps/bff/src/.
-const CONFIG_DIR = join(__dirname, '..', '..', 'bundled_templates', 'layers');
+/** Locate bundled_templates/layers/ at runtime.
+ *
+ *   - **Dev** (tsx-watched source files): this file lives at
+ *     `apps/bff/src/logic/layers/loader.ts`, so two `..` reaches
+ *     `apps/bff/src/bundled_templates/`.
+ *   - **Prod** (esbuild bundle): every import collapses into
+ *     `/app/dist/server.js`, so `__dirname` is `/app/dist/` and only
+ *     one `..` reaches the Dockerfile's `/app/bundled_templates/`.
+ *
+ *   We probe candidates in order — first existing wins. `process.cwd()`
+ *   is the last fallback for operators running the bundle from an
+ *   arbitrary working dir with the templates beside them. */
+function locateConfigDir(): string {
+  const candidates = [
+    join(__dirname, '..', '..', 'bundled_templates', 'layers'),
+    join(__dirname, '..', 'bundled_templates', 'layers'),
+    join(process.cwd(), 'bundled_templates', 'layers'),
+  ];
+  for (const dir of candidates) {
+    try {
+      readdirSync(dir);
+      return dir;
+    } catch {
+      /* try next */
+    }
+  }
+  // Last-resort default — first candidate. The first readdir of an
+  // empty result will surface the underlying ENOENT to the operator.
+  return candidates[0];
+}
+const CONFIG_DIR = locateConfigDir();
 
 let cache: Map<string, LayerTemplate> | null = null;
 
diff --git a/apps/bff/src/logic/overview/loader.ts 
b/apps/bff/src/logic/overview/loader.ts
index 0e93363..ae78189 100644
--- a/apps/bff/src/logic/overview/loader.ts
+++ b/apps/bff/src/logic/overview/loader.ts
@@ -35,12 +35,25 @@ import type {
 } from '@skywalking-horizon-ui/api-client';
 
 const __dirname = path.dirname(fileURLToPath(import.meta.url));
-// Bundled overview dashboards live under `bundled_templates/overviews/`
-// (sibling to `bundled_templates/layers/`). Co-locating the static
-// templates keeps the future "operator-editable / OAP-served" migration
-// to one directory swap. Two levels up from logic/overview/ to reach
-// apps/bff/src/.
-const CONFIG_DIR = path.join(__dirname, '..', '..', 'bundled_templates', 
'overviews');
+/** Mirrors the dev-vs-prod path probing in `logic/layers/loader.ts`
+ *  — see that file for the rationale. */
+function locateConfigDir(): string {
+  const candidates = [
+    path.join(__dirname, '..', '..', 'bundled_templates', 'overviews'),
+    path.join(__dirname, '..', 'bundled_templates', 'overviews'),
+    path.join(process.cwd(), 'bundled_templates', 'overviews'),
+  ];
+  for (const dir of candidates) {
+    try {
+      fs.readdirSync(dir);
+      return dir;
+    } catch {
+      /* try next */
+    }
+  }
+  return candidates[0];
+}
+const CONFIG_DIR = locateConfigDir();
 
 let cache: OverviewDashboard[] | null = null;
 
diff --git a/apps/ui/package.json b/apps/ui/package.json
index 0edc7a8..a0a32b6 100644
--- a/apps/ui/package.json
+++ b/apps/ui/package.json
@@ -10,7 +10,8 @@
     "preview": "vite preview",
     "type-check": "vue-tsc --noEmit",
     "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix 
--ignore-path ../../.gitignore",
-    "test:unit": "vitest --environment jsdom --root src/"
+    "test:unit": "vitest run --environment jsdom --root src/",
+    "test:unit:watch": "vitest --environment jsdom --root src/"
   },
   "dependencies": {
     "@skywalking-horizon-ui/api-client": "workspace:*",

Reply via email to