@ykdz/template 0.0.32 → 0.0.35

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.
Files changed (40) hide show
  1. package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.d.ts +2 -0
  2. package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.js +40 -14
  3. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.d.ts +1 -0
  4. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.js +17 -0
  5. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.js +1 -1
  6. package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.js +9 -10
  7. package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.js +6 -1
  8. package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.js +6 -1
  9. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment-diagnostics.yml +81 -0
  10. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment.yml +55 -0
  11. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-diagnostics.yml +64 -0
  12. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check.yml +24 -5
  13. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/gitignore +1 -0
  14. package/node_modules/@ykdz/template-builtin-presets/templates/shared/devcontainer/github-cli.Dockerfile +9 -0
  15. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/src/readiness.ts +1 -1
  16. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/test/global-setup.ts +21 -0
  17. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/tsconfig.json +6 -1
  18. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/vitest.config.ts +7 -0
  19. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/drizzle.config.ts +5 -1
  20. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/playwright.config.ts +103 -22
  21. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/test/playwright-teardown.ts +12 -0
  22. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/vite.config.ts +39 -1
  23. package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/playwright.config.ts +50 -8
  24. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/playwright.config.ts +61 -8
  25. package/node_modules/@ykdz/template-core/dist/ci-diagnostic-artifact.d.ts +19 -0
  26. package/node_modules/@ykdz/template-core/dist/ci-diagnostic-artifact.js +68 -0
  27. package/node_modules/@ykdz/template-core/dist/package-contribution.d.ts +3 -0
  28. package/node_modules/@ykdz/template-core/dist/package-contribution.js +7 -0
  29. package/node_modules/@ykdz/template-core/dist/project-github.d.ts +50 -6
  30. package/node_modules/@ykdz/template-core/dist/project-github.js +83 -47
  31. package/node_modules/@ykdz/template-core/dist/template-boundary-check.d.ts +15 -1
  32. package/node_modules/@ykdz/template-core/dist/template-boundary-check.js +162 -1
  33. package/package.json +1 -1
  34. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-playwright.yml +0 -20
  35. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-rust.yml +0 -23
  36. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check.dynamic.template +0 -19
  37. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/scripts/prepare-database.ts +0 -45
  38. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/scripts/run-playwright.ts +0 -151
  39. package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/scripts/run-playwright.ts +0 -62
  40. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/scripts/run-playwright.ts +0 -103
@@ -1,6 +1,18 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { rmSync } from "node:fs";
3
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
4
+ import { createServer } from "node:net";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
1
8
  import { defineConfig, devices } from "@playwright/test";
2
9
 
3
10
  const externalBaseUrlName = "PLAYWRIGHT_EXTERNAL_BASE_URL";
11
+ const externalReadinessTimeoutMs = 30_000;
12
+ const webRoot = path.dirname(fileURLToPath(import.meta.url));
13
+ const migrationsRoot = path.resolve(webRoot, "../../packages/db-migrations");
14
+ const databaseFile = path.join(webRoot, "node_modules/.tmp/e2e.sqlite");
15
+ const stateFile = path.join(webRoot, "node_modules/.tmp/playwright-port");
4
16
 
