@ykdz/template 0.0.32 → 0.0.33
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 +38 -13
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.js +1 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.js +6 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.js +6 -0
- 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/vike-app/web/playwright.config.ts +1 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/playwright.config.ts +2 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/playwright.config.ts +2 -0
- 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 +3 -3
- 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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CiDiagnosticArtifactDeclaration } from "#template-core/ci-diagnostic-artifact";
|
|
1
2
|
import { type DevelopmentContainerToolLayerBuildArgument, type DevelopmentContainerToolLayerMount, type DevelopmentContainerToolLayerProbe, type PlannedDevelopmentContainerToolLayer } from "#template-core/development-container-tool-layer";
|
|
2
3
|
import type { CheckEnvironmentNeed, DeploymentEnvironmentNeed } from "#template-core/module-graph";
|
|
3
4
|
import { type PackageContribution } from "#template-core/package-contribution";
|
|
@@ -42,6 +43,7 @@ export type GeneratedRepositoryPlan = {
|
|
|
42
43
|
};
|
|
43
44
|
readonly environmentNeeds: readonly CheckEnvironmentNeed[];
|
|
44
45
|
readonly deploymentEnvironmentNeeds: readonly DeploymentEnvironmentNeed[];
|
|
46
|
+
readonly ciDiagnosticArtifacts: readonly CiDiagnosticArtifactDeclaration[];
|
|
45
47
|
/** Structured manifests used to derive the generated Dependency Catalog. */
|
|
46
48
|
readonly manifests: readonly Readonly<Record<string, unknown>>[];
|
|
47
49
|
readonly dependencyCatalog: Readonly<Record<string, string>>;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { composeCiDiagnosticArtifacts } from "#template-core/ci-diagnostic-artifact";
|
|
4
5
|
import { collectGeneratedManifestCatalogReferences, selectTemplateDependencyCatalogEntries, } from "#template-core/dependency-catalog";
|
|
5
6
|
import { planDevelopmentContainerToolLayersSync, } from "#template-core/development-container-tool-layer";
|
|
6
7
|
import { editorCustomizationForCapabilities, loadEditorCustomizationDeclarations, } from "#template-core/editor-customization";
|
|
7
8
|
import { checkEnvironmentNeedFact, checkEnvironmentNeedFromFact, deploymentEnvironmentNeedFact, deploymentEnvironmentNeedFromFact, renderDeploymentCheckCommand, renderFixCommand, renderRootCheckCommand, } from "#template-core/module-graph";
|
|
8
9
|
import { assertPackageContribution, } from "#template-core/package-contribution";
|
|
9
10
|
import { assertProjectBlueprintV2, validateProjectBlueprintV2 as validateCoreProjectBlueprintV2, } from "#template-core/project-blueprint-v2";
|
|
10
|
-
import { projectCheckWorkflowTemplateReplacements, projectDependabotTemplateReplacements, } from "#template-core/project-github";
|
|
11
|
+
import { projectCheckWorkflowTemplateSource, projectCheckWorkflowTemplateReplacements, projectDependabotTemplateReplacements, } from "#template-core/project-github";
|
|
11
12
|
import { planExplicitProjectLinks } from "#template-core/project-linking-v2";
|
|
12
13
|
import { resolveTemplateSource, } from "#template-core/renderer";
|
|
13
14
|
import { rustBinDefinition } from "./rust-bin/definition.js";
|
|
@@ -720,12 +721,22 @@ function foundationPlan(options) {
|
|
|
720
721
|
scripts !== null &&
|
|
721
722
|
typeof scripts.deployment === "string");
|
|
722
723
|
});
|
|
723
|
-
const
|
|
724
|
-
if (new Set(
|
|
724
|
+
const contributionPackagePaths = contributions.map((contribution) => contribution.definition.path);
|
|
725
|
+
if (new Set(contributionPackagePaths).size !== contributionPackagePaths.length)
|
|
725
726
|
throw new Error("Package Contributions must have unique Package Paths");
|
|
727
|
+
const blueprintPackagePaths = options.blueprint.packages.map((definition) => definition.path);
|
|
728
|
+
if (contributionPackagePaths.length !== blueprintPackagePaths.length ||
|
|
729
|
+
contributionPackagePaths.some((packagePath) => !blueprintPackagePaths.includes(packagePath))) {
|
|
730
|
+
throw new Error("Package Contributions must exactly match Project Blueprint Package Paths");
|
|
731
|
+
}
|
|
726
732
|
const packageNames = contributions.map((contribution) => contribution.definition.name);
|
|
727
733
|
if (new Set(packageNames).size !== packageNames.length)
|
|
728
734
|
throw new Error("Package Contributions must have unique package names");
|
|
735
|
+
const ciDiagnosticArtifactDeclarations = contributions.flatMap((contribution) => contribution.ciDiagnosticArtifacts ?? []);
|
|
736
|
+
const ciDiagnosticArtifacts = composeCiDiagnosticArtifacts({
|
|
737
|
+
packagePaths: blueprintPackagePaths,
|
|
738
|
+
declarations: ciDiagnosticArtifactDeclarations,
|
|
739
|
+
});
|
|
729
740
|
assertCompatibleRustToolchainFacts(contributions);
|
|
730
741
|
const contributedToolLayers = contributions.flatMap((contribution) => contribution.foundation.developmentContainerToolLayers ?? []);
|
|
731
742
|
const developmentContainer = contributedDevcontainerComposition({
|
|
@@ -810,16 +821,29 @@ function foundationPlan(options) {
|
|
|
810
821
|
].join("\n"),
|
|
811
822
|
},
|
|
812
823
|
};
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
824
|
+
const workflowTemplateSource = projectCheckWorkflowTemplateSource({
|
|
825
|
+
packagePaths: blueprintPackagePaths,
|
|
826
|
+
deploymentEnvironmentNeeds,
|
|
827
|
+
hasDeploymentTask,
|
|
828
|
+
diagnosticArtifacts: ciDiagnosticArtifacts,
|
|
829
|
+
});
|
|
830
|
+
const workflowOperation = ciDiagnosticArtifacts.length === 0
|
|
831
|
+
? {
|
|
832
|
+
kind: "copyFile",
|
|
833
|
+
source: templateSources.foundation,
|
|
834
|
+
from: workflowTemplateSource,
|
|
835
|
+
to: ".github/workflows/check.yml",
|
|
836
|
+
}
|
|
837
|
+
: {
|
|
838
|
+
kind: "writeTextTemplate",
|
|
839
|
+
source: templateSources.foundation,
|
|
840
|
+
from: workflowTemplateSource,
|
|
841
|
+
to: ".github/workflows/check.yml",
|
|
842
|
+
replacements: projectCheckWorkflowTemplateReplacements({
|
|
843
|
+
packagePaths: blueprintPackagePaths,
|
|
844
|
+
diagnosticArtifacts: ciDiagnosticArtifacts,
|
|
845
|
+
}),
|
|
846
|
+
};
|
|
823
847
|
const workflowOperations = [
|
|
824
848
|
workflowOperation,
|
|
825
849
|
{
|
|
@@ -1021,6 +1045,7 @@ function foundationPlan(options) {
|
|
|
1021
1045
|
},
|
|
1022
1046
|
environmentNeeds,
|
|
1023
1047
|
deploymentEnvironmentNeeds,
|
|
1048
|
+
ciDiagnosticArtifacts,
|
|
1024
1049
|
manifests: [...contributions.map((item) => item.manifest), rootManifest],
|
|
1025
1050
|
dependencyCatalog,
|
|
1026
1051
|
dependencyMaintenancePolicy,
|
|
@@ -315,6 +315,7 @@ function webContribution(context) {
|
|
|
315
315
|
playwrightBrowserAssetsEnvironmentNeed({ browser: "chromium", owner }),
|
|
316
316
|
shellCheckEnvironmentNeed(owner),
|
|
317
317
|
],
|
|
318
|
+
ciDiagnosticArtifacts: [{ kind: "playwright", owner }],
|
|
318
319
|
deploymentEnvironmentNeeds: [dockerEngineEnvironmentNeed()],
|
|
319
320
|
foundation: {
|
|
320
321
|
...foundation(),
|
|
@@ -44,6 +44,12 @@ function appContribution(options) {
|
|
|
44
44
|
}),
|
|
45
45
|
operations,
|
|
46
46
|
environmentNeeds: vueApplicationEnvironmentNeeds(definition.path),
|
|
47
|
+
ciDiagnosticArtifacts: [
|
|
48
|
+
{
|
|
49
|
+
kind: "playwright",
|
|
50
|
+
owner: { kind: "package-boundary", path: definition.path },
|
|
51
|
+
},
|
|
52
|
+
],
|
|
47
53
|
foundation: {
|
|
48
54
|
toolchains: {},
|
|
49
55
|
editorCapabilities: ["oxc-format-lint", "vue", "tailwind", "vitest"],
|
|
@@ -140,6 +140,12 @@ function webContribution(context) {
|
|
|
140
140
|
}),
|
|
141
141
|
operations,
|
|
142
142
|
environmentNeeds: vueApplicationEnvironmentNeeds(definition.path),
|
|
143
|
+
ciDiagnosticArtifacts: [
|
|
144
|
+
{
|
|
145
|
+
kind: "playwright",
|
|
146
|
+
owner: { kind: "package-boundary", path: definition.path },
|
|
147
|
+
},
|
|
148
|
+
],
|
|
143
149
|
foundation: {
|
|
144
150
|
...packageFoundation(),
|
|
145
151
|
developmentContainerToolLayers: vueApplicationDevelopmentContainerToolLayers(),
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
name: Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
check:
|
|
18
|
+
name: ${{ matrix.job_name }}
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
timeout-minutes: ${{ matrix.timeout_minutes }}
|
|
21
|
+
strategy:
|
|
22
|
+
fail-fast: false
|
|
23
|
+
matrix:
|
|
24
|
+
include:
|
|
25
|
+
- capability: root
|
|
26
|
+
job_name: Root Check
|
|
27
|
+
task_entrypoint: pnpm run check
|
|
28
|
+
timeout_minutes: 30
|
|
29
|
+
requires_docker: false
|
|
30
|
+
- capability: deployment
|
|
31
|
+
job_name: Deployment Check
|
|
32
|
+
task_entrypoint: pnpm run check:deployment
|
|
33
|
+
timeout_minutes: 45
|
|
34
|
+
requires_docker: true
|
|
35
|
+
steps:
|
|
36
|
+
- name: Checkout source
|
|
37
|
+
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
38
|
+
with:
|
|
39
|
+
persist-credentials: false
|
|
40
|
+
fetch-depth: 1
|
|
41
|
+
- name: Set up Node.js
|
|
42
|
+
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
|
43
|
+
with:
|
|
44
|
+
node-version-file: package.json
|
|
45
|
+
- name: Set up pnpm
|
|
46
|
+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
|
|
47
|
+
with:
|
|
48
|
+
cache: true
|
|
49
|
+
- name: Set up Docker Buildx
|
|
50
|
+
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
|
|
51
|
+
if: matrix.requires_docker
|
|
52
|
+
- name: Install dependencies
|
|
53
|
+
run: pnpm install --frozen-lockfile
|
|
54
|
+
- name: Run selected Check
|
|
55
|
+
run: ${{ matrix.task_entrypoint }}
|
|
56
|
+
- name: Stage Root Check diagnostics
|
|
57
|
+
if: failure() && matrix.capability == 'root'
|
|
58
|
+
env:
|
|
59
|
+
DIAGNOSTIC_OWNER_PATHS: |-
|
|
60
|
+
{{DIAGNOSTIC_OWNER_PATHS}}
|
|
61
|
+
run: |
|
|
62
|
+
rm -rf .template-ci-diagnostics
|
|
63
|
+
mkdir -p .template-ci-diagnostics
|
|
64
|
+
printf '%s\n' "$DIAGNOSTIC_OWNER_PATHS" | while IFS= read -r owner_path; do
|
|
65
|
+
for diagnostic_directory in test-results playwright-report; do
|
|
66
|
+
source_path="$owner_path/$diagnostic_directory"
|
|
67
|
+
destination_path=".template-ci-diagnostics/$owner_path/$diagnostic_directory"
|
|
68
|
+
if [ -d "$source_path" ]; then
|
|
69
|
+
mkdir -p "$(dirname "$destination_path")"
|
|
70
|
+
cp -R "$source_path" "$destination_path"
|
|
71
|
+
fi
|
|
72
|
+
done
|
|
73
|
+
done
|
|
74
|
+
- name: Upload Root Check diagnostics
|
|
75
|
+
if: failure() && matrix.capability == 'root'
|
|
76
|
+
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4
|
|
77
|
+
with:
|
|
78
|
+
name: root-check-diagnostics
|
|
79
|
+
path: .template-ci-diagnostics
|
|
80
|
+
if-no-files-found: ignore
|
|
81
|
+
retention-days: 7
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
check:
|
|
18
|
+
name: ${{ matrix.job_name }}
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
timeout-minutes: ${{ matrix.timeout_minutes }}
|
|
21
|
+
strategy:
|
|
22
|
+
fail-fast: false
|
|
23
|
+
matrix:
|
|
24
|
+
include:
|
|
25
|
+
- capability: root
|
|
26
|
+
job_name: Root Check
|
|
27
|
+
task_entrypoint: pnpm run check
|
|
28
|
+
timeout_minutes: 30
|
|
29
|
+
requires_docker: false
|
|
30
|
+
- capability: deployment
|
|
31
|
+
job_name: Deployment Check
|
|
32
|
+
task_entrypoint: pnpm run check:deployment
|
|
33
|
+
timeout_minutes: 45
|
|
34
|
+
requires_docker: true
|
|
35
|
+
steps:
|
|
36
|
+
- name: Checkout source
|
|
37
|
+
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
38
|
+
with:
|
|
39
|
+
persist-credentials: false
|
|
40
|
+
fetch-depth: 1
|
|
41
|
+
- name: Set up Node.js
|
|
42
|
+
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
|
43
|
+
with:
|
|
44
|
+
node-version-file: package.json
|
|
45
|
+
- name: Set up pnpm
|
|
46
|
+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
|
|
47
|
+
with:
|
|
48
|
+
cache: true
|
|
49
|
+
- name: Set up Docker Buildx
|
|
50
|
+
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
|
|
51
|
+
if: matrix.requires_docker
|
|
52
|
+
- name: Install dependencies
|
|
53
|
+
run: pnpm install --frozen-lockfile
|
|
54
|
+
- name: Run selected Check
|
|
55
|
+
run: ${{ matrix.task_entrypoint }}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
check:
|
|
18
|
+
name: Root Check
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
timeout-minutes: 30
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout source
|
|
23
|
+
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
24
|
+
with:
|
|
25
|
+
persist-credentials: false
|
|
26
|
+
fetch-depth: 1
|
|
27
|
+
- name: Set up Node.js
|
|
28
|
+
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
|
29
|
+
with:
|
|
30
|
+
node-version-file: package.json
|
|
31
|
+
- name: Set up pnpm
|
|
32
|
+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
|
|
33
|
+
with:
|
|
34
|
+
cache: true
|
|
35
|
+
- name: Install dependencies
|
|
36
|
+
run: pnpm install --frozen-lockfile
|
|
37
|
+
- name: Run Root Check
|
|
38
|
+
run: pnpm run check
|
|
39
|
+
- name: Stage Root Check diagnostics
|
|
40
|
+
if: failure()
|
|
41
|
+
env:
|
|
42
|
+
DIAGNOSTIC_OWNER_PATHS: |-
|
|
43
|
+
{{DIAGNOSTIC_OWNER_PATHS}}
|
|
44
|
+
run: |
|
|
45
|
+
rm -rf .template-ci-diagnostics
|
|
46
|
+
mkdir -p .template-ci-diagnostics
|
|
47
|
+
printf '%s\n' "$DIAGNOSTIC_OWNER_PATHS" | while IFS= read -r owner_path; do
|
|
48
|
+
for diagnostic_directory in test-results playwright-report; do
|
|
49
|
+
source_path="$owner_path/$diagnostic_directory"
|
|
50
|
+
destination_path=".template-ci-diagnostics/$owner_path/$diagnostic_directory"
|
|
51
|
+
if [ -d "$source_path" ]; then
|
|
52
|
+
mkdir -p "$(dirname "$destination_path")"
|
|
53
|
+
cp -R "$source_path" "$destination_path"
|
|
54
|
+
fi
|
|
55
|
+
done
|
|
56
|
+
done
|
|
57
|
+
- name: Upload Root Check diagnostics
|
|
58
|
+
if: failure()
|
|
59
|
+
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4
|
|
60
|
+
with:
|
|
61
|
+
name: root-check-diagnostics
|
|
62
|
+
path: .template-ci-diagnostics
|
|
63
|
+
if-no-files-found: ignore
|
|
64
|
+
retention-days: 7
|
package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check.yml
CHANGED
|
@@ -6,14 +6,33 @@ on:
|
|
|
6
6
|
branches:
|
|
7
7
|
- main
|
|
8
8
|
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
9
16
|
jobs:
|
|
10
17
|
check:
|
|
18
|
+
name: Root Check
|
|
11
19
|
runs-on: ubuntu-latest
|
|
20
|
+
timeout-minutes: 30
|
|
12
21
|
steps:
|
|
13
|
-
-
|
|
14
|
-
|
|
22
|
+
- name: Checkout source
|
|
23
|
+
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
24
|
+
with:
|
|
25
|
+
persist-credentials: false
|
|
26
|
+
fetch-depth: 1
|
|
27
|
+
- name: Set up Node.js
|
|
28
|
+
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
|
15
29
|
with:
|
|
16
30
|
node-version-file: package.json
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
31
|
+
- name: Set up pnpm
|
|
32
|
+
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
|
|
33
|
+
with:
|
|
34
|
+
cache: true
|
|
35
|
+
- name: Install dependencies
|
|
36
|
+
run: pnpm install --frozen-lockfile
|
|
37
|
+
- name: Run Root Check
|
|
38
|
+
run: pnpm run check
|
|
@@ -17,8 +17,9 @@ export default defineConfig({
|
|
|
17
17
|
testDir: "./test/e2e",
|
|
18
18
|
use: {
|
|
19
19
|
baseURL: previewUrl,
|
|
20
|
-
trace: "on-
|
|
20
|
+
trace: "retain-on-failure",
|
|
21
21
|
},
|
|
22
|
+
reporter: [["list"], ["html"]],
|
|
22
23
|
webServer: {
|
|
23
24
|
command: `pnpm run preview --host 127.0.0.1 --port ${previewPort}`,
|
|
24
25
|
reuseExistingServer: !process.env.CI,
|
package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/playwright.config.ts
CHANGED
|
@@ -20,8 +20,10 @@ export default defineConfig({
|
|
|
20
20
|
testDir: "./test/e2e",
|
|
21
21
|
use: {
|
|
22
22
|
baseURL: webBaseUrl,
|
|
23
|
+
trace: "retain-on-failure",
|
|
23
24
|
...devices["Desktop Chrome"],
|
|
24
25
|
},
|
|
26
|
+
reporter: [["list"], ["html"]],
|
|
25
27
|
webServer: [
|
|
26
28
|
{
|
|
27
29
|
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
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
}):
|
|
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>;
|
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ykdz/template",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.33",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Project template generator for YKDZ starter projects.",
|
|
6
6
|
"keywords": [
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"semver": "^7.8.5",
|
|
61
61
|
"typescript": "npm:typescript@^6.0.3",
|
|
62
62
|
"valibot": "^1.4.2",
|
|
63
|
-
"@ykdz/template-
|
|
64
|
-
"@ykdz/template-
|
|
63
|
+
"@ykdz/template-builtin-presets": "0.0.0",
|
|
64
|
+
"@ykdz/template-core": "0.0.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@types/node": "^24.13.3",
|
|
@@ -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}}
|