@monster-orange/den 0.0.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +204 -0
- package/LICENSE +21 -0
- package/README.md +165 -1
- package/bin/den +68 -0
- package/docs/distributions.md +499 -0
- package/make/demo.mk +82 -0
- package/make/den.mk +80 -0
- package/make/journeys.mk +57 -0
- package/make/local_dev.mk +154 -0
- package/make/security.mk +59 -0
- package/make/test.mk +405 -0
- package/package.json +101 -4
- package/scripts/auth-configure.ts +415 -0
- package/scripts/heal-config.ts +92 -0
- package/scripts/jsonl-playwright-reporter.js +70 -0
- package/scripts/manifest-read.cjs +91 -0
- package/scripts/setup-1password.ts +570 -0
- package/scripts/setup-secrets.ts +149 -0
- package/scripts/view-results.js +156 -0
- package/service/install.sh +112 -0
- package/src/pillars/api/fixtures/index.ts +128 -0
- package/src/pillars/api/fixtures/openapi-validator.ts +111 -0
- package/src/pillars/api/global-setup.ts +103 -0
- package/src/pillars/api/playwright.config.ts +105 -0
- package/src/pillars/api/schemas/example-api.json +110 -0
- package/src/pillars/api/tests/health/health.spec.ts +19 -0
- package/src/pillars/api/tests/health/manifest.yml +6 -0
- package/src/pillars/browser/fixtures/index.ts +232 -0
- package/src/pillars/browser/fixtures/package.json +10 -0
- package/src/pillars/browser/global-setup.ts +140 -0
- package/src/pillars/browser/global-teardown.ts +48 -0
- package/src/pillars/browser/playwright.config.ts +212 -0
- package/src/pillars/browser/setup.ts +219 -0
- package/src/pillars/journeys/app-config.ts +237 -0
- package/src/pillars/journeys/discover.ts +160 -0
- package/src/pillars/journeys/hooks/deny-shell.cjs +44 -0
- package/src/pillars/journeys/observability/events.ts +288 -0
- package/src/pillars/journeys/observability/sdk-mapper.ts +306 -0
- package/src/pillars/journeys/report/friction.ts +304 -0
- package/src/pillars/journeys/report/junit.ts +105 -0
- package/src/pillars/journeys/report/render.ts +273 -0
- package/src/pillars/journeys/report/results.ts +323 -0
- package/src/pillars/journeys/run.ts +673 -0
- package/src/pillars/journeys/runner.ts +1025 -0
- package/src/pillars/journeys/sandbox.ts +403 -0
- package/src/pillars/journeys/spec/loader.ts +152 -0
- package/src/pillars/journeys/spec/schema.ts +208 -0
- package/src/pillars/journeys/teardown.ts +98 -0
- package/src/pillars/journeys/tools/browser-session.ts +219 -0
- package/src/pillars/journeys/tools/browser-tool.ts +914 -0
- package/src/pillars/journeys/tools/http-tool.ts +564 -0
- package/src/pillars/journeys/tools/ledger.ts +278 -0
- package/src/pillars/journeys/tools/snapshot-store.ts +117 -0
- package/src/pillars/journeys/verify/assertions.ts +502 -0
- package/src/pillars/journeys/verify/verdict.ts +71 -0
- package/src/pillars/perf/build-report.ts +36 -0
- package/src/pillars/perf/k6.config.js +112 -0
- package/src/pillars/perf/run.ts +345 -0
- package/src/pillars/perf/scenarios/load-profile.js +97 -0
- package/src/pillars/perf/staging.ts +73 -0
- package/src/pillars/security/discover.ts +124 -0
- package/src/pillars/security/findings.ts +151 -0
- package/src/pillars/security/plan.ts +355 -0
- package/src/pillars/security/run.ts +757 -0
- package/src/pillars/smoke/admin-probe.ts +68 -0
- package/src/pillars/smoke/base-url.ts +56 -0
- package/src/pillars/smoke/cleanup.ts +8 -0
- package/src/pillars/smoke/discover.ts +237 -0
- package/src/pillars/smoke/ensure-smoke-teardown.ts +40 -0
- package/src/pillars/smoke/fixture-manager.ts +217 -0
- package/src/pillars/smoke/fixtures.ts +468 -0
- package/src/pillars/smoke/global-setup-all.ts +262 -0
- package/src/pillars/smoke/global-setup-global.ts +262 -0
- package/src/pillars/smoke/global-setup.ts +273 -0
- package/src/pillars/smoke/global-teardown-all.ts +8 -0
- package/src/pillars/smoke/global-teardown-global.ts +8 -0
- package/src/pillars/smoke/global-teardown.ts +139 -0
- package/src/pillars/smoke/playwright-all.config.ts +147 -0
- package/src/pillars/smoke/playwright-global.config.ts +141 -0
- package/src/pillars/smoke/playwright.config.ts +162 -0
- package/src/pillars/smoke/setup-env.ts +132 -0
- package/src/pillars/smoke/setup-global.ts +126 -0
- package/src/pillars/smoke/setup.ts +115 -0
- package/src/pillars/smoke/smoke-repo.ts +16 -0
- package/src/pillars/smoke/staging.ts +49 -0
- package/src/pillars/suites/run.ts +147 -0
- package/src/scripts/configure-aws-profiles.ts +114 -0
- package/src/scripts/register-repos.ts +307 -0
- package/src/scripts/setup-default-workspace.ts +203 -0
- package/src/shared/agent-actor.ts +77 -0
- package/src/shared/applications.ts +138 -0
- package/src/shared/auth/api-token.ts +416 -0
- package/src/shared/auth/auth0-helper.ts +284 -0
- package/src/shared/auth/headless-login.ts +265 -0
- package/src/shared/auth/jwt-helper.ts +186 -0
- package/src/shared/auth/sudo-vault.ts +69 -0
- package/src/shared/auth/test-accounts.ts +98 -0
- package/src/shared/auth/token-injection.ts +184 -0
- package/src/shared/auth/token-providers/auth0.ts +80 -0
- package/src/shared/auth/token-providers/index.ts +182 -0
- package/src/shared/auth/token-refresh.ts +273 -0
- package/src/shared/banner.ts +15 -0
- package/src/shared/brand.ts +79 -0
- package/src/shared/cli-tabs.ts +131 -0
- package/src/shared/constants.ts +128 -0
- package/src/shared/den-config.ts +688 -0
- package/src/shared/dev-metrics/emit.ts +354 -0
- package/src/shared/edge/config.ts +229 -0
- package/src/shared/edge/render.ts +221 -0
- package/src/shared/edge/runtime.ts +268 -0
- package/src/shared/edge/status.ts +74 -0
- package/src/shared/edge/types.ts +62 -0
- package/src/shared/engine.ts +67 -0
- package/src/shared/environment/aws-profiles.ts +49 -0
- package/src/shared/environment/env-selector.ts +161 -0
- package/src/shared/environment/playwright-env.ts +82 -0
- package/src/shared/environment-manager/catalog.ts +77 -0
- package/src/shared/environment-manager/clone-guard.ts +163 -0
- package/src/shared/environment-manager/dag.ts +286 -0
- package/src/shared/environment-manager/default-workspace.ts +182 -0
- package/src/shared/environment-manager/derivation/alias.ts +90 -0
- package/src/shared/environment-manager/derivation/cache.ts +97 -0
- package/src/shared/environment-manager/derivation/derive-graph.ts +421 -0
- package/src/shared/environment-manager/derivation/derived-up.ts +354 -0
- package/src/shared/environment-manager/derivation/load-alias.ts +11 -0
- package/src/shared/environment-manager/derivation/rules-compose.ts +42 -0
- package/src/shared/environment-manager/derivation/rules-events.ts +50 -0
- package/src/shared/environment-manager/derivation/rules-http.ts +247 -0
- package/src/shared/environment-manager/derivation/rules-infra.ts +64 -0
- package/src/shared/environment-manager/derivation/rules-readiness.ts +96 -0
- package/src/shared/environment-manager/derivation/scan.ts +160 -0
- package/src/shared/environment-manager/derivation/text.ts +40 -0
- package/src/shared/environment-manager/derivation/types.ts +69 -0
- package/src/shared/environment-manager/health.ts +635 -0
- package/src/shared/environment-manager/index.ts +6 -0
- package/src/shared/environment-manager/load-applications.ts +108 -0
- package/src/shared/environment-manager/log-tail.ts +213 -0
- package/src/shared/environment-manager/make-targets.ts +78 -0
- package/src/shared/environment-manager/opctl.ts +335 -0
- package/src/shared/environment-manager/readiness.ts +93 -0
- package/src/shared/environment-manager/register-local.ts +72 -0
- package/src/shared/environment-manager/secrets-check.ts +260 -0
- package/src/shared/environment-manager/service-container.ts +47 -0
- package/src/shared/environment-manager/service-env.ts +110 -0
- package/src/shared/environment-manager/service-runner.ts +1603 -0
- package/src/shared/environment-manager/sync-targets.ts +66 -0
- package/src/shared/environment-manager/up-plan.ts +326 -0
- package/src/shared/environment-manager/workspace-manager.ts +905 -0
- package/src/shared/environment-manager/workspaces.ts +860 -0
- package/src/shared/manifest/deps.ts +27 -0
- package/src/shared/manifest/merge-override.ts +75 -0
- package/src/shared/manifest/migrate-manifest.ts +101 -0
- package/src/shared/manifest/placement.ts +321 -0
- package/src/shared/manifest/read-manifest.ts +278 -0
- package/src/shared/manifest/schema.ts +313 -0
- package/src/shared/manifest/service-links.ts +93 -0
- package/src/shared/manifest/validate-layers.ts +229 -0
- package/src/shared/manifest/validate-manifest.ts +200 -0
- package/src/shared/manifest/workspace-manifests.ts +92 -0
- package/src/shared/open-url.ts +122 -0
- package/src/shared/org.ts +44 -0
- package/src/shared/paths.ts +190 -0
- package/src/shared/perf/optional-fields.ts +105 -0
- package/src/shared/perf/report-normalizer.ts +404 -0
- package/src/shared/perf/report-paths.ts +19 -0
- package/src/shared/perf/report-store.ts +110 -0
- package/src/shared/perf/report-types.ts +70 -0
- package/src/shared/perf/test-paths.ts +25 -0
- package/src/shared/process/log-rotation.ts +147 -0
- package/src/shared/process/spawn-streaming.ts +229 -0
- package/src/shared/product.ts +92 -0
- package/src/shared/profile.ts +141 -0
- package/src/shared/redact.ts +107 -0
- package/src/shared/repo-kind.ts +32 -0
- package/src/shared/reporting/jsonl-reporter.ts +131 -0
- package/src/shared/reporting/slack-reporter.ts +340 -0
- package/src/shared/results/result-reader.ts +266 -0
- package/src/shared/results/result-writer.ts +168 -0
- package/src/shared/secrets/doppler.ts +126 -0
- package/src/shared/secrets/env.ts +48 -0
- package/src/shared/secrets/index.ts +197 -0
- package/src/shared/secrets/keys.ts +33 -0
- package/src/shared/secrets/onepassword.ts +75 -0
- package/src/shared/secrets/types.ts +52 -0
- package/src/shared/service-url.ts +119 -0
- package/src/shared/smoke/branch-resolver.ts +141 -0
- package/src/shared/smoke/git-remote.ts +70 -0
- package/src/shared/smoke/repo-cloner.ts +151 -0
- package/src/shared/stacks/config.ts +137 -0
- package/src/shared/stacks/manifest-commands.ts +118 -0
- package/src/shared/stacks/render.ts +253 -0
- package/src/shared/stacks/runtime.ts +334 -0
- package/src/shared/stacks/status.ts +99 -0
- package/src/shared/stacks/types.ts +53 -0
- package/src/shared/terminal/tabs.ts +212 -0
- package/src/shared/test-discovery/module-scanner.ts +569 -0
- package/src/shared/types/index.ts +701 -0
- package/src/shared/ui-marker.ts +19 -0
- package/src/ui/cli/applications-command.ts +148 -0
- package/src/ui/cli/config-command.ts +68 -0
- package/src/ui/cli/create-main.ts +9 -0
- package/src/ui/cli/create.ts +360 -0
- package/src/ui/cli/edge-command.ts +81 -0
- package/src/ui/cli/flag-parser.ts +246 -0
- package/src/ui/cli/graph-command.ts +570 -0
- package/src/ui/cli/health-display.ts +63 -0
- package/src/ui/cli/index.ts +3742 -0
- package/src/ui/cli/init.ts +855 -0
- package/src/ui/cli/invocation-target.ts +175 -0
- package/src/ui/cli/logs-command.ts +104 -0
- package/src/ui/cli/mcp-main.ts +13 -0
- package/src/ui/cli/mcp-stdout-guard.ts +10 -0
- package/src/ui/cli/module-selection.ts +31 -0
- package/src/ui/cli/prompts.ts +767 -0
- package/src/ui/cli/results-viewer.ts +436 -0
- package/src/ui/cli/secrets-preflight.ts +91 -0
- package/src/ui/cli/stack-command.ts +117 -0
- package/src/ui/cli/stalled-setup-recovery.ts +129 -0
- package/src/ui/cli/test-runner.ts +1319 -0
- package/src/ui/cli/web-command.ts +1073 -0
- package/src/ui/mcp/api.ts +284 -0
- package/src/ui/mcp/guards.ts +141 -0
- package/src/ui/mcp/server.ts +147 -0
- package/src/ui/mcp/tools.ts +565 -0
- package/src/ui/server/audit.ts +71 -0
- package/src/ui/server/changelog-parser.ts +105 -0
- package/src/ui/server/daemon-reaper.ts +129 -0
- package/src/ui/server/editor-manager.ts +301 -0
- package/src/ui/server/github-service.ts +223 -0
- package/src/ui/server/graph-prefs.ts +89 -0
- package/src/ui/server/health-row.ts +15 -0
- package/src/ui/server/http-helpers.ts +98 -0
- package/src/ui/server/index.ts +3350 -0
- package/src/ui/server/log-history.ts +453 -0
- package/src/ui/server/perf-report.ts +42 -0
- package/src/ui/server/public/environments.html +496 -0
- package/src/ui/server/public/favicon.ico +0 -0
- package/src/ui/server/public/graph-model.js +416 -0
- package/src/ui/server/public/graph.css +976 -0
- package/src/ui/server/public/graph.js +986 -0
- package/src/ui/server/public/header.js +1373 -0
- package/src/ui/server/public/index.html +2595 -0
- package/src/ui/server/public/local-dev.html +5344 -0
- package/src/ui/server/public/logs.html +1496 -0
- package/src/ui/server/public/panel.js +787 -0
- package/src/ui/server/public/perf-report.js +359 -0
- package/src/ui/server/public/release.html +2111 -0
- package/src/ui/server/public/services.html +1103 -0
- package/src/ui/server/public/workspace-picker.js +69 -0
- package/src/ui/server/readonly-routes.ts +200 -0
- package/src/ui/server/release-deploy-session.ts +246 -0
- package/src/ui/server/release-service.ts +1125 -0
- package/src/ui/server/sudo-store.ts +111 -0
- package/src/ui/server/ui-assets.ts +73 -0
- package/src/ui/server/workspace-routes.ts +2185 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright Configuration — All Smoke Tests, All Repos
|
|
3
|
+
*
|
|
4
|
+
* Used by: make test-smk-global
|
|
5
|
+
*
|
|
6
|
+
* Creates one Playwright project per service per registered repo.
|
|
7
|
+
* All results appear in a single combined report.
|
|
8
|
+
*
|
|
9
|
+
* Environment variables:
|
|
10
|
+
* E2E_ENVIRONMENT — env config to load (default: local)
|
|
11
|
+
* BASE_API_URL — override API base URL
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { defineConfig } from "@playwright/test";
|
|
15
|
+
import * as fs from "fs";
|
|
16
|
+
import * as path from "path";
|
|
17
|
+
import { findManifest } from "./discover.js";
|
|
18
|
+
import {
|
|
19
|
+
resolveManifestSmokeBaseUrl,
|
|
20
|
+
resolveSmokeBaseUrl,
|
|
21
|
+
} from "./base-url.js";
|
|
22
|
+
import type { Environment } from "../../shared/types/index.js";
|
|
23
|
+
import { getDistRoot } from "../../shared/paths.js";
|
|
24
|
+
import { distPath, enginePath } from "../../shared/engine.js";
|
|
25
|
+
|
|
26
|
+
import { product } from "../../shared/product.js";
|
|
27
|
+
// Den-root anchored in lockstep with setup-global.ts (the manifest writer).
|
|
28
|
+
const PROJECT_ROOT = getDistRoot();
|
|
29
|
+
const SMOKE_RUN_DIR = path.resolve(PROJECT_ROOT, ".smoke-run");
|
|
30
|
+
const environment = (process.env.E2E_ENVIRONMENT || "local") as Environment;
|
|
31
|
+
|
|
32
|
+
const configPath = path.resolve(
|
|
33
|
+
PROJECT_ROOT,
|
|
34
|
+
`config/environments/${environment}.json`,
|
|
35
|
+
);
|
|
36
|
+
let envConfig: { baseUrls: { api: string; web: string } };
|
|
37
|
+
try {
|
|
38
|
+
envConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
39
|
+
} catch {
|
|
40
|
+
console.error(
|
|
41
|
+
`\n❌ playwright-global.config: no env config for "${environment}"`,
|
|
42
|
+
);
|
|
43
|
+
console.error(` Expected: config/environments/${environment}.json\n`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Read exactly what setup-global.ts staged — no second discoverRepos() call
|
|
48
|
+
const globalManifestPath = path.join(SMOKE_RUN_DIR, "global-manifest.json");
|
|
49
|
+
if (!fs.existsSync(globalManifestPath)) {
|
|
50
|
+
console.error(
|
|
51
|
+
`\n❌ No staged smoke tests found. Run: make test-smk-global\n`,
|
|
52
|
+
);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const staged: Array<{
|
|
56
|
+
repo: string;
|
|
57
|
+
svc: string;
|
|
58
|
+
dir: string;
|
|
59
|
+
testMatch: string;
|
|
60
|
+
baseUrlKey: string;
|
|
61
|
+
}> = JSON.parse(fs.readFileSync(globalManifestPath, "utf-8"));
|
|
62
|
+
|
|
63
|
+
if (!staged.length) {
|
|
64
|
+
console.error(
|
|
65
|
+
`\n❌ No smoke services staged. Check your ${product().manifestYml} smoke: keys.\n`,
|
|
66
|
+
);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const projects = staged.map(({ repo, svc, dir, testMatch, baseUrlKey }) => {
|
|
71
|
+
if (!fs.existsSync(dir)) {
|
|
72
|
+
console.error(`\n❌ Staging dir missing: ${dir}`);
|
|
73
|
+
console.error(` Re-run: make test-smk-global\n`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const configuredUrl =
|
|
78
|
+
baseUrlKey === "web" ? envConfig.baseUrls.web : envConfig.baseUrls.api;
|
|
79
|
+
const discovered = findManifest(repo);
|
|
80
|
+
if (!discovered) {
|
|
81
|
+
console.error(
|
|
82
|
+
`\n❌ No manifest found for repo "${repo}" in .${product().name}/`,
|
|
83
|
+
);
|
|
84
|
+
console.error(` Run: make register-repos\n`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
const manifestUrl = resolveManifestSmokeBaseUrl({
|
|
88
|
+
manifest: discovered.manifest,
|
|
89
|
+
repoName: repo,
|
|
90
|
+
serviceName: svc,
|
|
91
|
+
environment,
|
|
92
|
+
baseUrlKey: baseUrlKey === "web" ? "web" : "api",
|
|
93
|
+
});
|
|
94
|
+
const baseUrl = resolveSmokeBaseUrl({
|
|
95
|
+
baseUrlKey: baseUrlKey === "web" ? "web" : "api",
|
|
96
|
+
configuredUrl,
|
|
97
|
+
manifestUrl,
|
|
98
|
+
overrideUrl: process.env.BASE_API_URL,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
name: `${repo}/${svc}`,
|
|
103
|
+
testDir: dir,
|
|
104
|
+
testMatch,
|
|
105
|
+
use: {
|
|
106
|
+
baseURL: baseUrl,
|
|
107
|
+
timeout: 30000,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
export default defineConfig({
|
|
113
|
+
globalSetup: enginePath("src/pillars/smoke/global-setup-global.ts"),
|
|
114
|
+
globalTeardown: enginePath("src/pillars/smoke/global-teardown-global.ts"),
|
|
115
|
+
|
|
116
|
+
fullyParallel: true,
|
|
117
|
+
forbidOnly: !!process.env.CI,
|
|
118
|
+
retries: process.env.CI ? 2 : 0,
|
|
119
|
+
workers: process.env.CI ? 1 : undefined,
|
|
120
|
+
|
|
121
|
+
reporter: [
|
|
122
|
+
[
|
|
123
|
+
"html",
|
|
124
|
+
{
|
|
125
|
+
outputFolder:
|
|
126
|
+
process.env.PLAYWRIGHT_HTML_REPORT || distPath("playwright-report"),
|
|
127
|
+
open: "never",
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
["list"],
|
|
131
|
+
[
|
|
132
|
+
"junit",
|
|
133
|
+
{ outputFile: distPath("reports", "junit-smoke-global-results.xml") },
|
|
134
|
+
],
|
|
135
|
+
],
|
|
136
|
+
|
|
137
|
+
projects,
|
|
138
|
+
|
|
139
|
+
timeout: 60000,
|
|
140
|
+
expect: { timeout: 10000 },
|
|
141
|
+
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright Configuration for Smoke Tests
|
|
3
|
+
*
|
|
4
|
+
* Dynamically configured from the service's .den.json manifest.
|
|
5
|
+
* The testDir, testMatch, and baseURL are all resolved at config time
|
|
6
|
+
* from the manifest discovered in .den/<repo>/.den.json.
|
|
7
|
+
*
|
|
8
|
+
* Environment variables:
|
|
9
|
+
* SMOKE_SERVICE — service name in the manifest (default: api)
|
|
10
|
+
* SMOKE_REPO — repo name in .den/ (required)
|
|
11
|
+
* E2E_ENVIRONMENT — env config to load (default: local)
|
|
12
|
+
* BASE_API_URL — override base URL (useful for one-off runs)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { defineConfig } from "@playwright/test";
|
|
16
|
+
import * as fs from "fs";
|
|
17
|
+
import * as path from "path";
|
|
18
|
+
import { findManifest, resolveService } from "./discover.js";
|
|
19
|
+
import { getDistRoot } from "../../shared/paths.js";
|
|
20
|
+
import { distPath, enginePath } from "../../shared/engine.js";
|
|
21
|
+
import {
|
|
22
|
+
resolveManifestSmokeBaseUrl,
|
|
23
|
+
resolveSmokeBaseUrl,
|
|
24
|
+
} from "./base-url.js";
|
|
25
|
+
import { smokeRepoName } from "./smoke-repo.js";
|
|
26
|
+
|
|
27
|
+
import { product } from "../../shared/product.js";
|
|
28
|
+
// Den-root anchored in lockstep with setup.ts (the .smoke-run writer).
|
|
29
|
+
const PROJECT_ROOT = getDistRoot();
|
|
30
|
+
|
|
31
|
+
function resolveConfig() {
|
|
32
|
+
const serviceName = process.env.SMOKE_SERVICE || "api";
|
|
33
|
+
const repoName = smokeRepoName();
|
|
34
|
+
const environment = process.env.E2E_ENVIRONMENT || "local";
|
|
35
|
+
|
|
36
|
+
// Discover the repo manifest
|
|
37
|
+
const discovered = findManifest(repoName);
|
|
38
|
+
if (!discovered) {
|
|
39
|
+
console.error(
|
|
40
|
+
`\n❌ Smoke config: no manifest found for repo "${repoName}" in .${product().name}/`,
|
|
41
|
+
);
|
|
42
|
+
console.error(` Run: make smoke-link repo=${repoName}\n`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const svc = resolveService(discovered, serviceName);
|
|
47
|
+
if (!svc) {
|
|
48
|
+
const available = Object.keys(discovered.manifest.smoke ?? {}).join(", ");
|
|
49
|
+
console.error(
|
|
50
|
+
`\n❌ Smoke config: service "${serviceName}" not found in ${repoName}/${product().manifestJson}`,
|
|
51
|
+
);
|
|
52
|
+
console.error(` Available services: ${available}\n`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Load environment config for the base URL
|
|
57
|
+
const configPath = path.resolve(
|
|
58
|
+
PROJECT_ROOT,
|
|
59
|
+
`config/environments/${environment}.json`,
|
|
60
|
+
);
|
|
61
|
+
let envConfig: { baseUrls: { api: string; web: string } };
|
|
62
|
+
try {
|
|
63
|
+
envConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
64
|
+
} catch {
|
|
65
|
+
console.error(
|
|
66
|
+
`\n❌ Smoke config: no env config found for "${environment}"`,
|
|
67
|
+
);
|
|
68
|
+
console.error(` Expected: config/environments/${environment}.json\n`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const configuredUrl =
|
|
73
|
+
svc.baseUrlKey === "api" ? envConfig.baseUrls.api : envConfig.baseUrls.web;
|
|
74
|
+
const manifestUrl = resolveManifestSmokeBaseUrl({
|
|
75
|
+
manifest: discovered.manifest,
|
|
76
|
+
repoName,
|
|
77
|
+
serviceName,
|
|
78
|
+
environment: environment as
|
|
79
|
+
| "local"
|
|
80
|
+
| "dev"
|
|
81
|
+
| "sandbox"
|
|
82
|
+
| "staging"
|
|
83
|
+
| "production",
|
|
84
|
+
baseUrlKey: svc.baseUrlKey,
|
|
85
|
+
});
|
|
86
|
+
const baseUrl = resolveSmokeBaseUrl({
|
|
87
|
+
baseUrlKey: svc.baseUrlKey,
|
|
88
|
+
configuredUrl,
|
|
89
|
+
manifestUrl,
|
|
90
|
+
overrideUrl: process.env.BASE_API_URL,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Use the staged test dir (.smoke-run/{repo}-{service}/) which has a
|
|
94
|
+
// node_modules shim pointing to den's playwright. This prevents the
|
|
95
|
+
// "Requiring @playwright/test second time" error caused by the repo's own
|
|
96
|
+
// @playwright/test in node_modules being found during test file loading.
|
|
97
|
+
const stagingDir = path.resolve(
|
|
98
|
+
PROJECT_ROOT,
|
|
99
|
+
`.smoke-run/${repoName}-${serviceName}`,
|
|
100
|
+
);
|
|
101
|
+
if (!fs.existsSync(stagingDir)) {
|
|
102
|
+
console.error(
|
|
103
|
+
`\n❌ Staging dir not found: .smoke-run/${repoName}-${serviceName}/`,
|
|
104
|
+
);
|
|
105
|
+
console.error(
|
|
106
|
+
` The setup step runs automatically via make targets. If running`,
|
|
107
|
+
);
|
|
108
|
+
console.error(
|
|
109
|
+
` playwright directly, run: SMOKE_SERVICE=${serviceName} SMOKE_REPO=${repoName} npx tsx src/pillars/smoke/setup.ts\n`,
|
|
110
|
+
);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { svc: { ...svc, testDir: stagingDir }, baseUrl };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const { svc, baseUrl } = resolveConfig();
|
|
118
|
+
|
|
119
|
+
export default defineConfig({
|
|
120
|
+
testDir: svc.testDir,
|
|
121
|
+
testMatch: svc.testMatch,
|
|
122
|
+
|
|
123
|
+
globalSetup: enginePath("src/pillars/smoke/global-setup.ts"),
|
|
124
|
+
globalTeardown: enginePath("src/pillars/smoke/global-teardown.ts"),
|
|
125
|
+
|
|
126
|
+
fullyParallel: true,
|
|
127
|
+
forbidOnly: !!process.env.CI,
|
|
128
|
+
retries: process.env.CI ? 2 : 0,
|
|
129
|
+
workers: process.env.CI ? 1 : undefined,
|
|
130
|
+
|
|
131
|
+
reporter: [
|
|
132
|
+
[
|
|
133
|
+
"html",
|
|
134
|
+
{
|
|
135
|
+
outputFolder:
|
|
136
|
+
process.env.PLAYWRIGHT_HTML_REPORT || distPath("playwright-report"),
|
|
137
|
+
open: "never",
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
["list"],
|
|
141
|
+
["junit", { outputFile: distPath("reports", "junit-smoke-results.xml") }],
|
|
142
|
+
],
|
|
143
|
+
|
|
144
|
+
use: {
|
|
145
|
+
baseURL: baseUrl,
|
|
146
|
+
// JSON headers only for API services; web tests use a headless browser
|
|
147
|
+
...(svc.baseUrlKey === "api"
|
|
148
|
+
? {
|
|
149
|
+
extraHTTPHeaders: {
|
|
150
|
+
Accept: "application/json",
|
|
151
|
+
"Content-Type": "application/json",
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
: {}),
|
|
155
|
+
timeout: 30000,
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
projects: [{ name: "smoke", use: {} }],
|
|
159
|
+
|
|
160
|
+
timeout: 60000,
|
|
161
|
+
expect: { timeout: 10000 },
|
|
162
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smoke Test Environment Setup
|
|
3
|
+
*
|
|
4
|
+
* For non-local environments: resolves the exact deployed SHA from the GitHub
|
|
5
|
+
* Deployments API and ensures the repo is cloned at that SHA inside
|
|
6
|
+
* .smoke-clones/{repo}/{env}/.
|
|
7
|
+
*
|
|
8
|
+
* For local: no-op — the local sibling repo is already linked via .den/.
|
|
9
|
+
*
|
|
10
|
+
* Usage (called by buildCommand before the staging step):
|
|
11
|
+
* npx tsx src/pillars/smoke/setup-env.ts --env staging [--repo api]
|
|
12
|
+
*
|
|
13
|
+
* Multiple --repo flags are accepted for global runs:
|
|
14
|
+
* npx tsx src/pillars/smoke/setup-env.ts --env staging --repo api --repo other-repo
|
|
15
|
+
*
|
|
16
|
+
* If --repo is omitted, all registered repos with smoke tests are resolved.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { discoverRepos, findManifest } from "./discover.js";
|
|
20
|
+
import { resolveDeployedRef } from "../../shared/smoke/branch-resolver.js";
|
|
21
|
+
import { ensureRepoAtSha } from "../../shared/smoke/repo-cloner.js";
|
|
22
|
+
import {
|
|
23
|
+
getGitHubCoords,
|
|
24
|
+
getGitRemoteOrigin,
|
|
25
|
+
} from "../../shared/smoke/git-remote.js";
|
|
26
|
+
import type { Environment } from "../../shared/types/index.js";
|
|
27
|
+
|
|
28
|
+
const LOCAL_ENV: Environment = "local";
|
|
29
|
+
|
|
30
|
+
function parseArgs(): { environment: Environment; repoNames: string[] } {
|
|
31
|
+
const args = process.argv.slice(2);
|
|
32
|
+
let environment: Environment = LOCAL_ENV;
|
|
33
|
+
const repoNames: string[] = [];
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < args.length; i++) {
|
|
36
|
+
if (args[i] === "--env" && args[i + 1]) {
|
|
37
|
+
environment = args[++i] as Environment;
|
|
38
|
+
} else if (args[i] === "--repo" && args[i + 1]) {
|
|
39
|
+
repoNames.push(args[++i]);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return { environment, repoNames };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function main() {
|
|
47
|
+
const { environment, repoNames } = parseArgs();
|
|
48
|
+
|
|
49
|
+
if (environment === LOCAL_ENV) {
|
|
50
|
+
// Nothing to do — local tests use the sibling repo via .den/ symlink
|
|
51
|
+
console.log(" ℹ local environment: using linked repo (no clone needed)");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Gather repos to resolve
|
|
56
|
+
let repos = discoverRepos().filter(
|
|
57
|
+
(r) =>
|
|
58
|
+
Object.keys(r.manifest.smoke ?? {}).length > 0 || !!r.manifest.synthetics,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (repoNames.length > 0) {
|
|
62
|
+
const filtered = repoNames.map((name) => {
|
|
63
|
+
const found = findManifest(name);
|
|
64
|
+
if (!found) {
|
|
65
|
+
console.error(`❌ Repo "${name}" not found. Run: make register-repos`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
return found;
|
|
69
|
+
});
|
|
70
|
+
repos = filtered;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!repos.length) {
|
|
74
|
+
console.error(
|
|
75
|
+
"❌ No repos with smoke tests registered. Run: make register-repos",
|
|
76
|
+
);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(
|
|
81
|
+
`\n🔍 Resolving deployed branch for ${repos.map((r) => r.repoName).join(", ")} @ ${environment}\n`,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
for (const repo of repos) {
|
|
85
|
+
// Derive GitHub owner/repo from the local repo's git remote
|
|
86
|
+
const coords = getGitHubCoords(repo.repoPath);
|
|
87
|
+
if (!coords) {
|
|
88
|
+
console.error(
|
|
89
|
+
`❌ Cannot determine GitHub owner/repo for "${repo.repoName}". ` +
|
|
90
|
+
`No git remote origin found at ${repo.repoPath}`,
|
|
91
|
+
);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Allow .den.yml to override the GitHub environment name
|
|
96
|
+
const githubEnv =
|
|
97
|
+
repo.manifest.github?.environments?.[environment] ?? environment;
|
|
98
|
+
|
|
99
|
+
let ref;
|
|
100
|
+
try {
|
|
101
|
+
ref = await resolveDeployedRef(coords.owner, coords.repo, githubEnv);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error(`❌ ${(err as Error).message}`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
console.log(
|
|
108
|
+
` 📦 ${repo.repoName}: ${coords.owner}/${coords.repo}@${githubEnv} → ` +
|
|
109
|
+
`${ref.sha.slice(0, 8)} (${ref.ref}, deployed ${new Date(ref.deployedAt).toLocaleString()})`,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Get the git remote URL from the local symlinked/registered copy
|
|
113
|
+
const originUrl = getGitRemoteOrigin(repo.repoPath);
|
|
114
|
+
if (!originUrl) {
|
|
115
|
+
console.error(
|
|
116
|
+
`❌ Cannot get git remote origin URL for "${repo.repoName}" at ${repo.repoPath}`,
|
|
117
|
+
);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
ensureRepoAtSha(repo.repoName, environment, originUrl, ref);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
console.log(
|
|
125
|
+
`\n✅ All repos cloned/updated for environment "${environment}"\n`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
main().catch((err) => {
|
|
130
|
+
console.error(err);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smoke Test Staging Setup — All Repos
|
|
3
|
+
*
|
|
4
|
+
* Stages smoke test files for every service in every registered repo.
|
|
5
|
+
* Used by: make test-smk-global
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from "fs";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
import { discoverRepos, resolveRepoPath, resolveService } from "./discover.js";
|
|
11
|
+
import { stageSmokeTree } from "./staging.js";
|
|
12
|
+
import type { Environment } from "../../shared/types/index.js";
|
|
13
|
+
import { getDistRoot } from "../../shared/paths.js";
|
|
14
|
+
|
|
15
|
+
// Den-root anchored, NOT process.cwd() — bulk writer of .smoke-run (see setup.ts).
|
|
16
|
+
const PROJECT_ROOT = getDistRoot();
|
|
17
|
+
const SMOKE_RUN_DIR = path.resolve(PROJECT_ROOT, ".smoke-run");
|
|
18
|
+
const DEN_MODULES = path.resolve(PROJECT_ROOT, "node_modules");
|
|
19
|
+
|
|
20
|
+
/** Separator used in staging dir names — must not appear in repo or service names. */
|
|
21
|
+
export const STAGING_SEP = "__";
|
|
22
|
+
|
|
23
|
+
/** Canonical staging dir for a repo+service pair. */
|
|
24
|
+
export function stagingDirFor(repoName: string, svcName: string): string {
|
|
25
|
+
return path.join(SMOKE_RUN_DIR, `${repoName}${STAGING_SEP}${svcName}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function main() {
|
|
29
|
+
const environment = (process.env.E2E_ENVIRONMENT || "local") as Environment;
|
|
30
|
+
const useLocalCheckout = process.env.USE_LOCAL_CHECKOUT === "1";
|
|
31
|
+
const repos = discoverRepos();
|
|
32
|
+
if (!repos.length) {
|
|
33
|
+
console.error("❌ No repos registered. Run: make register-repos");
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let total = 0;
|
|
38
|
+
const staged: Array<{
|
|
39
|
+
repo: string;
|
|
40
|
+
svc: string;
|
|
41
|
+
dir: string;
|
|
42
|
+
testMatch: string;
|
|
43
|
+
baseUrlKey: string;
|
|
44
|
+
}> = [];
|
|
45
|
+
|
|
46
|
+
for (const repo of repos) {
|
|
47
|
+
const services = Object.keys(repo.manifest.smoke ?? {});
|
|
48
|
+
if (!services.length) continue;
|
|
49
|
+
|
|
50
|
+
// Resolve the staging root for this run: deployed clone by default for
|
|
51
|
+
// cloud envs, or the registered checkout when explicitly requested.
|
|
52
|
+
let repoPath: string;
|
|
53
|
+
try {
|
|
54
|
+
repoPath = resolveRepoPath(repo, environment, useLocalCheckout);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.error(`❌ ${(err as Error).message}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const envRepo = { ...repo, repoPath };
|
|
60
|
+
|
|
61
|
+
for (const svcName of services) {
|
|
62
|
+
const svc = resolveService(envRepo, svcName);
|
|
63
|
+
if (!svc) continue;
|
|
64
|
+
|
|
65
|
+
const stagingDir = stagingDirFor(repo.repoName, svcName);
|
|
66
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
67
|
+
fs.mkdirSync(stagingDir, { recursive: true });
|
|
68
|
+
|
|
69
|
+
// Node modules shim — point @playwright/test to den's copy
|
|
70
|
+
const pwScopeDir = path.join(stagingDir, "node_modules", "@playwright");
|
|
71
|
+
fs.mkdirSync(pwScopeDir, { recursive: true });
|
|
72
|
+
fs.symlinkSync(
|
|
73
|
+
path.join(DEN_MODULES, "@playwright", "test"),
|
|
74
|
+
path.join(pwScopeDir, "test"),
|
|
75
|
+
);
|
|
76
|
+
fs.symlinkSync(
|
|
77
|
+
path.join(DEN_MODULES, "playwright"),
|
|
78
|
+
path.join(stagingDir, "node_modules", "playwright"),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
let count = 0;
|
|
82
|
+
try {
|
|
83
|
+
const result = stageSmokeTree(svc.testDir, stagingDir, svc.testMatch);
|
|
84
|
+
count = result.matchedTestFiles;
|
|
85
|
+
if (result.matchedTestFiles === 0) {
|
|
86
|
+
console.warn(
|
|
87
|
+
` ⚠ No files matching "${svc.testMatch}" in ${svc.testDir}`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
console.log(
|
|
91
|
+
`✅ ${repo.repoName}/${svcName}: staged ${result.matchedTestFiles} smoke test file(s) and ${result.copiedFiles} total file(s) → ${path.basename(stagingDir)}/`,
|
|
92
|
+
);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
95
|
+
console.warn(` ⚠ testDir not found, skipping: ${svc.testDir}`);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
staged.push({
|
|
101
|
+
repo: repo.repoName,
|
|
102
|
+
svc: svcName,
|
|
103
|
+
dir: stagingDir,
|
|
104
|
+
testMatch: svc.testMatch,
|
|
105
|
+
baseUrlKey: svc.baseUrlKey ?? "api",
|
|
106
|
+
});
|
|
107
|
+
total += count;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Write a manifest so playwright-global.config.ts reads exactly what was staged
|
|
112
|
+
// (avoids a second discoverRepos() call that could diverge)
|
|
113
|
+
fs.writeFileSync(
|
|
114
|
+
path.join(SMOKE_RUN_DIR, "global-manifest.json"),
|
|
115
|
+
JSON.stringify(staged, null, 2),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
console.log(
|
|
119
|
+
`\n Total: ${total} smoke test file(s) staged across ${repos.length} repo(s)\n`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
main().catch((err) => {
|
|
124
|
+
console.error(err);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smoke Test Staging Setup
|
|
3
|
+
*
|
|
4
|
+
* Creates .smoke-run/{repo}-{service}/ before each test run:
|
|
5
|
+
*
|
|
6
|
+
* 1. Copies all *.smoke.ts files from the repo's testDir
|
|
7
|
+
* 2. Creates node_modules/@playwright/test → den's playwright
|
|
8
|
+
* 3. Creates node_modules/playwright → den's playwright
|
|
9
|
+
*
|
|
10
|
+
* Why: service repos usually have their own @playwright/test in
|
|
11
|
+
* node_modules. If Playwright's test runner loads test files from those
|
|
12
|
+
* directories, Node resolves @playwright/test to the repo's copy — which is
|
|
13
|
+
* a different module instance than the runner's, causing the "Requiring
|
|
14
|
+
* @playwright/test second time" error.
|
|
15
|
+
*
|
|
16
|
+
* Staging the files inside den (with a node_modules shim) ensures
|
|
17
|
+
* there is exactly one @playwright/test instance: den's.
|
|
18
|
+
*
|
|
19
|
+
* The staged dir is gitignored (.smoke-run/) and re-created fresh each run.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import * as fs from "fs";
|
|
23
|
+
import * as path from "path";
|
|
24
|
+
import { findManifest, resolveRepoPath, resolveService } from "./discover.js";
|
|
25
|
+
import { stageSmokeTree } from "./staging.js";
|
|
26
|
+
import type { Environment } from "../../shared/types/index.js";
|
|
27
|
+
import { getDistRoot } from "../../shared/paths.js";
|
|
28
|
+
import { smokeRepoName } from "./smoke-repo.js";
|
|
29
|
+
|
|
30
|
+
import { product } from "../../shared/product.js";
|
|
31
|
+
// Den-root anchored, NOT process.cwd(): this stages (rm -rf + mkdir +
|
|
32
|
+
// symlink + tree-copy) .smoke-run, and CLI-spawned runs inherit the user's cwd.
|
|
33
|
+
const PROJECT_ROOT = getDistRoot();
|
|
34
|
+
const SMOKE_RUN_DIR = path.resolve(PROJECT_ROOT, ".smoke-run");
|
|
35
|
+
const ENGINE_MODULES = path.resolve(PROJECT_ROOT, "node_modules");
|
|
36
|
+
|
|
37
|
+
async function main() {
|
|
38
|
+
const serviceName = process.env.SMOKE_SERVICE || "api";
|
|
39
|
+
const repoName = smokeRepoName();
|
|
40
|
+
const environment = (process.env.E2E_ENVIRONMENT || "local") as Environment;
|
|
41
|
+
const useLocalCheckout = process.env.USE_LOCAL_CHECKOUT === "1";
|
|
42
|
+
|
|
43
|
+
const discovered = findManifest(repoName);
|
|
44
|
+
if (!discovered) {
|
|
45
|
+
console.error(
|
|
46
|
+
`❌ No manifest found for repo "${repoName}" in .${product().name}/`,
|
|
47
|
+
);
|
|
48
|
+
console.error(` Run: make smoke-link repo=${repoName}`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Non-local smoke runs stage from the deployed clone by default. Set
|
|
53
|
+
// USE_LOCAL_CHECKOUT=1 to stage from the registered checkout instead.
|
|
54
|
+
let repoPath: string;
|
|
55
|
+
try {
|
|
56
|
+
repoPath = resolveRepoPath(discovered, environment, useLocalCheckout);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.error(`❌ ${(err as Error).message}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Re-resolve the service from the selected staging root so testDir points
|
|
63
|
+
// into either the deployed clone or the registered checkout.
|
|
64
|
+
const envDiscovered = { ...discovered, repoPath };
|
|
65
|
+
const svc = resolveService(envDiscovered, serviceName);
|
|
66
|
+
if (!svc) {
|
|
67
|
+
const available = Object.keys(discovered.manifest.smoke ?? {}).join(", ");
|
|
68
|
+
console.error(
|
|
69
|
+
`❌ Service "${serviceName}" not found in ${repoName}/${product().manifestJson}`,
|
|
70
|
+
);
|
|
71
|
+
console.error(` Available: ${available}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const stagingDir = path.join(SMOKE_RUN_DIR, `${repoName}-${serviceName}`);
|
|
76
|
+
|
|
77
|
+
// Clean and recreate staging dir
|
|
78
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
79
|
+
fs.mkdirSync(stagingDir, { recursive: true });
|
|
80
|
+
|
|
81
|
+
// ── Node modules shim ──────────────────────────────────────────────────
|
|
82
|
+
// Symlink den's playwright into the staging dir's node_modules so
|
|
83
|
+
// that any @playwright/test import in copied test files resolves to the
|
|
84
|
+
// runner's copy, not the repo's own copy.
|
|
85
|
+
const nmDir = path.join(stagingDir, "node_modules");
|
|
86
|
+
const pwScopeDir = path.join(nmDir, "@playwright");
|
|
87
|
+
fs.mkdirSync(pwScopeDir, { recursive: true });
|
|
88
|
+
|
|
89
|
+
const pwTestSrc = path.join(ENGINE_MODULES, "@playwright", "test");
|
|
90
|
+
const pwTestDst = path.join(pwScopeDir, "test");
|
|
91
|
+
fs.symlinkSync(pwTestSrc, pwTestDst);
|
|
92
|
+
|
|
93
|
+
const pwSrc = path.join(ENGINE_MODULES, "playwright");
|
|
94
|
+
const pwDst = path.join(nmDir, "playwright");
|
|
95
|
+
fs.symlinkSync(pwSrc, pwDst);
|
|
96
|
+
|
|
97
|
+
// ── Copy test tree ─────────────────────────────────────────────────────
|
|
98
|
+
// Copy the full testDir so smoke tests can import shared helper modules
|
|
99
|
+
// that do not themselves match `testMatch`.
|
|
100
|
+
const result = stageSmokeTree(svc.testDir, stagingDir, svc.testMatch);
|
|
101
|
+
if (result.matchedTestFiles === 0) {
|
|
102
|
+
console.warn(
|
|
103
|
+
` ⚠ No files matching "${svc.testMatch}" found in ${svc.testDir}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
console.log(
|
|
108
|
+
`✅ Staged ${result.matchedTestFiles} smoke test file(s) and ${result.copiedFiles} total file(s) → .smoke-run/${repoName}-${serviceName}/`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
main().catch((err) => {
|
|
113
|
+
console.error(err);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The repo a smoke run targets. Always named by the caller (`make
|
|
3
|
+
* test-smoke repo=<name>` / the web UI set SMOKE_REPO); the engine has no
|
|
4
|
+
* default repo, so a missing name fails loudly instead of guessing one.
|
|
5
|
+
*/
|
|
6
|
+
export function smokeRepoName(env: NodeJS.ProcessEnv = process.env): string {
|
|
7
|
+
const name = env.SMOKE_REPO?.trim();
|
|
8
|
+
if (!name) {
|
|
9
|
+
console.error(
|
|
10
|
+
"\n❌ SMOKE_REPO is not set: name the repo whose smoke tests to run " +
|
|
11
|
+
"(make test-smoke repo=<name>).\n",
|
|
12
|
+
);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
return name;
|
|
16
|
+
}
|