5
17
  function externalBaseUrl(): string | undefined {
6
18
  if (!Object.hasOwn(process.env, externalBaseUrlName)) {
@@ -26,31 +38,98 @@ function externalBaseUrl(): string | undefined {
26
38
  return url.toString();
27
39
  }
28
40
 
29
- function requiredPort(name: string): number {
30
- return Number(requiredEnv(name));
41
+ async function availablePort(): Promise<string> {
42
+ return await new Promise((resolve, reject) => {
43
+ const server = createServer();
44
+
45
+ server.once("error", reject);
46
+ server.listen(0, "127.0.0.1", () => {
47
+ const address = server.address();
48
+
49
+ if (typeof address !== "object" || address === null) {
50
+ server.close(() => {
51
+ reject(new Error("Could not allocate a Playwright web port"));
52
+ });
53
+ return;
54
+ }
55
+
56
+ const port = String(address.port);
57
+ server.close((error) => {
58
+ if (error) {
59
+ reject(error);
60
+ return;
61
+ }
62
+
63
+ resolve(port);
64
+ });
65
+ });
66
+ });
31
67
  }
32
68
 
33
- function requiredEnv(name: string): string {
34
- const value = process.env[name];
69
+ async function awaitExternalService(baseUrl: string): Promise<void> {
70
+ const deadline = Date.now() + externalReadinessTimeoutMs;
35
71
 
36
- if (!value) {
37
- throw new Error(`${name} must be set by scripts/run-playwright.ts`);
72
+ while (Date.now() < deadline) {
73
+ try {
74
+ const response = await fetch(baseUrl, {
75
+ signal: AbortSignal.timeout(2_000),
76
+ });
77
+ if (response.ok) {
78
+ return;
79
+ }
80
+ } catch {
81
+ // The externally managed service may still be starting.
82
+ }
83
+
84
+ await new Promise((resolve) => setTimeout(resolve, 250));
38
85
  }
39
86
 
40
- return value;
87
+ throw new Error(
88
+ `External Playwright service at ${baseUrl} was not ready within ${externalReadinessTimeoutMs}ms`,
89
+ );
90
+ }
91
+
92
+ async function sharedPort(envName: string): Promise<{
93
+ readonly port: string;
94
+ readonly prepared: boolean;
95
+ }> {
96
+ const envPort = process.env[envName];
97
+ if (envPort !== undefined) {
98
+ return { port: envPort, prepared: false };
99
+ }
100
+ try {
101
+ return { port: await readFile(stateFile, "utf8"), prepared: false };
102
+ } catch {
103
+ const port = await availablePort();
104
+ await mkdir(path.dirname(stateFile), { recursive: true });
105
+ await writeFile(stateFile, port, "utf8");
106
+ return { port, prepared: true };
107
+ }
108
+ }
109
+
110
+ function prepareLocalDatabase(): void {
111
+ rmSync(databaseFile, { force: true });
112
+ execFileSync("pnpm", ["run", "db:push"], {
113
+ cwd: migrationsRoot,
114
+ env: { ...process.env, DATABASE_FILE: databaseFile },
115
+ stdio: "inherit",
116
+ });
41
117
  }
42
118
 
43
119
  const externalServiceUrl = externalBaseUrl();
44
- const previewPort = externalServiceUrl
45
- ? undefined
46
- : requiredPort("PLAYWRIGHT_WEB_PORT");
47
- const previewUrl = externalServiceUrl ?? `http://127.0.0.1:${previewPort}`;
48
- const databaseFile = externalServiceUrl
49
- ? undefined
50
- : requiredEnv("DATABASE_FILE");
51
-
52
- function shellValue(value: string): string {
53
- return `'${value.replaceAll("'", "'\\''")}'`;
120
+ let previewUrl: string;
121
+
122
+ if (externalServiceUrl === undefined) {
123
+ const { port, prepared } = await sharedPort("PLAYWRIGHT_WEB_PORT");
124
+ previewUrl = `http://127.0.0.1:${port}`;
125
+ process.env.DATABASE_FILE = databaseFile;
126
+ process.env.PORT = port;
127
+ if (prepared) {
128
+ prepareLocalDatabase();
129
+ }
130
+ } else {
131
+ previewUrl = externalServiceUrl;
132
+ await awaitExternalService(externalServiceUrl);
54
133
  }
55
134
 
56
135
  export default defineConfig({
@@ -60,16 +139,18 @@ export default defineConfig({
60
139
  baseURL: previewUrl,
61
140
  trace: "retain-on-failure",
62
141
  },
63
- ...(externalServiceUrl
64
- ? {}
65
- : {
142
+ reporter: [["list"], ["html"]],
143
+ globalTeardown: "./test/playwright-teardown.ts",
144
+ ...(externalServiceUrl === undefined
145
+ ? {
66
146
  webServer: {
67
- command: `DATABASE_FILE=${shellValue(databaseFile!)} pnpm --dir ../../packages/db-migrations run db:prepare:test && PORT=${previewPort} node dist/server/index.mjs`,
147
+ command: "node dist/server/index.mjs",
68
148
  reuseExistingServer: !process.env.CI,
69
149
  timeout: 60_000,
70
150
  url: previewUrl,
71
151
  },
72
- }),
152
+ }
153
+ : {}),
73
154
  projects: [
74
155
  {
75
156
  name: "chromium",
@@ -0,0 +1,12 @@
1
+ import { rmSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const webRoot = path.dirname(fileURLToPath(import.meta.url));
6
+
7
+ export default function teardown(): void {
8
+ rmSync(path.join(webRoot, "node_modules/.tmp/e2e.sqlite"), { force: true });
9
+ rmSync(path.join(webRoot, "node_modules/.tmp/playwright-port"), {
10
+ force: true,
11
+ });
12
+ }
@@ -1,12 +1,50 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
1
5
  import tailwindcss from "@tailwindcss/vite";
2
6
  import vue from "@vitejs/plugin-vue";
3
7
  import telefunc from "telefunc/vite";
4
8
  import vike from "vike/plugin";
5
9
  import { defineConfig, type PluginOption } from "vite";
6
10
 
11
+ const webRoot = path.dirname(fileURLToPath(import.meta.url));
12
+ const migrationsRoot = path.resolve(webRoot, "../../packages/db-migrations");
13
+ const databaseRoot = path.resolve(webRoot, "../../packages/db");
14
+ const databaseFile = path.join(webRoot, "data/app.sqlite");
15
+
7
16
  // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- telefunc/vite currently types its plugin factory as any.
8
17
  const telefuncPlugin = telefunc() as PluginOption;
9
18
 
19
+ async function prepareDatabase(): Promise<void> {
20
+ const env = { ...process.env, DATABASE_FILE: databaseFile };
21
+ process.env.DATABASE_FILE = databaseFile;
22
+ execFileSync("pnpm", ["run", "db:push"], {
23
+ cwd: migrationsRoot,
24
+ env,
25
+ stdio: "inherit",
26
+ });
27
+ execFileSync("pnpm", ["run", "db:seed:example"], {
28
+ cwd: databaseRoot,
29
+ env,
30
+ stdio: "inherit",
31
+ });
32
+ }
33
+
34
+ function databasePreparationPlugin(): PluginOption {
35
+ return {
36
+ name: "database-preparation",
37
+ configureServer: () => prepareDatabase(),
38
+ configurePreviewServer: () => prepareDatabase(),
39
+ };
40
+ }
41
+
10
42
  export default defineConfig({
11
- plugins: [vike(), telefuncPlugin, vue(), tailwindcss()],
43
+ plugins: [
44
+ vike(),
45
+ telefuncPlugin,
46
+ vue(),
47
+ tailwindcss(),
48
+ databasePreparationPlugin(),
49
+ ],
12
50
  });
@@ -1,24 +1,66 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { createServer } from "node:net";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
1
6
  import { defineConfig, devices } from "@playwright/test";
2
7
 
3
- function requiredPort(name: string): number {
4
- const value = process.env[name];
8
+ const webRoot = path.dirname(fileURLToPath(import.meta.url));
9
+ const stateFile = path.join(webRoot, "node_modules/.tmp/playwright-port");
5
10
 
6
- if (!value) {
7
- throw new Error(`${name} must be set by scripts/run-playwright.ts`);
8
- }
11
+ async function availablePort(): Promise<string> {
12
+ return await new Promise((resolve, reject) => {
13
+ const server = createServer();
14
+
15
+ server.once("error", reject);
16
+ server.listen(0, "127.0.0.1", () => {
17
+ const address = server.address();
18
+
19
+ if (typeof address !== "object" || address === null) {
20
+ server.close(() => {
21
+ reject(new Error("Could not allocate a Playwright web port"));
22
+ });
23
+ return;
24
+ }
25
+
26
+ const port = String(address.port);
27
+ server.close((error) => {
28
+ if (error) {
29
+ reject(error);
30
+ return;
31
+ }
9
32
 
10
- return Number(value);
33
+ resolve(port);
34
+ });
35
+ });
36
+ });
37
+ }
38
+
39
+ async function sharedPort(envName: string): Promise<string> {
40
+ const envPort = process.env[envName];
41
+ if (envPort !== undefined) {
42
+ return envPort;
43
+ }
44
+ try {
45
+ return await readFile(stateFile, "utf8");
46
+ } catch {
47
+ const port = await availablePort();
48
+ await mkdir(path.dirname(stateFile), { recursive: true });
49
+ await writeFile(stateFile, port, "utf8");
50
+ return port;
51
+ }
11
52
  }
12
53
 
13
- const previewPort = requiredPort("PLAYWRIGHT_WEB_PORT");
54
+ const previewPort = await sharedPort("PLAYWRIGHT_WEB_PORT");
14
55
  const previewUrl = `http://127.0.0.1:${previewPort}`;
15
56
 
16
57
  export default defineConfig({
17
58
  testDir: "./test/e2e",
18
59
  use: {
19
60
  baseURL: previewUrl,
20
- trace: "on-first-retry",
61
+ trace: "retain-on-failure",
21
62
  },
63
+ reporter: [["list"], ["html"]],
22
64
  webServer: {
23
65
  command: `pnpm run preview --host 127.0.0.1 --port ${previewPort}`,
24
66
  reuseExistingServer: !process.env.CI,
@@ -1,17 +1,68 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { createServer } from "node:net";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
1
6
  import { defineConfig, devices } from "@playwright/test";
2
7
 
3
- function requiredPort(name: string): number {
4
- const value = process.env[name];
8
+ const webRoot = path.dirname(fileURLToPath(import.meta.url));
9
+ const stateFile = path.join(webRoot, "node_modules/.tmp/playwright-ports");
5
10
 
6
- if (!value) {
7
- throw new Error(`${name} must be set by scripts/run-playwright.ts`);
8
- }
11
+ async function availablePort(): Promise<string> {
12
+ return await new Promise((resolve, reject) => {
13
+ const server = createServer();
14
+
15
+ server.once("error", reject);
16
+ server.listen(0, "127.0.0.1", () => {
17
+ const address = server.address();
18
+
19
+ if (typeof address !== "object" || address === null) {
20
+ server.close(() => {
21
+ reject(new Error("Could not allocate a Playwright web port"));
22
+ });
23
+ return;
24
+ }
25
+
26
+ const port = String(address.port);
27
+ server.close((error) => {
28
+ if (error) {
29
+ reject(error);
30
+ return;
31
+ }
9
32
 
10
- return Number(value);
33
+ resolve(port);
34
+ });
35
+ });
36
+ });
37
+ }
38
+
39
+ async function sharedPorts(): Promise<{
40
+ readonly api: string;
41
+ readonly web: string;
42
+ }> {
43
+ const envApi = process.env.PLAYWRIGHT_API_PORT;
44
+ const envWeb = process.env.PLAYWRIGHT_WEB_PORT;
45
+ if (envApi !== undefined && envWeb !== undefined) {
46
+ return { api: envApi, web: envWeb };
47
+ }
48
+ try {
49
+ const parsed = JSON.parse(await readFile(stateFile, "utf8")) as {
50
+ readonly api: string;
51
+ readonly web: string;
52
+ };
53
+ return parsed;
54
+ } catch {
55
+ const ports = {
56
+ api: envApi ?? (await availablePort()),
57
+ web: envWeb ?? (await availablePort()),
58
+ };
59
+ await mkdir(path.dirname(stateFile), { recursive: true });
60
+ await writeFile(stateFile, JSON.stringify(ports), "utf8");
61
+ return ports;
62
+ }
11
63
  }
12
64
 
13
- const apiPort = requiredPort("PLAYWRIGHT_API_PORT");
14
- const webPort = requiredPort("PLAYWRIGHT_WEB_PORT");
65
+ const { api: apiPort, web: webPort } = await sharedPorts();
15
66
  const apiBaseUrl = `http://127.0.0.1:${apiPort}`;
16
67
  const apiHealthUrl = `${apiBaseUrl}/api/health`;
17
68
  const webBaseUrl = `http://127.0.0.1:${webPort}`;
@@ -20,8 +71,10 @@ export default defineConfig({
20
71
  testDir: "./test/e2e",
21
72
  use: {
22
73
  baseURL: webBaseUrl,
74
+ trace: "retain-on-failure",
23
75
  ...devices["Desktop Chrome"],
24
76
  },
77
+ reporter: [["list"], ["html"]],
25
78
  webServer: [
26
79
  {
27
80
  command: `PORT=${apiPort} pnpm --dir ../api run start`,
@@ -0,0 +1,19 @@
1
+ import type { PackageBoundaryOwner } from "./module-graph.ts";
2
+ /** Closed, owner-scoped native evidence a Package Contribution may retain in CI. */
3
+ export type CiDiagnosticArtifactDeclaration = {
4
+ readonly kind: "playwright";
5
+ readonly owner: PackageBoundaryOwner;
6
+ };
7
+ /**
8
+ * Validates the intentionally closed diagnostic declaration surface. It does
9
+ * not accept paths: native paths are derived only after the owner is known.
10
+ */
11
+ export declare function assertCiDiagnosticArtifactDeclaration(value: unknown): CiDiagnosticArtifactDeclaration;
12
+ /**
13
+ * Composes closed native diagnostics independently of presets and environment
14
+ * preparation. Equivalent declarations collapse into stable owner order.
15
+ */
16
+ export declare function composeCiDiagnosticArtifacts(options: {
17
+ readonly packagePaths: readonly string[];
18
+ readonly declarations: readonly unknown[];
19
+ }): readonly CiDiagnosticArtifactDeclaration[];
@@ -0,0 +1,68 @@
1
+ function isRecord(value) {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ }
4
+ function hasExactKeys(value, keys) {
5
+ const actual = Object.keys(value).toSorted();
6
+ const expected = [...keys].toSorted();
7
+ return (actual.length === expected.length &&
8
+ actual.every((key, index) => key === expected[index]));
9
+ }
10
+ const reservedWorkspaceCollections = new Set([
11
+ ".git",
12
+ ".github",
13
+ ".devcontainer",
14
+ ".template",
15
+ "node_modules",
16
+ "dist",
17
+ "target",
18
+ ]);
19
+ function isSafePackagePath(value) {
20
+ return (/^[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/u.test(value) &&
21
+ !reservedWorkspaceCollections.has(value.split("/", 1)[0]));
22
+ }
23
+ /**
24
+ * Validates the intentionally closed diagnostic declaration surface. It does
25
+ * not accept paths: native paths are derived only after the owner is known.
26
+ */
27
+ export function assertCiDiagnosticArtifactDeclaration(value) {
28
+ if (!isRecord(value) || !hasExactKeys(value, ["kind", "owner"])) {
29
+ throw new Error("CI Diagnostic Artifact declarations may contain only kind and owner");
30
+ }
31
+ if (value.kind !== "playwright") {
32
+ throw new Error("Unsupported CI Diagnostic Artifact kind");
33
+ }
34
+ if (!isRecord(value.owner) || !hasExactKeys(value.owner, ["kind", "path"])) {
35
+ throw new Error("CI Diagnostic Artifact requires a Package Boundary owner");
36
+ }
37
+ if (value.owner.kind !== "package-boundary" ||
38
+ typeof value.owner.path !== "string" ||
39
+ !isSafePackagePath(value.owner.path)) {
40
+ throw new Error("CI Diagnostic Artifact owner has an unsafe Package Boundary path");
41
+ }
42
+ return {
43
+ kind: "playwright",
44
+ owner: { kind: "package-boundary", path: value.owner.path },
45
+ };
46
+ }
47
+ /**
48
+ * Composes closed native diagnostics independently of presets and environment
49
+ * preparation. Equivalent declarations collapse into stable owner order.
50
+ */
51
+ export function composeCiDiagnosticArtifacts(options) {
52
+ for (const packagePath of options.packagePaths) {
53
+ if (!isSafePackagePath(packagePath)) {
54
+ throw new Error(`CI Diagnostic Artifact Package Boundary path is unsafe: ${packagePath}`);
55
+ }
56
+ }
57
+ const owners = new Set(options.packagePaths);
58
+ const artifacts = new Map();
59
+ for (const value of options.declarations) {
60
+ const declaration = assertCiDiagnosticArtifactDeclaration(value);
61
+ if (!owners.has(declaration.owner.path)) {
62
+ throw new Error(`CI Diagnostic Artifact owner is not a declared Package Boundary: ${declaration.owner.path}`);
63
+ }
64
+ const key = `${declaration.kind}:${declaration.owner.path}`;
65
+ artifacts.set(key, declaration);
66
+ }
67
+ return [...artifacts.values()].toSorted((left, right) => left.owner.path.localeCompare(right.owner.path));
68
+ }
@@ -1,3 +1,4 @@
1
+ import { type CiDiagnosticArtifactDeclaration } from "./ci-diagnostic-artifact.ts";
1
2
  import type { DevelopmentContainerToolLayer } from "./development-container-tool-layer.ts";
2
3
  import type { EditorCustomizationCapability } from "./editor-customization.ts";
3
4
  import type { CheckEnvironmentNeed, DeploymentEnvironmentNeed } from "./module-graph.ts";
@@ -43,6 +44,8 @@ export type PackageContribution = {
43
44
  /** Typed requirements consumed by the Foundation for coordinated root outputs. */
44
45
  readonly foundation: FoundationContribution;
45
46
  readonly environmentNeeds: readonly CheckEnvironmentNeed[];
47
+ /** Closed native CI evidence owned by this Package Boundary. */
48
+ readonly ciDiagnosticArtifacts?: readonly CiDiagnosticArtifactDeclaration[];
46
49
  /** Requirements prepared only by a focused deployment entrypoint. */
47
50
  readonly deploymentEnvironmentNeeds?: readonly DeploymentEnvironmentNeed[];
48
51
  };
@@ -1,3 +1,4 @@
1
+ import { assertCiDiagnosticArtifactDeclaration, } from "./ci-diagnostic-artifact.js";
1
2
  const operationPath = (operation) => {
2
3
  if ("to" in operation)
3
4
  return operation.to;
@@ -20,5 +21,11 @@ export function assertPackageContribution(contribution, provenance = {}) {
20
21
  : `${provenance.definitionName}: ${provenance.planner ?? "Package Contribution"} `;
21
22
  throw new Error(`${owner}${rule}; ${contribution.definition.path} attempted ${target}`);
22
23
  }
24
+ for (const artifact of contribution.ciDiagnosticArtifacts ?? []) {
25
+ const declaration = assertCiDiagnosticArtifactDeclaration(artifact);
26
+ if (declaration.owner.path !== contribution.definition.path) {
27
+ throw new Error(`CI Diagnostic Artifact owner must match its Package Contribution: ${contribution.definition.path}`);
28
+ }
29
+ }
23
30
  return contribution;
24
31
  }
@@ -1,16 +1,52 @@
1
+ import { type CiDiagnosticArtifactDeclaration } from "./ci-diagnostic-artifact.ts";
1
2
  import type { DeploymentEnvironmentNeed } from "./module-graph.ts";
2
3
  export type CiCapability = {
3
4
  readonly workflowName: "Check";
4
5
  readonly jobName: "check";
6
+ readonly jobDisplayName: "Root Check";
5
7
  readonly runner: "ubuntu-latest";
8
+ readonly timeoutMinutes: 30;
6
9
  };
7
10
  export type CiEnvironmentPreparation = {
8
11
  readonly nodeFromPackageMetadata: boolean;
12
+ readonly packageManagerFromPackageMetadata: boolean;
13
+ readonly pnpmStoreCache: boolean;
9
14
  };
10
15
  export type PnpmTaskLayer = {
11
- readonly installCommand: "pnpm install";
16
+ readonly installCommand: "pnpm install --frozen-lockfile";
12
17
  readonly checkCommand: "pnpm run check";
13
18
  };
19
+ export type CiCheckMatrixEntry = {
20
+ readonly capability: "root" | "deployment";
21
+ readonly jobDisplayName: "Root Check" | "Deployment Check";
22
+ readonly taskEntrypoint: "pnpm run check" | "pnpm run check:deployment";
23
+ readonly timeoutMinutes: 30 | 45;
24
+ readonly requiresDocker: boolean;
25
+ };
26
+ export type ProjectCheckWorkflowPlan = {
27
+ readonly workflowName: "Check";
28
+ readonly triggers: {
29
+ readonly pullRequest: true;
30
+ readonly pushBranches: readonly ["main"];
31
+ };
32
+ readonly permissions: {
33
+ readonly contents: "read";
34
+ };
35
+ readonly concurrency: {
36
+ readonly group: "${{ github.workflow }}-${{ github.ref }}";
37
+ readonly cancelInProgress: true;
38
+ };
39
+ readonly rootCheck: CiCapability;
40
+ readonly matrix: {
41
+ readonly failFast: false;
42
+ readonly include: readonly CiCheckMatrixEntry[];
43
+ } | undefined;
44
+ readonly environmentPreparation: CiEnvironmentPreparation;
45
+ readonly taskLayer: PnpmTaskLayer;
46
+ /** Actual Package Boundaries allowed to own retained native diagnostics. */
47
+ readonly packagePaths: readonly string[];
48
+ readonly diagnosticArtifacts: readonly CiDiagnosticArtifactDeclaration[];
49
+ };
14
50
  export type DependencyEcosystem = "npm" | "cargo" | "github-actions" | "docker" | "rust-toolchain";
15
51
  export type DependabotDirectory = `/${string}`;
16
52
  export type DependencyMaintenancePolicy = {
@@ -19,19 +55,27 @@ export type DependencyMaintenancePolicy = {
19
55
  readonly extraDirectories?: Partial<Record<DependencyEcosystem, readonly DependabotDirectory[]>>;
20
56
  readonly interval: "weekly";
21
57
  };
22
- type ProjectCheckWorkflowOptions = {
58
+ export type ProjectCheckWorkflowDiagnosticOptions = {
59
+ /** Package Paths from the real Project Blueprint or Package Contributions. */
60
+ readonly packagePaths: readonly string[];
61
+ readonly diagnosticArtifacts?: readonly unknown[] | undefined;
62
+ };
63
+ type ProjectCheckWorkflowOptions = ProjectCheckWorkflowDiagnosticOptions & {
23
64
  readonly deploymentEnvironmentNeeds?: readonly DeploymentEnvironmentNeed[];
24
65
  readonly hasDeploymentTask?: boolean | undefined;
25
66
  readonly capability?: CiCapability | undefined;
26
67
  readonly environmentPreparation?: Partial<CiEnvironmentPreparation> | undefined;
27
68
  readonly taskLayer?: PnpmTaskLayer | undefined;
28
69
  };
29
- export declare function projectCheckWorkflow(options: ProjectCheckWorkflowOptions): string;
30
- /** Limited substitutions for the Foundation-owned workflow Template Source. */
31
- export declare function projectCheckWorkflowTemplateReplacements(options: {
70
+ /** Capability facts for the Foundation-owned GitHub Actions Template Source. */
71
+ export declare function projectCheckWorkflowPlan(options: ProjectCheckWorkflowOptions): ProjectCheckWorkflowPlan;
72
+ /** Selects a complete Foundation-owned workflow Template Source. */
73
+ export declare function projectCheckWorkflowTemplateSource(options: ProjectCheckWorkflowDiagnosticOptions & {
32
74
  readonly deploymentEnvironmentNeeds?: readonly DeploymentEnvironmentNeed[];
33
75
  readonly hasDeploymentTask?: boolean | undefined;
34
- }): Record<string, string>;
76
+ }): ".github/workflows/check.yml" | ".github/workflows/check-diagnostics.yml" | ".github/workflows/check-deployment.yml" | ".github/workflows/check-deployment-diagnostics.yml";
77
+ /** Limited, validated facts for static Foundation diagnostic staging. */
78
+ export declare function projectCheckWorkflowTemplateReplacements(options: ProjectCheckWorkflowDiagnosticOptions): Record<string, string>;
35
79
  export declare function projectDependabotConfig(policy: DependencyMaintenancePolicy): string;
36
80
  /** Limited substitution for the Foundation-owned Dependabot Template Source. */
37
81
  export declare function projectDependabotTemplateReplacements(policy: DependencyMaintenancePolicy): Record<string, string>;