@podosoft/podokit 0.16.4 → 0.17.1
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/dist/deploy-compose-exec.d.ts +93 -0
- package/dist/deploy-compose-exec.js +198 -0
- package/dist/deploy-compose-profile.d.ts +107 -0
- package/dist/deploy-compose-profile.js +361 -0
- package/dist/deploy-compose-render.d.ts +48 -0
- package/dist/deploy-compose-render.js +333 -0
- package/dist/deploy-compose-sync.d.ts +144 -0
- package/dist/deploy-compose-sync.js +412 -0
- package/dist/deploy-compose.d.ts +98 -0
- package/dist/deploy-compose.js +667 -0
- package/dist/deploy-dispatch.d.ts +33 -0
- package/dist/deploy-dispatch.js +58 -0
- package/dist/deploy-driver.d.ts +25 -0
- package/dist/deploy-driver.js +78 -0
- package/dist/deploy-profile.d.ts +3 -10
- package/dist/deploy-profile.js +82 -291
- package/dist/deploy-schema.d.ts +56 -0
- package/dist/deploy-schema.js +261 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +178 -6
- package/dist/lib.d.ts +9 -0
- package/dist/lib.js +30 -1
- package/dist/templates/fullstack-nest-svelte/AGENTS.md +2 -1
- package/dist/templates/fullstack-nest-svelte/Dockerfile.dev +34 -3
- package/dist/templates/fullstack-nest-svelte/apps/api/Dockerfile +68 -7
- package/dist/templates/fullstack-nest-svelte/apps/web/Dockerfile +60 -8
- package/dist/templates/fullstack-nest-svelte/apps/web/server.js +31 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/backend-proxy.test.ts +21 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/backend-proxy.ts +15 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/ws-upgrade.js +267 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/server/ws-upgrade.test.js +134 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/vite.config.ts +17 -7
- package/dist/templates/fullstack-nest-svelte/dot-agents/skills/podokit-deploy/SKILL.md +55 -10
- package/dist/templates/fullstack-nest-svelte/dot-agents/skills/podokit-deploy/references/profile.md +50 -1
- package/dist/templates/fullstack-nest-svelte/dot-agents/skills/podokit-deploy/references/release.md +64 -0
- package/dist/templates/fullstack-nest-svelte/dot-agents/skills/podokit-deploy-fast/SKILL.md +92 -0
- package/dist/templates/fullstack-nest-svelte/dot-claude/skills/podokit-deploy/SKILL.md +1 -1
- package/dist/templates/fullstack-nest-svelte/dot-claude/skills/podokit-deploy-fast/SKILL.md +8 -0
- package/dist/templates/fullstack-nest-svelte/dot-dockerignore +7 -0
- package/dist/templates/fullstack-nest-svelte/dot-env.example +7 -0
- package/dist/templates/fullstack-nest-svelte/dot-github/workflows/release.yml +142 -0
- package/dist/templates/fullstack-nest-svelte/infra/k3s/README.md +18 -0
- package/dist/templates/fullstack-nest-svelte/infra/k3s/api-deployment.yaml +6 -1
- package/dist/templates/fullstack-nest-svelte/infra/k3s/ingress.yaml +8 -0
- package/dist/templates/fullstack-nest-svelte/infra/k3s/web-deployment.yaml +11 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/components/account-page.svelte +15 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/device-label.test.ts +62 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/device-label.ts +50 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/catalogs/admin-dashboard/en.json +5 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/lib/i18n/catalogs/admin-dashboard/ko.json +5 -0
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(admin)/admin/sessions/+page.svelte +3 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(admin)/admin/users/+page.svelte +3 -2
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/reset-password/+page.svelte +12 -1
- package/dist/templates/modules/admin-dashboard/files/apps/web/src/routes/(auth)/signup/+page.svelte +13 -1
- package/dist/templates/modules/admin-dashboard/files/tests/helpers/accounts.ts +27 -2
- package/dist/templates/modules/admin-dashboard/files/tests/ui/account.ui.spec.ts +20 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/auth.ui.spec.ts +26 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/email.ui.spec.ts +10 -1
- package/dist/templates/modules/admin-dashboard/files/tests/ui/two-factor.ui.spec.ts +3 -3
- package/dist/templates/modules/bullmq/files/infra/k3s/worker-deployment.yaml +2 -1
- package/dist/templates/todo/AGENTS.md +2 -1
- package/dist/templates/todo/Dockerfile.dev +34 -3
- package/dist/templates/todo/apps/api/Dockerfile +68 -7
- package/dist/templates/todo/apps/web/Dockerfile +60 -8
- package/dist/templates/todo/apps/web/server.js +31 -0
- package/dist/templates/todo/apps/web/src/lib/server/backend-proxy.test.ts +21 -0
- package/dist/templates/todo/apps/web/src/lib/server/backend-proxy.ts +15 -1
- package/dist/templates/todo/apps/web/src/lib/server/ws-upgrade.js +267 -0
- package/dist/templates/todo/apps/web/src/lib/server/ws-upgrade.test.js +134 -0
- package/dist/templates/todo/apps/web/vite.config.ts +17 -7
- package/dist/templates/todo/dot-agents/skills/podokit-deploy/SKILL.md +55 -10
- package/dist/templates/todo/dot-agents/skills/podokit-deploy/references/profile.md +47 -0
- package/dist/templates/todo/dot-agents/skills/podokit-deploy/references/release.md +64 -0
- package/dist/templates/todo/dot-agents/skills/podokit-deploy-fast/SKILL.md +92 -0
- package/dist/templates/todo/dot-claude/skills/podokit-deploy/SKILL.md +1 -1
- package/dist/templates/todo/dot-claude/skills/podokit-deploy-fast/SKILL.md +8 -0
- package/dist/templates/todo/dot-dockerignore +7 -0
- package/dist/templates/todo/dot-env.example +7 -0
- package/dist/templates/todo/dot-github/workflows/release.yml +142 -0
- package/dist/templates/todo/infra/k3s/README.md +18 -0
- package/dist/templates/todo/infra/k3s/api-deployment.yaml +6 -1
- package/dist/templates/todo/infra/k3s/ingress.yaml +8 -0
- package/dist/templates/todo/infra/k3s/web-deployment.yaml +11 -1
- package/package.json +1 -1
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MANAGED_RUNTIME_CONFIG_KEYS = exports.STABLE_SEMVER_TAG_PATTERN = exports.SENSITIVE_KEY = exports.DNS_LABEL = exports.PROFILE_NAME = void 0;
|
|
4
|
+
exports.containsUrlCredentials = containsUrlCredentials;
|
|
5
|
+
exports.isRecord = isRecord;
|
|
6
|
+
exports.assertOnlyKeys = assertOnlyKeys;
|
|
7
|
+
exports.requiredRecord = requiredRecord;
|
|
8
|
+
exports.requiredString = requiredString;
|
|
9
|
+
exports.optionalString = optionalString;
|
|
10
|
+
exports.requiredNumber = requiredNumber;
|
|
11
|
+
exports.parseRequiredKeys = parseRequiredKeys;
|
|
12
|
+
exports.parseCommand = parseCommand;
|
|
13
|
+
exports.validateDnsLabel = validateDnsLabel;
|
|
14
|
+
exports.validateDnsSubdomain = validateDnsSubdomain;
|
|
15
|
+
exports.parseImageReference = parseImageReference;
|
|
16
|
+
exports.validateImage = validateImage;
|
|
17
|
+
exports.validateImageRepository = validateImageRepository;
|
|
18
|
+
exports.parseReleaseProfile = parseReleaseProfile;
|
|
19
|
+
exports.parseRuntimeConfig = parseRuntimeConfig;
|
|
20
|
+
exports.validateHttpsOrigin = validateHttpsOrigin;
|
|
21
|
+
exports.parseVerification = parseVerification;
|
|
22
|
+
exports.profileDirectory = profileDirectory;
|
|
23
|
+
exports.profilePath = profilePath;
|
|
24
|
+
const node_path_1 = require("node:path");
|
|
25
|
+
exports.PROFILE_NAME = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
26
|
+
exports.DNS_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
27
|
+
const IMAGE_NAME = /^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*(?::[1-9][0-9]{0,4})?\/)?(?:[a-z0-9]+(?:[._-][a-z0-9]+)*\/)*[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
|
|
28
|
+
const IMAGE_TAG = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/;
|
|
29
|
+
exports.SENSITIVE_KEY = /(password|passphrase|secret|token|private.?key|credential|access.?key|api.?keys?|signing.?key|encryption.?key)/i;
|
|
30
|
+
exports.STABLE_SEMVER_TAG_PATTERN = "^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$";
|
|
31
|
+
/** Keys PodoKit derives itself, so a profile may not set them. */
|
|
32
|
+
exports.MANAGED_RUNTIME_CONFIG_KEYS = ["PORT", "BACKEND_INTERNAL_URL"];
|
|
33
|
+
function containsUrlCredentials(value) {
|
|
34
|
+
if (!value.includes("://"))
|
|
35
|
+
return false;
|
|
36
|
+
try {
|
|
37
|
+
const parsed = new URL(value);
|
|
38
|
+
return parsed.username !== "" || parsed.password !== "";
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function isRecord(value) {
|
|
45
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
46
|
+
}
|
|
47
|
+
function assertOnlyKeys(value, allowed, field) {
|
|
48
|
+
const unexpected = Object.keys(value).filter((key) => !allowed.includes(key));
|
|
49
|
+
if (unexpected.length) {
|
|
50
|
+
throw new Error(`Deployment profile field "${field}" contains unknown key(s): ${unexpected.join(", ")}.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function requiredRecord(parent, key) {
|
|
54
|
+
const value = parent[key];
|
|
55
|
+
if (!isRecord(value))
|
|
56
|
+
throw new Error(`Deployment profile field "${key}" must be an object.`);
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
function requiredString(parent, key) {
|
|
60
|
+
const value = parent[key];
|
|
61
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
62
|
+
throw new Error(`Deployment profile field "${key}" must be a non-empty string.`);
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
function optionalString(parent, key) {
|
|
67
|
+
const value = parent[key];
|
|
68
|
+
if (value === null || value === undefined)
|
|
69
|
+
return null;
|
|
70
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
71
|
+
throw new Error(`Deployment profile field "${key}" must be a string or null.`);
|
|
72
|
+
}
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
function requiredNumber(parent, key) {
|
|
76
|
+
const value = parent[key];
|
|
77
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
78
|
+
throw new Error(`Deployment profile field "${key}" must be a positive integer.`);
|
|
79
|
+
}
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
function parseRequiredKeys(value, field) {
|
|
83
|
+
if (!Array.isArray(value)) {
|
|
84
|
+
throw new Error(`Deployment profile field "${field}" must be a string array.`);
|
|
85
|
+
}
|
|
86
|
+
const keys = value.map((entry) => {
|
|
87
|
+
if (typeof entry !== "string" || !/^[A-Z][A-Z0-9_]*$/.test(entry)) {
|
|
88
|
+
throw new Error(`Deployment profile field "${field}" contains an invalid environment key.`);
|
|
89
|
+
}
|
|
90
|
+
return entry;
|
|
91
|
+
});
|
|
92
|
+
if (new Set(keys).size !== keys.length) {
|
|
93
|
+
throw new Error(`Deployment profile field "${field}" contains duplicate keys.`);
|
|
94
|
+
}
|
|
95
|
+
return keys.sort();
|
|
96
|
+
}
|
|
97
|
+
function parseCommand(value, field) {
|
|
98
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
99
|
+
throw new Error(`Deployment profile field "${field}" must be a non-empty string array.`);
|
|
100
|
+
}
|
|
101
|
+
return value.map((entry, index) => {
|
|
102
|
+
if (typeof entry !== "string" || entry.trim() === "") {
|
|
103
|
+
throw new Error(`Deployment profile field "${field}[${index}]" must be a non-empty string.`);
|
|
104
|
+
}
|
|
105
|
+
if (/[\u0000-\u001f\u007f]/.test(entry)) {
|
|
106
|
+
throw new Error(`Deployment profile field "${field}[${index}]" must not contain control characters.`);
|
|
107
|
+
}
|
|
108
|
+
return entry;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function validateDnsLabel(value, field) {
|
|
112
|
+
if (!exports.DNS_LABEL.test(value)) {
|
|
113
|
+
throw new Error(`Deployment profile field "${field}" must be a Kubernetes DNS label.`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function validateDnsSubdomain(value, field) {
|
|
117
|
+
if (value.length > 253 || !value.split(".").every((label) => exports.DNS_LABEL.test(label))) {
|
|
118
|
+
throw new Error(`Deployment profile field "${field}" must be a Kubernetes DNS subdomain.`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function parseImageReference(value) {
|
|
122
|
+
if (value.length > 512 || value.includes("://") || /[\s\u0000-\u001f\u007f]/.test(value)) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const at = value.indexOf("@");
|
|
126
|
+
if (at !== -1 && at !== value.lastIndexOf("@"))
|
|
127
|
+
return null;
|
|
128
|
+
const digest = at === -1 ? null : value.slice(at + 1);
|
|
129
|
+
const tagged = at === -1 ? value : value.slice(0, at);
|
|
130
|
+
const lastSlash = tagged.lastIndexOf("/");
|
|
131
|
+
const lastColon = tagged.lastIndexOf(":");
|
|
132
|
+
const hasTag = lastColon > lastSlash;
|
|
133
|
+
const name = hasTag ? tagged.slice(0, lastColon) : tagged;
|
|
134
|
+
const tag = hasTag ? tagged.slice(lastColon + 1) : null;
|
|
135
|
+
if (!IMAGE_NAME.test(name) ||
|
|
136
|
+
(tag !== null && !IMAGE_TAG.test(tag)) ||
|
|
137
|
+
(digest !== null && !/^sha256:[a-f0-9]{64}$/.test(digest))) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
return { name, tag, digest };
|
|
141
|
+
}
|
|
142
|
+
function validateImage(value, field) {
|
|
143
|
+
const parsed = parseImageReference(value);
|
|
144
|
+
if (!parsed || parsed.tag === "latest" || (!parsed.tag && !parsed.digest)) {
|
|
145
|
+
throw new Error(`Deployment profile field "${field}" must use an explicit non-latest image tag or digest.`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function validateImageRepository(value, field) {
|
|
149
|
+
const parsed = parseImageReference(value);
|
|
150
|
+
if (!parsed || parsed.tag || parsed.digest) {
|
|
151
|
+
throw new Error(`Deployment profile field "${field}" must be an image repository without a tag or digest.`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function parseReleaseProfile(release) {
|
|
155
|
+
assertOnlyKeys(release, ["strategy", "tagPattern", "apiRepository", "webRepository"], "release");
|
|
156
|
+
const tagPattern = requiredString(release, "tagPattern");
|
|
157
|
+
if (tagPattern !== exports.STABLE_SEMVER_TAG_PATTERN) {
|
|
158
|
+
throw new Error("Deployment release.tagPattern must require stable vMAJOR.MINOR.PATCH tags.");
|
|
159
|
+
}
|
|
160
|
+
if (release.strategy !== "shared-tag") {
|
|
161
|
+
throw new Error("Deployment release.strategy must be shared-tag.");
|
|
162
|
+
}
|
|
163
|
+
const apiRepository = requiredString(release, "apiRepository");
|
|
164
|
+
const webRepository = requiredString(release, "webRepository");
|
|
165
|
+
validateImageRepository(apiRepository, "release.apiRepository");
|
|
166
|
+
validateImageRepository(webRepository, "release.webRepository");
|
|
167
|
+
return { strategy: "shared-tag", tagPattern, apiRepository, webRepository };
|
|
168
|
+
}
|
|
169
|
+
function parseRuntimeConfig(runtimeConfig) {
|
|
170
|
+
const parsed = {};
|
|
171
|
+
for (const [key, entry] of Object.entries(runtimeConfig)) {
|
|
172
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(key)) {
|
|
173
|
+
throw new Error(`Deployment runtimeConfig key "${key}" must be an uppercase environment name.`);
|
|
174
|
+
}
|
|
175
|
+
if (exports.SENSITIVE_KEY.test(key)) {
|
|
176
|
+
throw new Error(`Deployment runtimeConfig key "${key}" may contain a secret; use a secret store.`);
|
|
177
|
+
}
|
|
178
|
+
if (exports.MANAGED_RUNTIME_CONFIG_KEYS.includes(key)) {
|
|
179
|
+
throw new Error(`Deployment runtimeConfig key "${key}" is managed by PodoKit.`);
|
|
180
|
+
}
|
|
181
|
+
if (typeof entry !== "string") {
|
|
182
|
+
throw new Error(`Deployment runtimeConfig value "${key}" must be a string.`);
|
|
183
|
+
}
|
|
184
|
+
if (containsUrlCredentials(entry)) {
|
|
185
|
+
throw new Error(`Deployment runtimeConfig value "${key}" contains URL credentials; use a secret store.`);
|
|
186
|
+
}
|
|
187
|
+
parsed[key] = entry;
|
|
188
|
+
}
|
|
189
|
+
return parsed;
|
|
190
|
+
}
|
|
191
|
+
function validateHttpsOrigin(value) {
|
|
192
|
+
let parsed;
|
|
193
|
+
try {
|
|
194
|
+
parsed = new URL(value);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
throw new Error(`Deployment verification baseUrl "${value}" is invalid.`);
|
|
198
|
+
}
|
|
199
|
+
if (parsed.username || parsed.password) {
|
|
200
|
+
throw new Error("Deployment verification baseUrl must not contain URL credentials; use a credential-free HTTPS origin.");
|
|
201
|
+
}
|
|
202
|
+
if (parsed.protocol !== "https:" || parsed.pathname !== "/" || parsed.search || parsed.hash) {
|
|
203
|
+
throw new Error("Deployment verification baseUrl must be an HTTPS origin without a path.");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function parseVerification(verification) {
|
|
207
|
+
assertOnlyKeys(verification, ["baseUrl", "checks"], "verification");
|
|
208
|
+
const baseUrl = requiredString(verification, "baseUrl");
|
|
209
|
+
validateHttpsOrigin(baseUrl);
|
|
210
|
+
const baseOrigin = new URL(baseUrl).origin;
|
|
211
|
+
const checksValue = verification.checks;
|
|
212
|
+
if (!Array.isArray(checksValue) || checksValue.length === 0) {
|
|
213
|
+
throw new Error("Deployment verification.checks must be a non-empty array.");
|
|
214
|
+
}
|
|
215
|
+
const checks = checksValue.map((entry, index) => {
|
|
216
|
+
if (!isRecord(entry)) {
|
|
217
|
+
throw new Error(`Deployment verification.checks[${index}] must be an object.`);
|
|
218
|
+
}
|
|
219
|
+
assertOnlyKeys(entry, ["path", "expectedStatus", "expectedJson"], `verification.checks[${index}]`);
|
|
220
|
+
const path = requiredString(entry, "path");
|
|
221
|
+
if (!path.startsWith("/") ||
|
|
222
|
+
path.startsWith("//") ||
|
|
223
|
+
path.includes("\\") ||
|
|
224
|
+
/[\u0000-\u001f\u007f]/.test(path) ||
|
|
225
|
+
new URL(path, baseUrl).origin !== baseOrigin) {
|
|
226
|
+
throw new Error("Every deployment verification path must be an absolute URL path.");
|
|
227
|
+
}
|
|
228
|
+
const expectedStatus = requiredNumber(entry, "expectedStatus");
|
|
229
|
+
if (expectedStatus < 100 || expectedStatus > 599) {
|
|
230
|
+
throw new Error("Deployment verification expectedStatus must be an HTTP status.");
|
|
231
|
+
}
|
|
232
|
+
const expectedJsonValue = entry.expectedJson;
|
|
233
|
+
let expectedJson;
|
|
234
|
+
if (expectedJsonValue !== undefined) {
|
|
235
|
+
if (!isRecord(expectedJsonValue)) {
|
|
236
|
+
throw new Error("Deployment verification expectedJson must be an object.");
|
|
237
|
+
}
|
|
238
|
+
expectedJson = {};
|
|
239
|
+
for (const [key, value] of Object.entries(expectedJsonValue)) {
|
|
240
|
+
if (value !== null &&
|
|
241
|
+
typeof value !== "string" &&
|
|
242
|
+
typeof value !== "number" &&
|
|
243
|
+
typeof value !== "boolean") {
|
|
244
|
+
throw new Error("Deployment verification expectedJson values must be scalar.");
|
|
245
|
+
}
|
|
246
|
+
expectedJson[key] = value;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return { path, expectedStatus, ...(expectedJson ? { expectedJson } : {}) };
|
|
250
|
+
});
|
|
251
|
+
return { baseUrl, checks };
|
|
252
|
+
}
|
|
253
|
+
function profileDirectory(projectRoot) {
|
|
254
|
+
return (0, node_path_1.join)((0, node_path_1.resolve)(projectRoot), ".podokit", "deploy");
|
|
255
|
+
}
|
|
256
|
+
function profilePath(projectRoot, name) {
|
|
257
|
+
if (!exports.PROFILE_NAME.test(name)) {
|
|
258
|
+
throw new Error(`Invalid deployment profile name "${name}".`);
|
|
259
|
+
}
|
|
260
|
+
return (0, node_path_1.join)(profileDirectory(projectRoot), `${name}.json`);
|
|
261
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { type PackageManager } from "./create";
|
|
3
3
|
import { type LocaleDirection } from "./locale";
|
|
4
|
+
import { type DeployDriver } from "./deploy-driver";
|
|
4
5
|
interface ParsedArgs {
|
|
5
6
|
command?: string;
|
|
6
7
|
name?: string;
|
|
@@ -22,8 +23,14 @@ interface ParsedArgs {
|
|
|
22
23
|
revision?: number;
|
|
23
24
|
context?: string;
|
|
24
25
|
clusterFingerprint?: string;
|
|
26
|
+
endpointFingerprint?: string;
|
|
27
|
+
driver?: DeployDriver;
|
|
28
|
+
secretsDir?: string;
|
|
25
29
|
host?: string;
|
|
26
30
|
json: boolean;
|
|
31
|
+
build: boolean;
|
|
32
|
+
clean: boolean;
|
|
33
|
+
revert: boolean;
|
|
27
34
|
}
|
|
28
35
|
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
29
36
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,11 @@ const locale_1 = require("./locale");
|
|
|
17
17
|
const deploy_1 = require("./deploy");
|
|
18
18
|
const deploy_profile_1 = require("./deploy-profile");
|
|
19
19
|
const deploy_render_1 = require("./deploy-render");
|
|
20
|
+
const deploy_driver_1 = require("./deploy-driver");
|
|
21
|
+
const deploy_compose_profile_1 = require("./deploy-compose-profile");
|
|
22
|
+
const deploy_compose_render_1 = require("./deploy-compose-render");
|
|
23
|
+
const deploy_compose_1 = require("./deploy-compose");
|
|
24
|
+
const deploy_compose_sync_1 = require("./deploy-compose-sync");
|
|
20
25
|
const HELP = `podo — PodoKit project generator
|
|
21
26
|
|
|
22
27
|
Usage:
|
|
@@ -27,7 +32,8 @@ Usage:
|
|
|
27
32
|
podo diff List PodoKit-managed files you have edited
|
|
28
33
|
podo doctor Check framework versions against supported ranges
|
|
29
34
|
podo dev <action> [...] Run the shared, portless container development gateway
|
|
30
|
-
podo deploy <action> Plan, apply, verify, or roll back a
|
|
35
|
+
podo deploy <action> Plan, apply, verify, or roll back a deployment
|
|
36
|
+
(Kubernetes/Helm or Docker Compose)
|
|
31
37
|
podo locale <command> Add, validate, activate, or list JSON locales
|
|
32
38
|
podo update [--apply] Preview (or apply) what a version update would change
|
|
33
39
|
podo eject <path...> Take ownership of managed files (update skips them)
|
|
@@ -59,10 +65,11 @@ Example:
|
|
|
59
65
|
npx @podosoft/podokit create my-app --template todo
|
|
60
66
|
cd my-app && npx @podosoft/podokit add auth
|
|
61
67
|
`;
|
|
62
|
-
const DEPLOY_HELP = `podo deploy — Kubernetes
|
|
68
|
+
const DEPLOY_HELP = `podo deploy — release an application to Kubernetes or Docker Compose
|
|
63
69
|
|
|
64
70
|
Usage:
|
|
65
|
-
podo deploy init --profile <name> --
|
|
71
|
+
podo deploy init --profile <name> [--driver <driver>] --context <context>
|
|
72
|
+
[--host <hostname>] [--secrets-dir <path>]
|
|
66
73
|
podo deploy doctor --profile <name> [--json]
|
|
67
74
|
podo deploy render --profile <name> --release <tag> [--json]
|
|
68
75
|
podo deploy plan --profile <name> --release <tag> [--json]
|
|
@@ -70,10 +77,24 @@ Usage:
|
|
|
70
77
|
podo deploy status --profile <name> [--json]
|
|
71
78
|
podo deploy verify --profile <name> [--json]
|
|
72
79
|
podo deploy rollback --profile <name> --revision <number> [--confirm <plan-hash>]
|
|
80
|
+
podo deploy sync --profile <name> [--build] [--clean] [--revert] [--json]
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
Drivers:
|
|
83
|
+
kubernetes-helm (default) Helm releases on an existing cluster. --context is a
|
|
84
|
+
kubeconfig context; the namespace, Secrets, IngressClass, and
|
|
85
|
+
storage classes must already exist.
|
|
86
|
+
docker-compose A Compose project on one Docker host. --context is a Docker
|
|
87
|
+
context, local or ssh://; the env files named by the profile
|
|
88
|
+
must already exist on that host.
|
|
89
|
+
|
|
90
|
+
A profile records which driver it uses, so every action after init reads it from
|
|
91
|
+
the profile. Both drivers pin the target by fingerprint, resolve image tags to
|
|
92
|
+
digests, and require apply and rollback to confirm the exact plan hash.
|
|
93
|
+
|
|
94
|
+
sync is a development shortcut for the docker-compose driver only. It copies local
|
|
95
|
+
build output into the running containers and restarts them, so the deployment runs
|
|
96
|
+
code its image tag does not describe until the next apply recreates the containers.
|
|
97
|
+
It never runs migrations and refuses when runtime dependencies have changed.
|
|
77
98
|
`;
|
|
78
99
|
function parseArgs(argv) {
|
|
79
100
|
const parsed = {
|
|
@@ -83,6 +104,9 @@ function parseArgs(argv) {
|
|
|
83
104
|
adopt: false,
|
|
84
105
|
ai: true,
|
|
85
106
|
json: false,
|
|
107
|
+
build: false,
|
|
108
|
+
clean: false,
|
|
109
|
+
revert: false,
|
|
86
110
|
positionals: [],
|
|
87
111
|
};
|
|
88
112
|
const positionals = [];
|
|
@@ -147,12 +171,34 @@ function parseArgs(argv) {
|
|
|
147
171
|
else if (arg === "--cluster-fingerprint") {
|
|
148
172
|
parsed.clusterFingerprint = argv[++i];
|
|
149
173
|
}
|
|
174
|
+
else if (arg === "--endpoint-fingerprint") {
|
|
175
|
+
parsed.endpointFingerprint = argv[++i];
|
|
176
|
+
}
|
|
177
|
+
else if (arg === "--driver") {
|
|
178
|
+
const driver = argv[++i];
|
|
179
|
+
if (driver !== "kubernetes-helm" && driver !== "docker-compose") {
|
|
180
|
+
throw new Error(`--driver must be one of: ${deploy_driver_1.DEPLOY_DRIVERS.join(", ")}.`);
|
|
181
|
+
}
|
|
182
|
+
parsed.driver = driver;
|
|
183
|
+
}
|
|
184
|
+
else if (arg === "--secrets-dir") {
|
|
185
|
+
parsed.secretsDir = argv[++i];
|
|
186
|
+
}
|
|
150
187
|
else if (arg === "--host") {
|
|
151
188
|
parsed.host = argv[++i];
|
|
152
189
|
}
|
|
153
190
|
else if (arg === "--json") {
|
|
154
191
|
parsed.json = true;
|
|
155
192
|
}
|
|
193
|
+
else if (arg === "--build") {
|
|
194
|
+
parsed.build = true;
|
|
195
|
+
}
|
|
196
|
+
else if (arg === "--clean") {
|
|
197
|
+
parsed.clean = true;
|
|
198
|
+
}
|
|
199
|
+
else if (arg === "--revert") {
|
|
200
|
+
parsed.revert = true;
|
|
201
|
+
}
|
|
156
202
|
else if (arg !== undefined && !arg.startsWith("-")) {
|
|
157
203
|
positionals.push(arg);
|
|
158
204
|
}
|
|
@@ -166,6 +212,118 @@ function fail(message) {
|
|
|
166
212
|
process.stderr.write(`error: ${message}\n`);
|
|
167
213
|
process.exit(1);
|
|
168
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* The docker-compose driver's half of `podo deploy`. It answers the same actions as
|
|
217
|
+
* the Kubernetes half and enforces the same rule: every mutation echoes back the
|
|
218
|
+
* exact hash of a plan that was printed first.
|
|
219
|
+
*/
|
|
220
|
+
async function runComposeDeploy(action, args, profileName) {
|
|
221
|
+
const cwd = process.cwd();
|
|
222
|
+
if (action === "init") {
|
|
223
|
+
if (!args.context) {
|
|
224
|
+
fail("Usage: podo deploy init --profile <name> --driver docker-compose --context <docker-context> [--host <hostname>] [--secrets-dir <path>].");
|
|
225
|
+
}
|
|
226
|
+
const endpointFingerprint = args.endpointFingerprint ?? (0, deploy_compose_1.inspectComposeEndpointFingerprint)(args.context);
|
|
227
|
+
const initialized = (0, deploy_compose_profile_1.initializeComposeProfile)(cwd, profileName, {
|
|
228
|
+
context: args.context,
|
|
229
|
+
endpointFingerprint,
|
|
230
|
+
host: args.host,
|
|
231
|
+
secretsDirectory: args.secretsDir,
|
|
232
|
+
});
|
|
233
|
+
process.stdout.write(args.json
|
|
234
|
+
? `${JSON.stringify(initialized, null, 2)}\n`
|
|
235
|
+
: `Created deployment profile ${initialized.name} at ${initialized.path}.\n`);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (action === "doctor") {
|
|
239
|
+
// A release makes the doctor probe with the image about to run, and lets it
|
|
240
|
+
// check registry access at all.
|
|
241
|
+
const findings = (0, deploy_compose_1.doctorComposeDeployment)(cwd, profileName, undefined, args.release);
|
|
242
|
+
process.stdout.write(args.json
|
|
243
|
+
? `${JSON.stringify(findings, null, 2)}\n`
|
|
244
|
+
: `${findings.map((f) => `${f.ok ? "ok " : "FAIL"} ${f.message}`).join("\n")}\n`);
|
|
245
|
+
if (findings.some((finding) => !finding.ok))
|
|
246
|
+
process.exitCode = 1;
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (action === "render") {
|
|
250
|
+
if (!args.release)
|
|
251
|
+
fail("podo deploy render requires --release <tag>.");
|
|
252
|
+
const profile = (0, deploy_driver_1.loadComposeProfile)(cwd, profileName);
|
|
253
|
+
const runtime = (0, deploy_compose_render_1.renderComposeDeployment)(cwd, profileName, profile, args.release);
|
|
254
|
+
process.stdout.write(args.json
|
|
255
|
+
? `${JSON.stringify(runtime, null, 2)}\n`
|
|
256
|
+
: `Rendered Compose project into ${runtime.root}.\n`);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (action === "plan") {
|
|
260
|
+
if (!args.release)
|
|
261
|
+
fail("podo deploy plan requires --release <tag>.");
|
|
262
|
+
const plan = (0, deploy_compose_1.planComposeDeployment)(cwd, profileName, args.release);
|
|
263
|
+
process.stdout.write(args.json
|
|
264
|
+
? `${JSON.stringify(plan, null, 2)}\n`
|
|
265
|
+
: `${plan.actions.map((entry) => `${entry.order}. ${entry.description}`).join("\n")}\n` +
|
|
266
|
+
`${plan.warnings.map((warning) => `warning: ${warning}`).join("\n")}\n` +
|
|
267
|
+
`Plan hash: ${plan.planHash}\n`);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (action === "apply") {
|
|
271
|
+
if (!args.release || !args.confirm) {
|
|
272
|
+
fail("podo deploy apply requires --release <tag> --confirm <plan-hash>.");
|
|
273
|
+
}
|
|
274
|
+
const result = await (0, deploy_compose_1.applyComposeDeployment)(cwd, profileName, args.release, args.confirm);
|
|
275
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
276
|
+
if (!result.verification.ok)
|
|
277
|
+
process.exitCode = 1;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (action === "status") {
|
|
281
|
+
process.stdout.write(`${JSON.stringify((0, deploy_compose_1.getComposeStatus)(cwd, profileName), null, 2)}\n`);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (action === "verify") {
|
|
285
|
+
const result = await (0, deploy_compose_1.verifyComposeDeployment)(cwd, profileName);
|
|
286
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
287
|
+
if (!result.ok)
|
|
288
|
+
process.exitCode = 1;
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (action === "rollback") {
|
|
292
|
+
if (!args.revision)
|
|
293
|
+
fail("podo deploy rollback requires --revision <number>.");
|
|
294
|
+
if (!args.confirm) {
|
|
295
|
+
const plan = (0, deploy_compose_1.planComposeRollback)(cwd, profileName, args.revision);
|
|
296
|
+
process.stdout.write(`${JSON.stringify(plan, null, 2)}\nRe-run with --confirm ${plan.planHash}.\n`);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const result = await (0, deploy_compose_1.rollbackComposeDeployment)(cwd, profileName, args.revision, args.confirm);
|
|
300
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
301
|
+
if (!result.verification.ok)
|
|
302
|
+
process.exitCode = 1;
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (action === "sync") {
|
|
306
|
+
if (args.revert) {
|
|
307
|
+
const reverted = await (0, deploy_compose_sync_1.revertComposeSync)(cwd, profileName);
|
|
308
|
+
process.stdout.write(args.json
|
|
309
|
+
? `${JSON.stringify(reverted, null, 2)}\n`
|
|
310
|
+
: `Recreated ${reverted.services.join(", ")} from the applied Compose project.\n`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const result = await (0, deploy_compose_sync_1.syncComposeDeployment)(cwd, profileName, {
|
|
314
|
+
build: args.build,
|
|
315
|
+
clean: args.clean,
|
|
316
|
+
});
|
|
317
|
+
process.stdout.write(args.json
|
|
318
|
+
? `${JSON.stringify(result, null, 2)}\n`
|
|
319
|
+
: `${result.plan.warnings.map((warning) => `warning: ${warning}`).join("\n")}\n` +
|
|
320
|
+
`Copied ${result.plan.artifacts.length} artifact(s) into ${result.restarted.join(", ")}.\n` +
|
|
321
|
+
"This deployment now runs code its image tag does not describe; " +
|
|
322
|
+
"podo deploy sync --revert restores the image.\n");
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
fail(`Unknown deploy command "${action}". Use init, doctor, render, plan, apply, status, verify, rollback, or sync.`);
|
|
326
|
+
}
|
|
169
327
|
async function main(argv) {
|
|
170
328
|
const args = parseArgs(argv);
|
|
171
329
|
if (args.help || !args.command) {
|
|
@@ -300,6 +458,14 @@ async function main(argv) {
|
|
|
300
458
|
fail("Deployment commands require --profile <name>.");
|
|
301
459
|
}
|
|
302
460
|
try {
|
|
461
|
+
// A profile names its own driver, so everything but `init` reads it from disk.
|
|
462
|
+
const driver = action === "init"
|
|
463
|
+
? (args.driver ?? "kubernetes-helm")
|
|
464
|
+
: (0, deploy_driver_1.readDeploymentDriver)(process.cwd(), profileName);
|
|
465
|
+
if (driver === "docker-compose") {
|
|
466
|
+
await runComposeDeploy(action, args, profileName);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
303
469
|
if (action === "init") {
|
|
304
470
|
if (!args.context) {
|
|
305
471
|
fail("Usage: podo deploy init --profile <name> --context <context> [--host <hostname>].");
|
|
@@ -376,6 +542,12 @@ async function main(argv) {
|
|
|
376
542
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
377
543
|
return;
|
|
378
544
|
}
|
|
545
|
+
if (action === "sync") {
|
|
546
|
+
// Not a gap to fill later. There is no image to swap artifacts into: a
|
|
547
|
+
// cluster schedules pods across nodes from a registry, so the equivalent
|
|
548
|
+
// shortcut would have to reach every node and would stop being a shortcut.
|
|
549
|
+
fail("podo deploy sync is only available for the docker-compose driver. On a cluster, build and roll out a release.");
|
|
550
|
+
}
|
|
379
551
|
fail(`Unknown deploy command "${action}". Use init, doctor, render, plan, apply, status, verify, or rollback.`);
|
|
380
552
|
}
|
|
381
553
|
catch (err) {
|
package/dist/lib.d.ts
CHANGED
|
@@ -16,6 +16,15 @@ export { assertReleaseTag, deploymentProfileDigest, initializeDeploymentProfile,
|
|
|
16
16
|
export type { DependencyMode, DeployProfileV1, DeploymentProfileSummary, InitializeDeploymentProfileOptions, ObjectStorageDependencyProfile, RedisDependencyProfile, ResourceProfile, StatefulDependencyProfile, WorkloadProfile, } from "./deploy-profile";
|
|
17
17
|
export { renderDeployment } from "./deploy-render";
|
|
18
18
|
export type { DeploymentRuntime } from "./deploy-render";
|
|
19
|
+
export { DEPLOY_DRIVERS, assertAnyReleaseTag, listAnyDeploymentProfiles, loadAnyDeploymentProfile, loadComposeProfile, loadKubernetesProfile, readDeploymentDriver, } from "./deploy-driver";
|
|
20
|
+
export type { AnyDeployProfile, DeployDriver } from "./deploy-driver";
|
|
21
|
+
export { initializeComposeProfile, parseComposeProfile } from "./deploy-compose-profile";
|
|
22
|
+
export type { DockerComposeProfileV1 } from "./deploy-compose-profile";
|
|
23
|
+
export { renderComposeDeployment } from "./deploy-compose-render";
|
|
24
|
+
export type { ComposeRuntime } from "./deploy-compose-render";
|
|
25
|
+
export { applyComposeDeployment, doctorComposeDeployment, getComposeStatus, inspectComposeEndpointFingerprint, planComposeDeployment, planComposeRollback, rollbackComposeDeployment, verifyComposeDeployment, } from "./deploy-compose";
|
|
26
|
+
export type { ComposePlan, ComposeStatus } from "./deploy-compose";
|
|
27
|
+
export { describeDeploymentTarget, doctorAnyDeployment, getAnyDeploymentStatus, planAnyDeployment, verifyAnyDeployment, } from "./deploy-dispatch";
|
|
19
28
|
/** Absolute path to the templates bundled in this installed package. */
|
|
20
29
|
export declare function builtinTemplatesDir(): string;
|
|
21
30
|
/** Absolute path to the modules bundled in this installed package. */
|
package/dist/lib.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.renderDeployment = exports.loadDeploymentProfile = exports.listDeploymentProfiles = exports.initializeDeploymentProfile = exports.deploymentProfileDigest = exports.assertReleaseTag = exports.verifyDeployment = exports.rollbackDeployment = exports.planRollback = exports.planDeployment = exports.inspectClusterFingerprint = exports.getDeploymentStatus = exports.doctorDeployment = exports.applyDeployment = exports.runDevCommand = exports.resolveDevRuntime = exports.renderRuntimeCompose = exports.renderRoute = exports.readDevConfig = exports.summarize = exports.planUpdate = exports.SUPPORTED_FRAMEWORKS = exports.doctor = exports.diff = exports.status = exports.addModule = exports.listModules = exports.isKnownTemplate = exports.TEMPLATE_NAMES = exports.DEFAULT_TEMPLATE = exports.TEMPLATES = exports.create = void 0;
|
|
3
|
+
exports.verifyComposeDeployment = exports.rollbackComposeDeployment = exports.planComposeRollback = exports.planComposeDeployment = exports.inspectComposeEndpointFingerprint = exports.getComposeStatus = exports.doctorComposeDeployment = exports.applyComposeDeployment = exports.renderComposeDeployment = exports.parseComposeProfile = exports.initializeComposeProfile = exports.readDeploymentDriver = exports.loadKubernetesProfile = exports.loadComposeProfile = exports.loadAnyDeploymentProfile = exports.listAnyDeploymentProfiles = exports.assertAnyReleaseTag = exports.DEPLOY_DRIVERS = exports.renderDeployment = exports.loadDeploymentProfile = exports.listDeploymentProfiles = exports.initializeDeploymentProfile = exports.deploymentProfileDigest = exports.assertReleaseTag = exports.verifyDeployment = exports.rollbackDeployment = exports.planRollback = exports.planDeployment = exports.inspectClusterFingerprint = exports.getDeploymentStatus = exports.doctorDeployment = exports.applyDeployment = exports.runDevCommand = exports.resolveDevRuntime = exports.renderRuntimeCompose = exports.renderRoute = exports.readDevConfig = exports.summarize = exports.planUpdate = exports.SUPPORTED_FRAMEWORKS = exports.doctor = exports.diff = exports.status = exports.addModule = exports.listModules = exports.isKnownTemplate = exports.TEMPLATE_NAMES = exports.DEFAULT_TEMPLATE = exports.TEMPLATES = exports.create = void 0;
|
|
4
|
+
exports.verifyAnyDeployment = exports.planAnyDeployment = exports.getAnyDeploymentStatus = exports.doctorAnyDeployment = exports.describeDeploymentTarget = void 0;
|
|
4
5
|
exports.builtinTemplatesDir = builtinTemplatesDir;
|
|
5
6
|
exports.builtinModulesDir = builtinModulesDir;
|
|
6
7
|
// Library entry point: the reusable functions behind the `podo` CLI, for other
|
|
@@ -47,6 +48,34 @@ Object.defineProperty(exports, "listDeploymentProfiles", { enumerable: true, get
|
|
|
47
48
|
Object.defineProperty(exports, "loadDeploymentProfile", { enumerable: true, get: function () { return deploy_profile_1.loadDeploymentProfile; } });
|
|
48
49
|
var deploy_render_1 = require("./deploy-render");
|
|
49
50
|
Object.defineProperty(exports, "renderDeployment", { enumerable: true, get: function () { return deploy_render_1.renderDeployment; } });
|
|
51
|
+
var deploy_driver_1 = require("./deploy-driver");
|
|
52
|
+
Object.defineProperty(exports, "DEPLOY_DRIVERS", { enumerable: true, get: function () { return deploy_driver_1.DEPLOY_DRIVERS; } });
|
|
53
|
+
Object.defineProperty(exports, "assertAnyReleaseTag", { enumerable: true, get: function () { return deploy_driver_1.assertAnyReleaseTag; } });
|
|
54
|
+
Object.defineProperty(exports, "listAnyDeploymentProfiles", { enumerable: true, get: function () { return deploy_driver_1.listAnyDeploymentProfiles; } });
|
|
55
|
+
Object.defineProperty(exports, "loadAnyDeploymentProfile", { enumerable: true, get: function () { return deploy_driver_1.loadAnyDeploymentProfile; } });
|
|
56
|
+
Object.defineProperty(exports, "loadComposeProfile", { enumerable: true, get: function () { return deploy_driver_1.loadComposeProfile; } });
|
|
57
|
+
Object.defineProperty(exports, "loadKubernetesProfile", { enumerable: true, get: function () { return deploy_driver_1.loadKubernetesProfile; } });
|
|
58
|
+
Object.defineProperty(exports, "readDeploymentDriver", { enumerable: true, get: function () { return deploy_driver_1.readDeploymentDriver; } });
|
|
59
|
+
var deploy_compose_profile_1 = require("./deploy-compose-profile");
|
|
60
|
+
Object.defineProperty(exports, "initializeComposeProfile", { enumerable: true, get: function () { return deploy_compose_profile_1.initializeComposeProfile; } });
|
|
61
|
+
Object.defineProperty(exports, "parseComposeProfile", { enumerable: true, get: function () { return deploy_compose_profile_1.parseComposeProfile; } });
|
|
62
|
+
var deploy_compose_render_1 = require("./deploy-compose-render");
|
|
63
|
+
Object.defineProperty(exports, "renderComposeDeployment", { enumerable: true, get: function () { return deploy_compose_render_1.renderComposeDeployment; } });
|
|
64
|
+
var deploy_compose_1 = require("./deploy-compose");
|
|
65
|
+
Object.defineProperty(exports, "applyComposeDeployment", { enumerable: true, get: function () { return deploy_compose_1.applyComposeDeployment; } });
|
|
66
|
+
Object.defineProperty(exports, "doctorComposeDeployment", { enumerable: true, get: function () { return deploy_compose_1.doctorComposeDeployment; } });
|
|
67
|
+
Object.defineProperty(exports, "getComposeStatus", { enumerable: true, get: function () { return deploy_compose_1.getComposeStatus; } });
|
|
68
|
+
Object.defineProperty(exports, "inspectComposeEndpointFingerprint", { enumerable: true, get: function () { return deploy_compose_1.inspectComposeEndpointFingerprint; } });
|
|
69
|
+
Object.defineProperty(exports, "planComposeDeployment", { enumerable: true, get: function () { return deploy_compose_1.planComposeDeployment; } });
|
|
70
|
+
Object.defineProperty(exports, "planComposeRollback", { enumerable: true, get: function () { return deploy_compose_1.planComposeRollback; } });
|
|
71
|
+
Object.defineProperty(exports, "rollbackComposeDeployment", { enumerable: true, get: function () { return deploy_compose_1.rollbackComposeDeployment; } });
|
|
72
|
+
Object.defineProperty(exports, "verifyComposeDeployment", { enumerable: true, get: function () { return deploy_compose_1.verifyComposeDeployment; } });
|
|
73
|
+
var deploy_dispatch_1 = require("./deploy-dispatch");
|
|
74
|
+
Object.defineProperty(exports, "describeDeploymentTarget", { enumerable: true, get: function () { return deploy_dispatch_1.describeDeploymentTarget; } });
|
|
75
|
+
Object.defineProperty(exports, "doctorAnyDeployment", { enumerable: true, get: function () { return deploy_dispatch_1.doctorAnyDeployment; } });
|
|
76
|
+
Object.defineProperty(exports, "getAnyDeploymentStatus", { enumerable: true, get: function () { return deploy_dispatch_1.getAnyDeploymentStatus; } });
|
|
77
|
+
Object.defineProperty(exports, "planAnyDeployment", { enumerable: true, get: function () { return deploy_dispatch_1.planAnyDeployment; } });
|
|
78
|
+
Object.defineProperty(exports, "verifyAnyDeployment", { enumerable: true, get: function () { return deploy_dispatch_1.verifyAnyDeployment; } });
|
|
50
79
|
/** Absolute path to the templates bundled in this installed package. */
|
|
51
80
|
function builtinTemplatesDir() {
|
|
52
81
|
return (0, node_path_1.join)(__dirname, "templates");
|
|
@@ -69,7 +69,8 @@ Useful commands:
|
|
|
69
69
|
- `podo remove <module>` — un-apply a module (inverse of add; refuses if another module needs it, keeps files you edited).
|
|
70
70
|
- `podo status` / `podo diff` — see your local edits vs. what PodoKit generated.
|
|
71
71
|
- `podo update` — pull in template/module improvements (3-way merges your edits; never touches owned files).
|
|
72
|
-
- `podo deploy` — plan, apply, verify, or roll back an immutable Kubernetes release. Use `.agents/skills/podokit-deploy`.
|
|
72
|
+
- `podo deploy` — plan, apply, verify, or roll back an immutable release on Kubernetes or one Docker host. Tag `vX.Y.Z` to build and push the images (`.github/workflows/release.yml`; check its runner — GitHub-hosted minutes are free for public repositories only). Use `.agents/skills/podokit-deploy`.
|
|
73
|
+
- `podo deploy sync` — copy local build output into a running Compose deployment and restart it, for iterating without a release. Not a release: no image is published and the next `apply` discards it. Use `.agents/skills/podokit-deploy-fast`.
|
|
73
74
|
|
|
74
75
|
## Commit conventions
|
|
75
76
|
|