berg-pages 0.1.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +93 -0
- package/LICENSE +183 -0
- package/README.md +93 -0
- package/dist/adapters/local-deploy-execution-ports.d.ts +12 -0
- package/dist/adapters/local-deploy-execution-ports.js +81 -0
- package/dist/adapters/local-environment-probe.d.ts +5 -0
- package/dist/adapters/local-environment-probe.js +54 -0
- package/dist/adapters/local-init-apply-ports.d.ts +10 -0
- package/dist/adapters/local-init-apply-ports.js +47 -0
- package/dist/adapters/local-project-inspector.d.ts +4 -0
- package/dist/adapters/local-project-inspector.js +184 -0
- package/dist/application/contracts/plugins/deploy-mode-plugin.d.ts +20 -0
- package/dist/application/contracts/plugins/deploy-mode-plugin.js +1 -0
- package/dist/application/contracts/plugins/framework-plugin.d.ts +19 -0
- package/dist/application/contracts/plugins/framework-plugin.js +1 -0
- package/dist/application/contracts/ports/environment-probe-port.d.ts +4 -0
- package/dist/application/contracts/ports/environment-probe-port.js +1 -0
- package/dist/application/contracts/ports/project-inspector-port.d.ts +28 -0
- package/dist/application/contracts/ports/project-inspector-port.js +1 -0
- package/dist/application/contracts/project-context.d.ts +6 -0
- package/dist/application/contracts/project-context.js +1 -0
- package/dist/application/index.d.ts +11 -0
- package/dist/application/index.js +11 -0
- package/dist/application/usecases/apply-deploy-plan.d.ts +8 -0
- package/dist/application/usecases/apply-deploy-plan.js +100 -0
- package/dist/application/usecases/apply-init-plan.d.ts +13 -0
- package/dist/application/usecases/apply-init-plan.js +65 -0
- package/dist/application/usecases/build-deploy-plan.d.ts +9 -0
- package/dist/application/usecases/build-deploy-plan.js +177 -0
- package/dist/application/usecases/build-doctor-plan.d.ts +9 -0
- package/dist/application/usecases/build-doctor-plan.js +235 -0
- package/dist/application/usecases/build-init-plan.d.ts +9 -0
- package/dist/application/usecases/build-init-plan.js +140 -0
- package/dist/application/usecases/create-project-context.d.ts +8 -0
- package/dist/application/usecases/create-project-context.js +168 -0
- package/dist/application/usecases/workflow-diagnostics.d.ts +13 -0
- package/dist/application/usecases/workflow-diagnostics.js +137 -0
- package/dist/core/diagnostics/diagnostic.d.ts +11 -0
- package/dist/core/diagnostics/diagnostic.js +1 -0
- package/dist/core/entities/command.d.ts +1 -0
- package/dist/core/entities/command.js +1 -0
- package/dist/core/entities/deploy-mode.d.ts +6 -0
- package/dist/core/entities/deploy-mode.js +1 -0
- package/dist/core/entities/environment.d.ts +16 -0
- package/dist/core/entities/environment.js +1 -0
- package/dist/core/entities/framework.d.ts +8 -0
- package/dist/core/entities/framework.js +1 -0
- package/dist/core/entities/pages-target.d.ts +22 -0
- package/dist/core/entities/pages-target.js +1 -0
- package/dist/core/entities/project.d.ts +45 -0
- package/dist/core/entities/project.js +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.js +13 -0
- package/dist/core/plans/assumption.d.ts +7 -0
- package/dist/core/plans/assumption.js +1 -0
- package/dist/core/plans/build-plan.d.ts +12 -0
- package/dist/core/plans/build-plan.js +1 -0
- package/dist/core/plans/command-plan.d.ts +27 -0
- package/dist/core/plans/command-plan.js +45 -0
- package/dist/core/plans/deploy-plan.d.ts +17 -0
- package/dist/core/plans/deploy-plan.js +1 -0
- package/dist/core/plans/plan-step.d.ts +59 -0
- package/dist/core/plans/plan-step.js +1 -0
- package/dist/core/plans/template-plan.d.ts +11 -0
- package/dist/core/plans/template-plan.js +1 -0
- package/dist/features/deployment-modes/git-pages-deploy-mode-plugin.d.ts +12 -0
- package/dist/features/deployment-modes/git-pages-deploy-mode-plugin.js +49 -0
- package/dist/features/frameworks/astro-framework-plugin.d.ts +36 -0
- package/dist/features/frameworks/astro-framework-plugin.js +97 -0
- package/dist/features/frameworks/static-html-framework-plugin.d.ts +36 -0
- package/dist/features/frameworks/static-html-framework-plugin.js +94 -0
- package/dist/features/frameworks/vite-framework-plugin.d.ts +36 -0
- package/dist/features/frameworks/vite-framework-plugin.js +97 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/interfaces/cli/main.d.ts +2 -0
- package/dist/interfaces/cli/main.js +157 -0
- package/dist/interfaces/cli/render-command-plan.d.ts +2 -0
- package/dist/interfaces/cli/render-command-plan.js +87 -0
- package/docs/CANARY.md +117 -0
- package/docs/COMPATIBILITY.md +88 -0
- package/docs/DEPLOY-SAFETY.md +113 -0
- package/package.json +83 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { createCommandPlan } from "../../core/plans/command-plan.js";
|
|
2
|
+
import { getWorkflowFileDiagnostics } from "./workflow-diagnostics.js";
|
|
3
|
+
export function buildDeployPlan(input) {
|
|
4
|
+
const { project } = input.context;
|
|
5
|
+
const diagnostics = [];
|
|
6
|
+
const assumptions = [];
|
|
7
|
+
const steps = [];
|
|
8
|
+
const outputDir = project.paths.buildOutputDir;
|
|
9
|
+
const outputDirExists = project.paths.buildOutputDirExists === true;
|
|
10
|
+
const target = project.pages.target;
|
|
11
|
+
const remoteUrl = project.repository.remoteUrl;
|
|
12
|
+
const publishBranch = "pages";
|
|
13
|
+
assumptions.push({
|
|
14
|
+
code: "deploy.output-dir.exists",
|
|
15
|
+
summary: "The build output directory exists before deploy.",
|
|
16
|
+
status: outputDir && outputDirExists ? "confirmed" : "missing",
|
|
17
|
+
detail: outputDir
|
|
18
|
+
? `Expected publish source: ${outputDir}`
|
|
19
|
+
: "No build output directory is known for this project."
|
|
20
|
+
}, {
|
|
21
|
+
code: "deploy.target",
|
|
22
|
+
summary: "The Codeberg Pages target is known.",
|
|
23
|
+
status: target ? "confirmed" : "missing",
|
|
24
|
+
detail: target ? `Target URL: ${target.url}` : "No deploy target is available."
|
|
25
|
+
}, {
|
|
26
|
+
code: "deploy.remote",
|
|
27
|
+
summary: "The Codeberg remote is known.",
|
|
28
|
+
status: remoteUrl ? "confirmed" : "missing",
|
|
29
|
+
detail: remoteUrl ? `Remote URL: ${remoteUrl}` : "No remote URL is available."
|
|
30
|
+
}, {
|
|
31
|
+
code: "deploy.branch",
|
|
32
|
+
summary: "The publish branch is selected.",
|
|
33
|
+
status: "confirmed",
|
|
34
|
+
detail: `Planned publish branch: ${publishBranch}`
|
|
35
|
+
});
|
|
36
|
+
if (!input.frameworkPlugin) {
|
|
37
|
+
diagnostics.push({
|
|
38
|
+
code: "deploy.framework-plugin.required",
|
|
39
|
+
severity: "error",
|
|
40
|
+
summary: "A framework plugin is required to build a deploy plan.",
|
|
41
|
+
detail: "Deploy planning needs framework build expectations before it can identify publish output.",
|
|
42
|
+
suggestions: [
|
|
43
|
+
"Resolve a supported Vite, Astro, or plain static framework plugin before deploy planning.",
|
|
44
|
+
"Run doctor to inspect framework detection first."
|
|
45
|
+
],
|
|
46
|
+
source: "application"
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
diagnostics.push(...input.frameworkPlugin.getDiagnostics(project));
|
|
51
|
+
diagnostics.push(...getWorkflowFileDiagnostics({
|
|
52
|
+
project,
|
|
53
|
+
templates: input.frameworkPlugin.getInitTemplates(project),
|
|
54
|
+
command: "deploy",
|
|
55
|
+
plannedConflictSeverity: "error",
|
|
56
|
+
unplannedWorkflowSeverity: "error"
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
if (!input.deployModePlugin) {
|
|
60
|
+
diagnostics.push({
|
|
61
|
+
code: "deploy.deploy-mode-plugin.required",
|
|
62
|
+
severity: "error",
|
|
63
|
+
summary: "A deployment-mode plugin is required to build a deploy plan.",
|
|
64
|
+
detail: "M5 only supports the git-pages deployment mode for Codeberg project Pages.",
|
|
65
|
+
suggestions: [
|
|
66
|
+
"Resolve the git-pages deployment-mode plugin before deploy planning.",
|
|
67
|
+
"Run doctor to inspect target and deploy-mode detection first."
|
|
68
|
+
],
|
|
69
|
+
source: "application"
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
diagnostics.push(...input.deployModePlugin.getDiagnostics(project));
|
|
74
|
+
}
|
|
75
|
+
if (!outputDir) {
|
|
76
|
+
diagnostics.push({
|
|
77
|
+
code: "deploy.output-dir.missing",
|
|
78
|
+
severity: "error",
|
|
79
|
+
summary: "The build output directory could not be inferred.",
|
|
80
|
+
detail: "Deploy cannot identify publish content until the framework output directory is known.",
|
|
81
|
+
suggestions: [
|
|
82
|
+
"Check that the project still matches a supported Vite, Astro, or plain static path.",
|
|
83
|
+
"Run doctor before deploy planning."
|
|
84
|
+
],
|
|
85
|
+
source: "application"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
else if (!outputDirExists) {
|
|
89
|
+
diagnostics.push({
|
|
90
|
+
code: "deploy.output-dir.not-found",
|
|
91
|
+
severity: "error",
|
|
92
|
+
summary: `The expected build output directory does not exist: ${outputDir}.`,
|
|
93
|
+
detail: "M5/M6 deploy behavior requires build output to exist before deploy planning or apply. It does not run builds for you.",
|
|
94
|
+
suggestions: [
|
|
95
|
+
"Run your project build command before deploy.",
|
|
96
|
+
"Confirm the framework output directory if the site builds somewhere other than dist."
|
|
97
|
+
],
|
|
98
|
+
source: "application"
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (!target) {
|
|
102
|
+
diagnostics.push({
|
|
103
|
+
code: "deploy.target.missing",
|
|
104
|
+
severity: "error",
|
|
105
|
+
summary: "No Codeberg Pages target is configured.",
|
|
106
|
+
detail: "Deploy cannot describe a publish destination until the Pages target is known.",
|
|
107
|
+
suggestions: [
|
|
108
|
+
"Use a Codeberg project remote so the target can be inferred.",
|
|
109
|
+
"Run doctor to inspect owner, repository, and base path diagnostics."
|
|
110
|
+
],
|
|
111
|
+
source: "application"
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (!remoteUrl) {
|
|
115
|
+
diagnostics.push({
|
|
116
|
+
code: "deploy.remote.missing",
|
|
117
|
+
severity: "error",
|
|
118
|
+
summary: "No git remote URL is configured.",
|
|
119
|
+
detail: "Deploy apply needs a remote destination for the future pages branch push.",
|
|
120
|
+
suggestions: [
|
|
121
|
+
"Configure a Codeberg origin remote.",
|
|
122
|
+
"Run doctor to inspect repository metadata."
|
|
123
|
+
],
|
|
124
|
+
source: "application"
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
steps.push(...assumptions.map(assumptionToVerifyStep));
|
|
128
|
+
if (target?.kind === "codeberg-project" && outputDir && remoteUrl) {
|
|
129
|
+
const sourcePath = `${project.paths.rootDir}/${outputDir}`.replace(/\\/g, "/");
|
|
130
|
+
steps.push({
|
|
131
|
+
id: "publish-site:git-pages",
|
|
132
|
+
kind: "publish-site",
|
|
133
|
+
title: "Publish site through git-pages",
|
|
134
|
+
modeId: "git-pages",
|
|
135
|
+
targetKind: target.kind,
|
|
136
|
+
outputDir,
|
|
137
|
+
sourcePath,
|
|
138
|
+
destination: target.url,
|
|
139
|
+
remote: remoteUrl,
|
|
140
|
+
branch: publishBranch,
|
|
141
|
+
force: true,
|
|
142
|
+
operations: [
|
|
143
|
+
`create temporary publish repository from ${outputDir}`,
|
|
144
|
+
`copy ${outputDir} contents into publish root`,
|
|
145
|
+
`commit publish contents on ${publishBranch}`,
|
|
146
|
+
`force push ${publishBranch} to ${remoteUrl}`
|
|
147
|
+
]
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
steps.push({
|
|
151
|
+
id: "note:deploy-safety",
|
|
152
|
+
kind: "note",
|
|
153
|
+
title: "Deploy safety boundary",
|
|
154
|
+
category: "deployment",
|
|
155
|
+
message: "Deploy dry-run only renders the plan. Deploy apply requires explicit confirmation before pushing.",
|
|
156
|
+
detail: "The command does not run builds, infer unpublished artifacts, or hide git push behavior."
|
|
157
|
+
});
|
|
158
|
+
return createCommandPlan({
|
|
159
|
+
command: "deploy",
|
|
160
|
+
summary: "Plan the git-pages publish path for Codeberg Pages.",
|
|
161
|
+
project,
|
|
162
|
+
assumptions,
|
|
163
|
+
diagnostics,
|
|
164
|
+
steps
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function assumptionToVerifyStep(assumption) {
|
|
168
|
+
return {
|
|
169
|
+
id: `verify:${assumption.code}`,
|
|
170
|
+
kind: "verify",
|
|
171
|
+
title: `Verify ${assumption.code}`,
|
|
172
|
+
subject: assumption.code,
|
|
173
|
+
status: assumption.status,
|
|
174
|
+
expected: assumption.summary,
|
|
175
|
+
...(assumption.detail ? { detail: assumption.detail } : {})
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DeployModePlugin } from "../contracts/plugins/deploy-mode-plugin.js";
|
|
2
|
+
import type { FrameworkPlugin } from "../contracts/plugins/framework-plugin.js";
|
|
3
|
+
import type { ProjectContext } from "../contracts/project-context.js";
|
|
4
|
+
export interface BuildDoctorPlanInput {
|
|
5
|
+
context: ProjectContext;
|
|
6
|
+
frameworkPlugins?: FrameworkPlugin[];
|
|
7
|
+
deployModePlugins?: DeployModePlugin[];
|
|
8
|
+
}
|
|
9
|
+
export declare function buildDoctorPlan(input: BuildDoctorPlanInput): import("../../core/plans/command-plan.js").CommandPlan;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { createCommandPlan } from "../../core/plans/command-plan.js";
|
|
2
|
+
import { getWorkflowFileDiagnostics } from "./workflow-diagnostics.js";
|
|
3
|
+
export function buildDoctorPlan(input) {
|
|
4
|
+
const { project } = input.context;
|
|
5
|
+
const diagnostics = [];
|
|
6
|
+
const assumptions = [
|
|
7
|
+
{
|
|
8
|
+
code: "repository.remote-url",
|
|
9
|
+
summary: "Repository remote URL is known.",
|
|
10
|
+
status: project.repository.remoteUrl ? "confirmed" : "unverified",
|
|
11
|
+
detail: project.repository.remoteUrl
|
|
12
|
+
? `Remote URL: ${project.repository.remoteUrl}`
|
|
13
|
+
: "No remote URL has been captured yet."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
code: "pages.target",
|
|
17
|
+
summary: "Pages target is identified.",
|
|
18
|
+
status: project.pages.target ? "confirmed" : "missing",
|
|
19
|
+
detail: project.pages.target
|
|
20
|
+
? `Detected target kind: ${project.pages.target.kind}.`
|
|
21
|
+
: "No Pages target is associated with the project yet."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
code: "pages.deploy-mode",
|
|
25
|
+
summary: "Deployment mode is selected.",
|
|
26
|
+
status: project.pages.deployMode ? "confirmed" : "missing",
|
|
27
|
+
detail: project.pages.deployMode
|
|
28
|
+
? `Selected deployment mode: ${project.pages.deployMode.id}.`
|
|
29
|
+
: "The project has not selected a deployment mode yet."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
code: "build.output-dir",
|
|
33
|
+
summary: "Build output directory is known.",
|
|
34
|
+
status: project.paths.buildOutputDir ? "confirmed" : "unverified",
|
|
35
|
+
detail: project.paths.buildOutputDir
|
|
36
|
+
? `Build output directory: ${project.paths.buildOutputDir}`
|
|
37
|
+
: "The build output directory still needs to be inferred or configured."
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
code: "pages.base-path",
|
|
41
|
+
summary: "Pages base path is known for the selected target.",
|
|
42
|
+
status: project.pages.target?.kind === "codeberg-project"
|
|
43
|
+
? "confirmed"
|
|
44
|
+
: "missing",
|
|
45
|
+
detail: project.pages.target?.kind === "codeberg-project"
|
|
46
|
+
? `Base path: ${project.pages.target.basePath}`
|
|
47
|
+
: "The Codeberg Pages base path still needs to be inferred from the repository target."
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
if (!project.repository.owner) {
|
|
51
|
+
diagnostics.push({
|
|
52
|
+
code: "repository.owner.missing",
|
|
53
|
+
severity: "warning",
|
|
54
|
+
summary: "Repository owner is not known yet.",
|
|
55
|
+
detail: "Owner information is useful for computing Codeberg Pages URLs and deployment destinations.",
|
|
56
|
+
suggestions: [
|
|
57
|
+
"Inspect the configured remote URL.",
|
|
58
|
+
"Let the repository inspector fill owner metadata before deployment planning."
|
|
59
|
+
],
|
|
60
|
+
source: "core"
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (!project.framework) {
|
|
64
|
+
diagnostics.push({
|
|
65
|
+
code: "framework.detection.missing",
|
|
66
|
+
severity: "warning",
|
|
67
|
+
summary: "No supported framework was detected.",
|
|
68
|
+
detail: "The project does not yet match a supported Vite, Astro, or plain static path.",
|
|
69
|
+
suggestions: [
|
|
70
|
+
"Check for a vite.config file.",
|
|
71
|
+
"Check for an astro.config file.",
|
|
72
|
+
"Check for a publishable dist or public directory."
|
|
73
|
+
],
|
|
74
|
+
source: "application"
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (!project.pages.target) {
|
|
78
|
+
diagnostics.push({
|
|
79
|
+
code: "pages.target.missing",
|
|
80
|
+
severity: "error",
|
|
81
|
+
summary: "No Pages target is configured.",
|
|
82
|
+
detail: "The tool cannot reason about deployment until it knows where the site should be published.",
|
|
83
|
+
suggestions: [
|
|
84
|
+
"Choose a Codeberg Pages target.",
|
|
85
|
+
"Capture the target during `init` or from repository metadata."
|
|
86
|
+
],
|
|
87
|
+
source: "core"
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (!project.pages.deployMode) {
|
|
91
|
+
diagnostics.push({
|
|
92
|
+
code: "pages.deploy-mode.missing",
|
|
93
|
+
severity: "warning",
|
|
94
|
+
summary: "No deployment mode has been selected.",
|
|
95
|
+
detail: "A deployment mode decides how the site should reach Codeberg Pages.",
|
|
96
|
+
suggestions: [
|
|
97
|
+
"Select a deployment mode before applying changes.",
|
|
98
|
+
"Use a deployment-mode plugin to diagnose the available path."
|
|
99
|
+
],
|
|
100
|
+
source: "core"
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (!project.paths.buildOutputDir) {
|
|
104
|
+
diagnostics.push({
|
|
105
|
+
code: "build.output-dir.missing",
|
|
106
|
+
severity: "warning",
|
|
107
|
+
summary: "The build output directory is still unknown.",
|
|
108
|
+
detail: "Framework-aware planning works better once the tool knows which directory contains the generated site.",
|
|
109
|
+
suggestions: [
|
|
110
|
+
"Infer the build output directory from framework configuration.",
|
|
111
|
+
"Let a framework plugin report the expected output directory."
|
|
112
|
+
],
|
|
113
|
+
source: "core"
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (project.pages.publishStrategy?.kind === "branch" &&
|
|
117
|
+
project.repository.currentBranch &&
|
|
118
|
+
project.repository.currentBranch !== project.pages.publishStrategy.branch) {
|
|
119
|
+
diagnostics.push({
|
|
120
|
+
code: "repository.branch.unexpected",
|
|
121
|
+
severity: "warning",
|
|
122
|
+
summary: "The current branch differs from the planned Pages branch.",
|
|
123
|
+
detail: `Current branch: ${project.repository.currentBranch}. Planned branch: ${project.pages.publishStrategy.branch}.`,
|
|
124
|
+
suggestions: [
|
|
125
|
+
"Run doctor from the branch that will receive the planned workflow.",
|
|
126
|
+
"Confirm the default branch before turning dry-run output into applied changes."
|
|
127
|
+
],
|
|
128
|
+
source: "core"
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (project.pages.target?.kind !== "codeberg-project") {
|
|
132
|
+
diagnostics.push({
|
|
133
|
+
code: "pages.base-path.missing",
|
|
134
|
+
severity: "warning",
|
|
135
|
+
summary: "The project Pages base path is still unknown.",
|
|
136
|
+
detail: "The first M2 slice expects a Codeberg project Pages path such as /repository-name/.",
|
|
137
|
+
suggestions: [
|
|
138
|
+
"Infer the repository owner and name from the Codeberg remote URL.",
|
|
139
|
+
"Select the Codeberg project Pages target before applying changes."
|
|
140
|
+
],
|
|
141
|
+
source: "core"
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
if (project.paths.domainsFilePath) {
|
|
145
|
+
const domains = project.paths.customDomains ?? [];
|
|
146
|
+
diagnostics.push({
|
|
147
|
+
code: "pages.custom-domain.detected",
|
|
148
|
+
severity: "warning",
|
|
149
|
+
summary: "A Codeberg Pages custom-domain file was detected.",
|
|
150
|
+
detail: domains.length > 0
|
|
151
|
+
? `Detected ${project.paths.domainsFilePath}: ${domains.join(", ")}. Custom-domain deploy is outside the supported git-pages path.`
|
|
152
|
+
: `Detected ${project.paths.domainsFilePath}, but no active domains were parsed. Custom-domain deploy is outside the supported git-pages path.`,
|
|
153
|
+
suggestions: [
|
|
154
|
+
"Use doctor output as guidance only; M12 does not add custom-domain deploy support.",
|
|
155
|
+
"Review Codeberg's custom-domain documentation before publishing custom-domain sites.",
|
|
156
|
+
"Keep this tool on the codeberg.page git-pages path until custom-domain support is explicitly scoped."
|
|
157
|
+
],
|
|
158
|
+
source: "application"
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const frameworkPlugin = project.framework
|
|
162
|
+
? input.frameworkPlugins?.find((plugin) => plugin.id === project.framework?.id)
|
|
163
|
+
: undefined;
|
|
164
|
+
if (project.framework && !frameworkPlugin) {
|
|
165
|
+
diagnostics.push({
|
|
166
|
+
code: "framework.plugin.missing",
|
|
167
|
+
severity: "warning",
|
|
168
|
+
summary: `No framework plugin is registered for "${project.framework.id}".`,
|
|
169
|
+
detail: "The project names a framework, but the composition root did not provide a matching plugin.",
|
|
170
|
+
suggestions: [
|
|
171
|
+
"Register the corresponding framework plugin.",
|
|
172
|
+
"Clear the framework selection if it was only a guess."
|
|
173
|
+
],
|
|
174
|
+
source: "application"
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
if (frameworkPlugin) {
|
|
178
|
+
diagnostics.push(...frameworkPlugin.getDiagnostics(project));
|
|
179
|
+
diagnostics.push(...getWorkflowFileDiagnostics({
|
|
180
|
+
project,
|
|
181
|
+
templates: frameworkPlugin.getInitTemplates(project),
|
|
182
|
+
command: "doctor",
|
|
183
|
+
plannedConflictSeverity: "warning",
|
|
184
|
+
unplannedWorkflowSeverity: "warning"
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
const pagesTarget = project.pages.target;
|
|
188
|
+
const deployModePlugin = project.pages.deployMode && pagesTarget
|
|
189
|
+
? input.deployModePlugins?.find((plugin) => plugin.id === project.pages.deployMode?.id &&
|
|
190
|
+
plugin.supports(pagesTarget))
|
|
191
|
+
: undefined;
|
|
192
|
+
if (project.pages.deployMode && project.pages.target && !deployModePlugin) {
|
|
193
|
+
diagnostics.push({
|
|
194
|
+
code: "deploy-mode.plugin.missing",
|
|
195
|
+
severity: "warning",
|
|
196
|
+
summary: `No deployment-mode plugin is registered for "${project.pages.deployMode.id}".`,
|
|
197
|
+
detail: "The selected deployment mode cannot contribute diagnostics or plans until its plugin is registered.",
|
|
198
|
+
suggestions: [
|
|
199
|
+
"Register the matching deployment-mode plugin.",
|
|
200
|
+
"Use a supported deployment mode for the current target."
|
|
201
|
+
],
|
|
202
|
+
source: "application"
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (deployModePlugin) {
|
|
206
|
+
diagnostics.push(...deployModePlugin.getDiagnostics(project));
|
|
207
|
+
}
|
|
208
|
+
const steps = assumptions.map((assumption) => ({
|
|
209
|
+
id: `verify:${assumption.code}`,
|
|
210
|
+
kind: "verify",
|
|
211
|
+
title: `Verify ${assumption.code}`,
|
|
212
|
+
subject: assumption.code,
|
|
213
|
+
status: assumption.status,
|
|
214
|
+
expected: assumption.summary,
|
|
215
|
+
...(assumption.detail ? { detail: assumption.detail } : {})
|
|
216
|
+
}));
|
|
217
|
+
if (project.framework) {
|
|
218
|
+
steps.push({
|
|
219
|
+
id: "note:framework-selection",
|
|
220
|
+
kind: "note",
|
|
221
|
+
title: "Framework selection",
|
|
222
|
+
category: "framework",
|
|
223
|
+
message: `The project is currently associated with the "${project.framework.id}" framework.`,
|
|
224
|
+
detail: `Selection source: ${project.framework.source}`
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return createCommandPlan({
|
|
228
|
+
command: "doctor",
|
|
229
|
+
summary: "Inspect the repository state and deployment assumptions before any changes are applied.",
|
|
230
|
+
project,
|
|
231
|
+
assumptions,
|
|
232
|
+
diagnostics,
|
|
233
|
+
steps
|
|
234
|
+
});
|
|
235
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DeployModePlugin } from "../contracts/plugins/deploy-mode-plugin.js";
|
|
2
|
+
import type { FrameworkPlugin } from "../contracts/plugins/framework-plugin.js";
|
|
3
|
+
import type { ProjectContext } from "../contracts/project-context.js";
|
|
4
|
+
export interface BuildInitPlanInput {
|
|
5
|
+
context: ProjectContext;
|
|
6
|
+
frameworkPlugin?: FrameworkPlugin;
|
|
7
|
+
deployModePlugin?: DeployModePlugin;
|
|
8
|
+
}
|
|
9
|
+
export declare function buildInitPlan(input: BuildInitPlanInput): import("../../core/plans/command-plan.js").CommandPlan;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { createCommandPlan } from "../../core/plans/command-plan.js";
|
|
2
|
+
import { getWorkflowFileDiagnostics } from "./workflow-diagnostics.js";
|
|
3
|
+
export function buildInitPlan(input) {
|
|
4
|
+
const { project } = input.context;
|
|
5
|
+
const diagnostics = [];
|
|
6
|
+
const assumptions = [];
|
|
7
|
+
const steps = [];
|
|
8
|
+
if (!input.frameworkPlugin) {
|
|
9
|
+
diagnostics.push({
|
|
10
|
+
code: "framework.plugin.required",
|
|
11
|
+
severity: "error",
|
|
12
|
+
summary: "A framework plugin is required to build an init plan.",
|
|
13
|
+
detail: "Init planning needs framework-specific templates and build expectations.",
|
|
14
|
+
suggestions: [
|
|
15
|
+
"Resolve a framework plugin before running init.",
|
|
16
|
+
"Use a plain HTML plugin for a static site without a build tool."
|
|
17
|
+
],
|
|
18
|
+
source: "application"
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
if (!project.paths.buildOutputDir && !input.frameworkPlugin) {
|
|
22
|
+
diagnostics.push({
|
|
23
|
+
code: "build.output-dir.missing",
|
|
24
|
+
severity: "warning",
|
|
25
|
+
summary: "The build output directory could not be inferred.",
|
|
26
|
+
detail: "Init planning needs a supported framework or static output directory before it can infer publish output.",
|
|
27
|
+
suggestions: [
|
|
28
|
+
"Check that the project has a recognizable Vite or Astro signal.",
|
|
29
|
+
"For plain static sites, create a dist or public directory.",
|
|
30
|
+
"Keep init in dry-run mode until the build output expectation is clear."
|
|
31
|
+
],
|
|
32
|
+
source: "application"
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const frameworkPlugin = input.frameworkPlugin;
|
|
36
|
+
if (frameworkPlugin) {
|
|
37
|
+
diagnostics.push(...frameworkPlugin.getDiagnostics(project));
|
|
38
|
+
const buildPlan = frameworkPlugin.getBuildPlan(project);
|
|
39
|
+
const templatePlans = frameworkPlugin.getInitTemplates(project);
|
|
40
|
+
diagnostics.push(...buildPlan.diagnostics);
|
|
41
|
+
assumptions.push(...buildPlan.assumptions);
|
|
42
|
+
diagnostics.push(...getWorkflowFileDiagnostics({
|
|
43
|
+
project,
|
|
44
|
+
templates: templatePlans,
|
|
45
|
+
command: "init",
|
|
46
|
+
plannedConflictSeverity: "error",
|
|
47
|
+
unplannedWorkflowSeverity: "error"
|
|
48
|
+
}));
|
|
49
|
+
steps.push(...templatePlans.map(templatePlanToStep));
|
|
50
|
+
if (buildPlan.outputDir) {
|
|
51
|
+
assumptions.push({
|
|
52
|
+
code: "init.build-output",
|
|
53
|
+
summary: "The framework plugin supplied a build output directory.",
|
|
54
|
+
status: "confirmed",
|
|
55
|
+
detail: `Expected build output directory: ${buildPlan.outputDir}`
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
steps.push({
|
|
59
|
+
id: `note:build-plan:${frameworkPlugin.id}`,
|
|
60
|
+
kind: "note",
|
|
61
|
+
title: "Framework build plan",
|
|
62
|
+
category: "framework",
|
|
63
|
+
message: buildPlan.summary,
|
|
64
|
+
...(buildPlan.outputDir ? { detail: `Output directory: ${buildPlan.outputDir}` } : {})
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (input.deployModePlugin) {
|
|
68
|
+
const deployPlan = input.deployModePlugin.getDeployPlan(project);
|
|
69
|
+
diagnostics.push(...input.deployModePlugin.getDiagnostics(project));
|
|
70
|
+
diagnostics.push(...deployPlan.diagnostics);
|
|
71
|
+
assumptions.push(...deployPlan.assumptions);
|
|
72
|
+
steps.push({
|
|
73
|
+
id: `note:deploy-mode:${input.deployModePlugin.id}`,
|
|
74
|
+
kind: "note",
|
|
75
|
+
title: "Selected deployment mode",
|
|
76
|
+
category: "deployment",
|
|
77
|
+
message: deployPlan.summary,
|
|
78
|
+
...(deployPlan.target ? { detail: `Target kind: ${deployPlan.target.kind}` } : {})
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (!input.deployModePlugin && project.pages.deployMode) {
|
|
82
|
+
diagnostics.push({
|
|
83
|
+
code: "deploy-mode.plugin.optional-missing",
|
|
84
|
+
severity: "warning",
|
|
85
|
+
summary: `No deployment-mode plugin is registered for "${project.pages.deployMode.id}".`,
|
|
86
|
+
detail: "Init can still prepare local files, but it cannot describe the chosen publish path in detail.",
|
|
87
|
+
suggestions: [
|
|
88
|
+
"Register the deployment-mode plugin to enrich init planning.",
|
|
89
|
+
"Continue with file initialization and resolve deployment details during doctor or deploy."
|
|
90
|
+
],
|
|
91
|
+
source: "application"
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
steps.push(...assumptions.map((assumption) => ({
|
|
95
|
+
id: `verify:${assumption.code}`,
|
|
96
|
+
kind: "verify",
|
|
97
|
+
title: `Verify ${assumption.code}`,
|
|
98
|
+
subject: assumption.code,
|
|
99
|
+
status: assumption.status,
|
|
100
|
+
expected: assumption.summary,
|
|
101
|
+
...(assumption.detail ? { detail: assumption.detail } : {})
|
|
102
|
+
})));
|
|
103
|
+
if (steps.length === 0) {
|
|
104
|
+
steps.push({
|
|
105
|
+
id: "note:init:no-op",
|
|
106
|
+
kind: "note",
|
|
107
|
+
title: "No init changes planned",
|
|
108
|
+
category: "setup",
|
|
109
|
+
message: "The init planner produced no file or setup actions for the current inputs."
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return createCommandPlan({
|
|
113
|
+
command: "init",
|
|
114
|
+
summary: "Prepare the repository shape and baseline assumptions for Codeberg Pages.",
|
|
115
|
+
project,
|
|
116
|
+
assumptions,
|
|
117
|
+
diagnostics,
|
|
118
|
+
steps
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function templatePlanToStep(template) {
|
|
122
|
+
if (template.kind === "directory") {
|
|
123
|
+
return {
|
|
124
|
+
id: `ensure-directory:${template.path}`,
|
|
125
|
+
kind: "ensure-directory",
|
|
126
|
+
title: `Ensure directory ${template.path}`,
|
|
127
|
+
path: template.path,
|
|
128
|
+
reason: template.reason
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
id: `write-file:${template.path}`,
|
|
133
|
+
kind: "write-file",
|
|
134
|
+
title: `Write ${template.path}`,
|
|
135
|
+
path: template.path,
|
|
136
|
+
content: template.content,
|
|
137
|
+
mode: template.mode,
|
|
138
|
+
reason: template.reason
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ProjectInspection } from "../contracts/ports/project-inspector-port.js";
|
|
2
|
+
import type { ProjectContext } from "../contracts/project-context.js";
|
|
3
|
+
import type { EnvironmentSnapshot } from "../../core/entities/environment.js";
|
|
4
|
+
export interface CreateProjectContextInput {
|
|
5
|
+
inspection: ProjectInspection;
|
|
6
|
+
environment: EnvironmentSnapshot;
|
|
7
|
+
}
|
|
8
|
+
export declare function createProjectContext(input: CreateProjectContextInput): ProjectContext;
|