@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.
- package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.d.ts +2 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.js +40 -14
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.d.ts +1 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.js +17 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.js +1 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.js +9 -10
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.js +6 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.js +6 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment-diagnostics.yml +81 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment.yml +55 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-diagnostics.yml +64 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check.yml +24 -5
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/gitignore +1 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/devcontainer/github-cli.Dockerfile +9 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/src/readiness.ts +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/test/global-setup.ts +21 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/tsconfig.json +6 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/vitest.config.ts +7 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/drizzle.config.ts +5 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/playwright.config.ts +103 -22
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/test/playwright-teardown.ts +12 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/vite.config.ts +39 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/playwright.config.ts +50 -8
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/playwright.config.ts +61 -8
- package/node_modules/@ykdz/template-core/dist/ci-diagnostic-artifact.d.ts +19 -0
- package/node_modules/@ykdz/template-core/dist/ci-diagnostic-artifact.js +68 -0
- package/node_modules/@ykdz/template-core/dist/package-contribution.d.ts +3 -0
- package/node_modules/@ykdz/template-core/dist/package-contribution.js +7 -0
- package/node_modules/@ykdz/template-core/dist/project-github.d.ts +50 -6
- package/node_modules/@ykdz/template-core/dist/project-github.js +83 -47
- package/node_modules/@ykdz/template-core/dist/template-boundary-check.d.ts +15 -1
- package/node_modules/@ykdz/template-core/dist/template-boundary-check.js +162 -1
- package/package.json +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-playwright.yml +0 -20
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-rust.yml +0 -23
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check.dynamic.template +0 -19
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/scripts/prepare-database.ts +0 -45
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/scripts/run-playwright.ts +0 -151
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/scripts/run-playwright.ts +0 -62
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/scripts/run-playwright.ts +0 -103
|
@@ -1,73 +1,109 @@
|
|
|
1
|
+
import { composeCiDiagnosticArtifacts, } from "./ci-diagnostic-artifact.js";
|
|
2
|
+
function validatedDiagnosticArtifacts(options) {
|
|
3
|
+
return composeCiDiagnosticArtifacts({
|
|
4
|
+
packagePaths: options.packagePaths,
|
|
5
|
+
declarations: options.artifacts,
|
|
6
|
+
});
|
|
7
|
+
}
|
|
1
8
|
const defaultCiCapability = {
|
|
2
9
|
workflowName: "Check",
|
|
3
10
|
jobName: "check",
|
|
11
|
+
jobDisplayName: "Root Check",
|
|
4
12
|
runner: "ubuntu-latest",
|
|
13
|
+
timeoutMinutes: 30,
|
|
5
14
|
};
|
|
6
15
|
const pnpmTaskLayer = {
|
|
7
|
-
installCommand: "pnpm install",
|
|
16
|
+
installCommand: "pnpm install --frozen-lockfile",
|
|
8
17
|
checkCommand: "pnpm run check",
|
|
9
18
|
};
|
|
10
|
-
|
|
19
|
+
/** Capability facts for the Foundation-owned GitHub Actions Template Source. */
|
|
20
|
+
export function projectCheckWorkflowPlan(options) {
|
|
11
21
|
const capability = options.capability ?? defaultCiCapability;
|
|
12
22
|
const environmentPreparation = {
|
|
13
23
|
nodeFromPackageMetadata: true,
|
|
24
|
+
packageManagerFromPackageMetadata: true,
|
|
25
|
+
pnpmStoreCache: true,
|
|
14
26
|
...options.environmentPreparation,
|
|
15
27
|
};
|
|
16
28
|
const taskLayer = options.taskLayer ?? pnpmTaskLayer;
|
|
17
29
|
const hasDeploymentTask = options.hasDeploymentTask === true;
|
|
18
30
|
const needsDocker = (options.deploymentEnvironmentNeeds ?? []).some((need) => need.kind === "docker-engine");
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"",
|
|
22
|
-
"on:",
|
|
23
|
-
" pull_request:",
|
|
24
|
-
" push:",
|
|
25
|
-
" branches:",
|
|
26
|
-
" - main",
|
|
27
|
-
"",
|
|
28
|
-
"jobs:",
|
|
29
|
-
` ${capability.jobName}:`,
|
|
30
|
-
` runs-on: ${capability.runner}`,
|
|
31
|
-
...(hasDeploymentTask
|
|
32
|
-
? [" strategy:", " matrix:", " check: [root, deployment]"]
|
|
33
|
-
: []),
|
|
34
|
-
" steps:",
|
|
35
|
-
" - uses: actions/checkout@v7",
|
|
36
|
-
];
|
|
37
|
-
if (environmentPreparation.nodeFromPackageMetadata) {
|
|
38
|
-
lines.push(" - uses: actions/setup-node@v7", " with:", " node-version-file: package.json", " - run: corepack enable");
|
|
39
|
-
}
|
|
40
|
-
if (hasDeploymentTask && needsDocker) {
|
|
41
|
-
lines.push(" - uses: docker/setup-buildx-action@v3", " if: matrix.check == 'deployment'");
|
|
31
|
+
if (hasDeploymentTask && !needsDocker) {
|
|
32
|
+
throw new Error("Deployment Check requires a Docker environment need");
|
|
42
33
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
const packagePaths = [...options.packagePaths];
|
|
35
|
+
const diagnosticArtifacts = validatedDiagnosticArtifacts({
|
|
36
|
+
packagePaths,
|
|
37
|
+
artifacts: options.diagnosticArtifacts ?? [],
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
workflowName: capability.workflowName,
|
|
41
|
+
triggers: { pullRequest: true, pushBranches: ["main"] },
|
|
42
|
+
permissions: { contents: "read" },
|
|
43
|
+
concurrency: {
|
|
44
|
+
group: "${{ github.workflow }}-${{ github.ref }}",
|
|
45
|
+
cancelInProgress: true,
|
|
46
|
+
},
|
|
47
|
+
rootCheck: capability,
|
|
48
|
+
matrix: hasDeploymentTask
|
|
49
|
+
? {
|
|
50
|
+
failFast: false,
|
|
51
|
+
include: [
|
|
52
|
+
{
|
|
53
|
+
capability: "root",
|
|
54
|
+
jobDisplayName: capability.jobDisplayName,
|
|
55
|
+
taskEntrypoint: taskLayer.checkCommand,
|
|
56
|
+
timeoutMinutes: capability.timeoutMinutes,
|
|
57
|
+
requiresDocker: false,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
capability: "deployment",
|
|
61
|
+
jobDisplayName: "Deployment Check",
|
|
62
|
+
taskEntrypoint: "pnpm run check:deployment",
|
|
63
|
+
timeoutMinutes: 45,
|
|
64
|
+
requiresDocker: true,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
}
|
|
68
|
+
: undefined,
|
|
69
|
+
environmentPreparation,
|
|
70
|
+
taskLayer,
|
|
71
|
+
packagePaths,
|
|
72
|
+
diagnosticArtifacts,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** Selects a complete Foundation-owned workflow Template Source. */
|
|
76
|
+
export function projectCheckWorkflowTemplateSource(options) {
|
|
77
|
+
const hasDeploymentTask = options.hasDeploymentTask === true;
|
|
78
|
+
const needsDocker = (options.deploymentEnvironmentNeeds ?? []).some((need) => need.kind === "docker-engine");
|
|
79
|
+
if (hasDeploymentTask && !needsDocker) {
|
|
80
|
+
throw new Error("Deployment Check requires a Docker environment need");
|
|
47
81
|
}
|
|
82
|
+
const hasDiagnostics = validatedDiagnosticArtifacts({
|
|
83
|
+
packagePaths: options.packagePaths,
|
|
84
|
+
artifacts: options.diagnosticArtifacts ?? [],
|
|
85
|
+
}).length > 0;
|
|
48
86
|
if (hasDeploymentTask) {
|
|
49
|
-
|
|
87
|
+
return hasDiagnostics
|
|
88
|
+
? ".github/workflows/check-deployment-diagnostics.yml"
|
|
89
|
+
: ".github/workflows/check-deployment.yml";
|
|
50
90
|
}
|
|
51
|
-
|
|
52
|
-
|
|
91
|
+
return hasDiagnostics
|
|
92
|
+
? ".github/workflows/check-diagnostics.yml"
|
|
93
|
+
: ".github/workflows/check.yml";
|
|
53
94
|
}
|
|
54
|
-
/** Limited
|
|
95
|
+
/** Limited, validated facts for static Foundation diagnostic staging. */
|
|
55
96
|
export function projectCheckWorkflowTemplateReplacements(options) {
|
|
56
|
-
const
|
|
57
|
-
|
|
97
|
+
const declarations = validatedDiagnosticArtifacts({
|
|
98
|
+
packagePaths: options.packagePaths,
|
|
99
|
+
artifacts: options.diagnosticArtifacts ?? [],
|
|
100
|
+
});
|
|
101
|
+
if (declarations.length === 0)
|
|
102
|
+
return {};
|
|
58
103
|
return {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
DEPLOYMENT_DOCKER_PREPARATION: hasDeploymentTask && needsDocker
|
|
63
|
-
? "\n - uses: docker/setup-buildx-action@v3\n if: matrix.check == 'deployment'"
|
|
64
|
-
: "",
|
|
65
|
-
ROOT_CHECK_CONDITION: hasDeploymentTask
|
|
66
|
-
? "\n if: matrix.check == 'root'"
|
|
67
|
-
: "",
|
|
68
|
-
DEPLOYMENT_CHECK: hasDeploymentTask
|
|
69
|
-
? "\n - run: pnpm run check:deployment\n if: matrix.check == 'deployment'"
|
|
70
|
-
: "",
|
|
104
|
+
DIAGNOSTIC_OWNER_PATHS: declarations
|
|
105
|
+
.map((declaration) => declaration.owner.path)
|
|
106
|
+
.join("\n "),
|
|
71
107
|
};
|
|
72
108
|
}
|
|
73
109
|
export function projectDependabotConfig(policy) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ProjectBlueprintV2 } from "./project-blueprint-v2.ts";
|
|
1
2
|
import { type RenderOperation } from "./renderer.ts";
|
|
2
3
|
export type TemplateBoundaryViolation = {
|
|
3
4
|
readonly preset: string;
|
|
@@ -34,6 +35,19 @@ export type CheckTemplateSourceBoundaryOptions = {
|
|
|
34
35
|
readonly projections: readonly TemplateBoundaryCheckProjection[];
|
|
35
36
|
readonly templateSourceContexts?: readonly TemplateSourceContext[];
|
|
36
37
|
readonly manifestReferencedSourceFiles?: readonly string[];
|
|
38
|
+
/** Complete Built-in plans whose Foundation workflow is a protected output. */
|
|
39
|
+
readonly protectedWorkflowPlans?: readonly {
|
|
40
|
+
readonly name: string;
|
|
41
|
+
readonly sourceFilePath: string;
|
|
42
|
+
readonly definitionName?: string;
|
|
43
|
+
readonly planningContribution?: string;
|
|
44
|
+
readonly generatedPath: ".github/workflows/check.yml";
|
|
45
|
+
/** The original Blueprint, before Foundation composes workflow facts. */
|
|
46
|
+
readonly blueprint: ProjectBlueprintV2;
|
|
47
|
+
/** Raw Package Contribution declarations, not the composed plan field. */
|
|
48
|
+
readonly diagnosticArtifactDeclarations: readonly unknown[];
|
|
49
|
+
readonly operations: readonly RenderOperation[];
|
|
50
|
+
}[];
|
|
37
51
|
};
|
|
38
52
|
/**
|
|
39
53
|
* Reads each registered Template Source context before any plan is inspected.
|
|
@@ -41,4 +55,4 @@ export type CheckTemplateSourceBoundaryOptions = {
|
|
|
41
55
|
*/
|
|
42
56
|
export declare function checkTemplateSourceContexts(contexts: readonly Omit<TemplateSourceContext, "checkedFiles">[]): Promise<readonly TemplateSourceContext[]>;
|
|
43
57
|
export declare function isProtectedGeneratedPath(generatedPath: string): boolean;
|
|
44
|
-
export declare function checkTemplateSourceBoundary({ projections, templateSourceContexts, manifestReferencedSourceFiles, }: CheckTemplateSourceBoundaryOptions): Promise<TemplateBoundaryCheckResult>;
|
|
58
|
+
export declare function checkTemplateSourceBoundary({ projections, templateSourceContexts, manifestReferencedSourceFiles, protectedWorkflowPlans, }: CheckTemplateSourceBoundaryOptions): Promise<TemplateBoundaryCheckResult>;
|
|
@@ -589,9 +589,170 @@ function pushSourceBackedOperationViolations(options) {
|
|
|
589
589
|
});
|
|
590
590
|
}
|
|
591
591
|
}
|
|
592
|
-
|
|
592
|
+
const workflowTemplateVariants = new Map([
|
|
593
|
+
[
|
|
594
|
+
".github/workflows/check.yml",
|
|
595
|
+
{ operationKind: "copyFile", replacementKeys: [] },
|
|
596
|
+
],
|
|
597
|
+
[
|
|
598
|
+
".github/workflows/check-deployment.yml",
|
|
599
|
+
{ operationKind: "copyFile", replacementKeys: [] },
|
|
600
|
+
],
|
|
601
|
+
[
|
|
602
|
+
".github/workflows/check-diagnostics.yml",
|
|
603
|
+
{
|
|
604
|
+
operationKind: "writeTextTemplate",
|
|
605
|
+
replacementKeys: ["DIAGNOSTIC_OWNER_PATHS"],
|
|
606
|
+
},
|
|
607
|
+
],
|
|
608
|
+
[
|
|
609
|
+
".github/workflows/check-deployment-diagnostics.yml",
|
|
610
|
+
{
|
|
611
|
+
operationKind: "writeTextTemplate",
|
|
612
|
+
replacementKeys: ["DIAGNOSTIC_OWNER_PATHS"],
|
|
613
|
+
},
|
|
614
|
+
],
|
|
615
|
+
]);
|
|
616
|
+
function hasExactReplacementKeys(replacements, expectedKeys) {
|
|
617
|
+
const keys = Object.keys(replacements).toSorted();
|
|
618
|
+
const expected = [...expectedKeys].toSorted();
|
|
619
|
+
return (keys.length === expected.length &&
|
|
620
|
+
keys.every((key, index) => key === expected[index]));
|
|
621
|
+
}
|
|
622
|
+
const reservedWorkspaceCollections = new Set([
|
|
623
|
+
".git",
|
|
624
|
+
".github",
|
|
625
|
+
".devcontainer",
|
|
626
|
+
".template",
|
|
627
|
+
"node_modules",
|
|
628
|
+
"dist",
|
|
629
|
+
"target",
|
|
630
|
+
]);
|
|
631
|
+
function isSafePackageBoundaryPath(value) {
|
|
632
|
+
return (/^[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/u.test(value) &&
|
|
633
|
+
!reservedWorkspaceCollections.has(value.split("/", 1)[0]));
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Independent oracle for the Foundation workflow's limited substitution.
|
|
637
|
+
* It deliberately consumes raw Blueprint and Package Contribution facts rather
|
|
638
|
+
* than the production CI artifact composer or its composed plan output.
|
|
639
|
+
*/
|
|
640
|
+
function expectedDiagnosticOwnerPaths(options) {
|
|
641
|
+
const blueprintPackagePaths = options.blueprint.packages.map((packageDefinition) => packageDefinition.path);
|
|
642
|
+
if (!blueprintPackagePaths.every(isSafePackageBoundaryPath)) {
|
|
643
|
+
return { valid: false, value: "" };
|
|
644
|
+
}
|
|
645
|
+
const blueprintOwners = new Set(blueprintPackagePaths);
|
|
646
|
+
const owners = new Set();
|
|
647
|
+
for (const declaration of options.diagnosticArtifactDeclarations) {
|
|
648
|
+
if (!isRecord(declaration) ||
|
|
649
|
+
!hasExactKeys(declaration, ["kind", "owner"])) {
|
|
650
|
+
return { valid: false, value: "" };
|
|
651
|
+
}
|
|
652
|
+
if (declaration.kind !== "playwright" || !isRecord(declaration.owner)) {
|
|
653
|
+
return { valid: false, value: "" };
|
|
654
|
+
}
|
|
655
|
+
if (!hasExactKeys(declaration.owner, ["kind", "path"])) {
|
|
656
|
+
return { valid: false, value: "" };
|
|
657
|
+
}
|
|
658
|
+
const ownerPath = declaration.owner.path;
|
|
659
|
+
if (declaration.owner.kind !== "package-boundary" ||
|
|
660
|
+
typeof ownerPath !== "string" ||
|
|
661
|
+
!isSafePackageBoundaryPath(ownerPath) ||
|
|
662
|
+
!blueprintOwners.has(ownerPath)) {
|
|
663
|
+
return { valid: false, value: "" };
|
|
664
|
+
}
|
|
665
|
+
owners.add(ownerPath);
|
|
666
|
+
}
|
|
667
|
+
return {
|
|
668
|
+
valid: true,
|
|
669
|
+
value: [...owners].toSorted().join("\n "),
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
function sourceFileForWorkflowOperation(options) {
|
|
673
|
+
return options.operation?.provenance?.plannerSourceFile ?? options.fallback;
|
|
674
|
+
}
|
|
675
|
+
function findWorkflowOperationOwners(sourceFile, operation) {
|
|
676
|
+
if (operation === undefined)
|
|
677
|
+
return ["<unknown>"];
|
|
678
|
+
const targetPath = operationTarget(operation);
|
|
679
|
+
if (targetPath === undefined)
|
|
680
|
+
return ["<unknown>"];
|
|
681
|
+
const expectedTargetPath = targetPath;
|
|
682
|
+
const operationKind = operation.kind;
|
|
683
|
+
const owners = new Set();
|
|
684
|
+
function visit(node) {
|
|
685
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
686
|
+
if (propertyStringValue(node, "kind") === operationKind &&
|
|
687
|
+
propertyPathMatches(node, "to", expectedTargetPath)) {
|
|
688
|
+
owners.add(owningFunctionName(node));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
ts.forEachChild(node, visit);
|
|
692
|
+
}
|
|
693
|
+
visit(sourceFile);
|
|
694
|
+
return owners.size === 0 ? ["<unknown>"] : [...owners];
|
|
695
|
+
}
|
|
696
|
+
async function protectedWorkflowProjectionViolations(plans) {
|
|
697
|
+
const violations = [];
|
|
698
|
+
for (const plan of plans) {
|
|
699
|
+
const matchingOperations = plan.operations.filter((operation) => operationTarget(operation) === plan.generatedPath);
|
|
700
|
+
const operation = matchingOperations[0];
|
|
701
|
+
const sourceFilePath = sourceFileForWorkflowOperation({
|
|
702
|
+
operation,
|
|
703
|
+
fallback: plan.sourceFilePath,
|
|
704
|
+
});
|
|
705
|
+
const sourceText = await readFile(sourceFilePath, "utf8");
|
|
706
|
+
const sourceFile = ts.createSourceFile(sourceFilePath, sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
707
|
+
const expectedDiagnosticOwners = expectedDiagnosticOwnerPaths(plan);
|
|
708
|
+
const variant = operation !== undefined &&
|
|
709
|
+
(operation.kind === "copyFile" || operation.kind === "writeTextTemplate")
|
|
710
|
+
? workflowTemplateVariants.get(operation.from)
|
|
711
|
+
: undefined;
|
|
712
|
+
const replacementValid = operation?.kind === "writeTextTemplate" &&
|
|
713
|
+
hasExactReplacementKeys(operation.replacements, [
|
|
714
|
+
"DIAGNOSTIC_OWNER_PATHS",
|
|
715
|
+
]) &&
|
|
716
|
+
operation.replacements.DIAGNOSTIC_OWNER_PATHS ===
|
|
717
|
+
expectedDiagnosticOwners.value;
|
|
718
|
+
const sourceBackedExactlyOnce = matchingOperations.length === 1 &&
|
|
719
|
+
operation !== undefined &&
|
|
720
|
+
(operation.kind === "copyFile" || operation.kind === "writeTextTemplate");
|
|
721
|
+
const diagnosticsExpected = expectedDiagnosticOwners.value !== "";
|
|
722
|
+
const operationValid = expectedDiagnosticOwners.valid &&
|
|
723
|
+
sourceBackedExactlyOnce &&
|
|
724
|
+
variant !== undefined &&
|
|
725
|
+
operation?.kind === variant.operationKind &&
|
|
726
|
+
(operation.kind === "copyFile"
|
|
727
|
+
? variant.replacementKeys.length === 0 && !diagnosticsExpected
|
|
728
|
+
: variant.replacementKeys.length === 1 &&
|
|
729
|
+
diagnosticsExpected &&
|
|
730
|
+
replacementValid);
|
|
731
|
+
if (operationValid)
|
|
732
|
+
continue;
|
|
733
|
+
for (const owningFunction of findWorkflowOperationOwners(sourceFile, operation)) {
|
|
734
|
+
violations.push({
|
|
735
|
+
preset: plan.name,
|
|
736
|
+
generatedPath: plan.generatedPath,
|
|
737
|
+
owningFunction,
|
|
738
|
+
operationKind: operation?.kind ?? "writeTextTemplate",
|
|
739
|
+
sourceFilePath,
|
|
740
|
+
...(plan.definitionName === undefined
|
|
741
|
+
? {}
|
|
742
|
+
: { definitionName: plan.definitionName }),
|
|
743
|
+
...(plan.planningContribution === undefined
|
|
744
|
+
? {}
|
|
745
|
+
: { planningContribution: plan.planningContribution }),
|
|
746
|
+
ownershipRule: "Foundation workflow projections require exactly one source-backed template operation with closed diagnostic owner facts",
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return violations;
|
|
751
|
+
}
|
|
752
|
+
export async function checkTemplateSourceBoundary({ projections, templateSourceContexts = [], manifestReferencedSourceFiles = [], protectedWorkflowPlans = [], }) {
|
|
593
753
|
const manifestReferencedSourceFileSet = new Set(manifestReferencedSourceFiles.map((sourceFile) => path.resolve(sourceFile)));
|
|
594
754
|
const violations = [];
|
|
755
|
+
violations.push(...(await protectedWorkflowProjectionViolations(protectedWorkflowPlans)));
|
|
595
756
|
for (const projection of projections) {
|
|
596
757
|
const sourceText = await readFile(projection.sourceFilePath, "utf8");
|
|
597
758
|
const sourceFile = ts.createSourceFile(projection.sourceFilePath, sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
package/package.json
CHANGED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
name: Check
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
push:
|
|
6
|
-
branches:
|
|
7
|
-
- main
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
check:
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
steps:
|
|
13
|
-
- uses: actions/checkout@v7
|
|
14
|
-
- uses: actions/setup-node@v7
|
|
15
|
-
with:
|
|
16
|
-
node-version-file: package.json
|
|
17
|
-
- run: corepack enable
|
|
18
|
-
- run: pnpm install
|
|
19
|
-
- run: pnpm --filter ./apps/web exec playwright install --with-deps chromium
|
|
20
|
-
- run: pnpm run check
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
name: Check
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
push:
|
|
6
|
-
branches:
|
|
7
|
-
- main
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
check:
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
steps:
|
|
13
|
-
- uses: actions/checkout@v7
|
|
14
|
-
- uses: actions/setup-node@v7
|
|
15
|
-
with:
|
|
16
|
-
node-version-file: package.json
|
|
17
|
-
- run: corepack enable
|
|
18
|
-
- uses: dtolnay/rust-toolchain@stable
|
|
19
|
-
with:
|
|
20
|
-
components: rustfmt, clippy
|
|
21
|
-
- uses: Swatinem/rust-cache@v2
|
|
22
|
-
- run: pnpm install
|
|
23
|
-
- run: pnpm run check
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
name: Check
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
push:
|
|
6
|
-
branches:
|
|
7
|
-
- main
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
check:
|
|
11
|
-
runs-on: ubuntu-latest{{DEPLOYMENT_MATRIX}}
|
|
12
|
-
steps:
|
|
13
|
-
- uses: actions/checkout@v7
|
|
14
|
-
- uses: actions/setup-node@v7
|
|
15
|
-
with:
|
|
16
|
-
node-version-file: package.json
|
|
17
|
-
- run: corepack enable{{DEPLOYMENT_DOCKER_PREPARATION}}
|
|
18
|
-
- run: pnpm install
|
|
19
|
-
- run: pnpm run check{{ROOT_CHECK_CONDITION}}{{DEPLOYMENT_CHECK}}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { mkdirSync, rmSync } from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
const mode = process.argv[2];
|
|
6
|
-
|
|
7
|
-
if (mode !== "dev" && mode !== "test") {
|
|
8
|
-
throw new Error("Usage: node scripts/prepare-database.ts <dev|test>");
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const packageDirectory = process.cwd();
|
|
12
|
-
const databaseFile = process.env.DATABASE_FILE
|
|
13
|
-
? path.resolve(
|
|
14
|
-
process.env.INIT_CWD ?? packageDirectory,
|
|
15
|
-
process.env.DATABASE_FILE,
|
|
16
|
-
)
|
|
17
|
-
: path.resolve(packageDirectory, "../../apps/web/data/app.sqlite");
|
|
18
|
-
const environment = { ...process.env, DATABASE_FILE: databaseFile };
|
|
19
|
-
|
|
20
|
-
mkdirSync(path.dirname(databaseFile), { recursive: true });
|
|
21
|
-
if (mode === "test") {
|
|
22
|
-
rmSync(databaseFile, { force: true });
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
execFileSync("pnpm", ["run", "db:push"], {
|
|
26
|
-
cwd: packageDirectory,
|
|
27
|
-
env: environment,
|
|
28
|
-
stdio: "inherit",
|
|
29
|
-
});
|
|
30
|
-
const databasePackageName = process.env.DATABASE_PACKAGE_NAME;
|
|
31
|
-
if (!databasePackageName) {
|
|
32
|
-
throw new Error(
|
|
33
|
-
"DATABASE_PACKAGE_NAME must name the database Package Boundary",
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
execFileSync(
|
|
38
|
-
"pnpm",
|
|
39
|
-
["--filter", databasePackageName, "run", "db:seed:example"],
|
|
40
|
-
{
|
|
41
|
-
cwd: packageDirectory,
|
|
42
|
-
env: environment,
|
|
43
|
-
stdio: "inherit",
|
|
44
|
-
},
|
|
45
|
-
);
|
package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/scripts/run-playwright.ts
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { mkdir, rm } from "node:fs/promises";
|
|
3
|
-
import { createServer } from "node:net";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
|
|
7
|
-
const databaseFile = fileURLToPath(
|
|
8
|
-
new URL("../node_modules/.tmp/e2e.sqlite", import.meta.url),
|
|
9
|
-
);
|
|
10
|
-
const externalBaseUrlName = "PLAYWRIGHT_EXTERNAL_BASE_URL";
|
|
11
|
-
const externalReadinessTimeoutMs = 30_000;
|
|
12
|
-
|
|
13
|
-
function externalBaseUrl(): string | undefined {
|
|
14
|
-
if (!Object.hasOwn(process.env, externalBaseUrlName)) {
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const value = process.env[externalBaseUrlName]?.trim();
|
|
19
|
-
if (!value) {
|
|
20
|
-
throw new Error(`${externalBaseUrlName} must be a non-empty HTTP(S) URL`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
let url: URL;
|
|
24
|
-
try {
|
|
25
|
-
url = new URL(value);
|
|
26
|
-
} catch {
|
|
27
|
-
throw new Error(`${externalBaseUrlName} must be a valid HTTP(S) URL`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
31
|
-
throw new Error(`${externalBaseUrlName} must use HTTP or HTTPS`);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return url.toString();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function availablePort(): Promise<string> {
|
|
38
|
-
return await new Promise((resolve, reject) => {
|
|
39
|
-
const server = createServer();
|
|
40
|
-
|
|
41
|
-
server.once("error", reject);
|
|
42
|
-
server.listen(0, "127.0.0.1", () => {
|
|
43
|
-
const address = server.address();
|
|
44
|
-
|
|
45
|
-
if (typeof address !== "object" || address === null) {
|
|
46
|
-
server.close(() => {
|
|
47
|
-
reject(new Error("Could not allocate a Playwright web port"));
|
|
48
|
-
});
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const port = String(address.port);
|
|
53
|
-
server.close((error) => {
|
|
54
|
-
if (error) {
|
|
55
|
-
reject(error);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
resolve(port);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async function awaitExternalService(baseUrl: string): Promise<void> {
|
|
66
|
-
const deadline = Date.now() + externalReadinessTimeoutMs;
|
|
67
|
-
|
|
68
|
-
while (Date.now() < deadline) {
|
|
69
|
-
try {
|
|
70
|
-
const response = await fetch(baseUrl, {
|
|
71
|
-
signal: AbortSignal.timeout(2_000),
|
|
72
|
-
});
|
|
73
|
-
if (response.ok) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
} catch {
|
|
77
|
-
// The externally managed service may still be starting.
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
throw new Error(
|
|
84
|
-
`External Playwright service at ${baseUrl} was not ready within ${externalReadinessTimeoutMs}ms`,
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async function localPlaywrightEnv(): Promise<NodeJS.ProcessEnv> {
|
|
89
|
-
return {
|
|
90
|
-
...process.env,
|
|
91
|
-
DATABASE_FILE: process.env.DATABASE_FILE ?? databaseFile,
|
|
92
|
-
PLAYWRIGHT_WEB_PORT:
|
|
93
|
-
process.env.PLAYWRIGHT_WEB_PORT ?? (await availablePort()),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const externalServiceUrl = externalBaseUrl();
|
|
98
|
-
const env = externalServiceUrl
|
|
99
|
-
? { ...process.env }
|
|
100
|
-
: await localPlaywrightEnv();
|
|
101
|
-
const localDatabaseFile = externalServiceUrl ? undefined : env.DATABASE_FILE;
|
|
102
|
-
delete env.NO_COLOR;
|
|
103
|
-
|
|
104
|
-
if (externalServiceUrl) {
|
|
105
|
-
await awaitExternalService(externalServiceUrl);
|
|
106
|
-
} else {
|
|
107
|
-
await mkdir(path.dirname(localDatabaseFile!), { recursive: true });
|
|
108
|
-
await rm(localDatabaseFile!, { force: true });
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const command = process.platform === "win32" ? "playwright.cmd" : "playwright";
|
|
112
|
-
const child = spawn(command, ["test", ...process.argv.slice(2)], {
|
|
113
|
-
env,
|
|
114
|
-
stdio: "inherit",
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
const forwardedSignals = ["SIGINT", "SIGTERM"] as const;
|
|
118
|
-
const forwardSignal = (signal: NodeJS.Signals): void => {
|
|
119
|
-
child.kill(signal);
|
|
120
|
-
};
|
|
121
|
-
for (const signal of forwardedSignals) {
|
|
122
|
-
process.once(signal, forwardSignal);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
const result = await new Promise<{
|
|
127
|
-
code: number | null;
|
|
128
|
-
signal: NodeJS.Signals | null;
|
|
129
|
-
}>((resolve, reject) => {
|
|
130
|
-
child.once("error", reject);
|
|
131
|
-
child.once("exit", (code, signal) => resolve({ code, signal }));
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
if (result.signal) {
|
|
135
|
-
console.error(`Playwright exited with signal ${result.signal}`);
|
|
136
|
-
process.exitCode = 1;
|
|
137
|
-
} else {
|
|
138
|
-
process.exitCode = result.code ?? 1;
|
|
139
|
-
}
|
|
140
|
-
} catch (error) {
|
|
141
|
-
console.error(error);
|
|
142
|
-
process.exitCode = 1;
|
|
143
|
-
} finally {
|
|
144
|
-
for (const signal of forwardedSignals) {
|
|
145
|
-
process.removeListener(signal, forwardSignal);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (localDatabaseFile) {
|
|
149
|
-
await rm(localDatabaseFile, { force: true });
|
|
150
|
-
}
|
|
151
|
-
}
|