windmill-cli 1.588.0 → 1.589.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/esm/gen/core/OpenAPI.js +1 -1
- package/esm/gen/services.gen.js +15 -8
- package/esm/src/commands/app/app_metadata.js +224 -33
- package/esm/src/commands/app/apps.js +19 -9
- package/esm/src/commands/app/dev.js +10 -8
- package/esm/src/commands/app/raw_apps.js +7 -59
- package/esm/src/commands/flow/flow_metadata.js +5 -2
- package/esm/src/commands/sync/global.js +2 -2
- package/esm/src/commands/sync/sync.js +29 -10
- package/esm/src/main.js +2 -2
- package/esm/src/utils/utils.js +9 -0
- package/package.json +1 -1
- package/types/gen/services.gen.d.ts +7 -3
- package/types/gen/services.gen.d.ts.map +1 -1
- package/types/gen/types.gen.d.ts +1 -2
- package/types/gen/types.gen.d.ts.map +1 -1
- package/types/src/commands/app/app_metadata.d.ts +8 -1
- package/types/src/commands/app/app_metadata.d.ts.map +1 -1
- package/types/src/commands/app/apps.d.ts +1 -1
- package/types/src/commands/app/apps.d.ts.map +1 -1
- package/types/src/commands/app/dev.d.ts.map +1 -1
- package/types/src/commands/app/raw_apps.d.ts +0 -7
- package/types/src/commands/app/raw_apps.d.ts.map +1 -1
- package/types/src/commands/flow/flow_metadata.d.ts.map +1 -1
- package/types/src/commands/sync/global.d.ts +1 -1
- package/types/src/commands/sync/global.d.ts.map +1 -1
- package/types/src/commands/sync/sync.d.ts.map +1 -1
- package/types/src/main.d.ts +1 -1
- package/types/src/main.d.ts.map +1 -1
- package/types/src/utils/utils.d.ts +1 -0
- package/types/src/utils/utils.d.ts.map +1 -1
- package/types/windmill-utils-internal/src/gen/types.gen.d.ts +1 -2
- package/types/windmill-utils-internal/src/gen/types.gen.d.ts.map +1 -1
package/esm/gen/core/OpenAPI.js
CHANGED
package/esm/gen/services.gen.js
CHANGED
|
@@ -4924,18 +4924,16 @@ export const listFlowPathsFromWorkspaceRunnable = (data) => {
|
|
|
4924
4924
|
* @param data The data for the request.
|
|
4925
4925
|
* @param data.workspace
|
|
4926
4926
|
* @param data.version
|
|
4927
|
-
* @param data.path
|
|
4928
4927
|
* @returns Flow flow details
|
|
4929
4928
|
* @throws ApiError
|
|
4930
4929
|
*/
|
|
4931
4930
|
export const getFlowVersion = (data) => {
|
|
4932
4931
|
return __request(OpenAPI, {
|
|
4933
4932
|
method: 'GET',
|
|
4934
|
-
url: '/w/{workspace}/flows/get/v/{version}
|
|
4933
|
+
url: '/w/{workspace}/flows/get/v/{version}',
|
|
4935
4934
|
path: {
|
|
4936
4935
|
workspace: data.workspace,
|
|
4937
|
-
version: data.version
|
|
4938
|
-
path: data.path
|
|
4936
|
+
version: data.version
|
|
4939
4937
|
}
|
|
4940
4938
|
});
|
|
4941
4939
|
};
|
|
@@ -4944,7 +4942,6 @@ export const getFlowVersion = (data) => {
|
|
|
4944
4942
|
* @param data The data for the request.
|
|
4945
4943
|
* @param data.workspace
|
|
4946
4944
|
* @param data.version
|
|
4947
|
-
* @param data.path
|
|
4948
4945
|
* @param data.requestBody Flow deployment message
|
|
4949
4946
|
* @returns string success
|
|
4950
4947
|
* @throws ApiError
|
|
@@ -4952,11 +4949,10 @@ export const getFlowVersion = (data) => {
|
|
|
4952
4949
|
export const updateFlowHistory = (data) => {
|
|
4953
4950
|
return __request(OpenAPI, {
|
|
4954
4951
|
method: 'POST',
|
|
4955
|
-
url: '/w/{workspace}/flows/history_update/v/{version}
|
|
4952
|
+
url: '/w/{workspace}/flows/history_update/v/{version}',
|
|
4956
4953
|
path: {
|
|
4957
4954
|
workspace: data.workspace,
|
|
4958
|
-
version: data.version
|
|
4959
|
-
path: data.path
|
|
4955
|
+
version: data.version
|
|
4960
4956
|
},
|
|
4961
4957
|
body: data.requestBody,
|
|
4962
4958
|
mediaType: 'application/json'
|
|
@@ -10308,6 +10304,17 @@ export const listBlacklistedAgentTokens = (data = {}) => {
|
|
|
10308
10304
|
}
|
|
10309
10305
|
});
|
|
10310
10306
|
};
|
|
10307
|
+
/**
|
|
10308
|
+
* get minimum worker version across all workers
|
|
10309
|
+
* @returns string minimum worker version
|
|
10310
|
+
* @throws ApiError
|
|
10311
|
+
*/
|
|
10312
|
+
export const getMinVersion = () => {
|
|
10313
|
+
return __request(OpenAPI, {
|
|
10314
|
+
method: 'GET',
|
|
10315
|
+
url: '/agent_workers/get_min_version'
|
|
10316
|
+
});
|
|
10317
|
+
};
|
|
10311
10318
|
/**
|
|
10312
10319
|
* get granular acls
|
|
10313
10320
|
* @param data The data for the request.
|
|
@@ -3,35 +3,35 @@ import * as dntShim from "../../../_dnt.shims.js";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { SEP, colors, log, yamlParseFile, yamlStringify, } from "../../../deps.js";
|
|
5
5
|
import { checkifMetadataUptodate, blueColor, clearGlobalLock, updateMetadataGlobalLock, inferSchema, getRawWorkspaceDependencies, } from "../../utils/metadata.js";
|
|
6
|
-
import { workspaceDependenciesLanguages } from "../../utils/script_common.js";
|
|
7
|
-
import { inferContentTypeFromFilePath, } from "../../utils/script_common.js";
|
|
6
|
+
import { workspaceDependenciesLanguages, } from "../../utils/script_common.js";
|
|
8
7
|
import { generateHash, getHeaders, writeIfChanged } from "../../utils/utils.js";
|
|
9
8
|
import { exts } from "../script/script.js";
|
|
10
9
|
import { FSFSElement, yamlOptions } from "../sync/sync.js";
|
|
11
10
|
import { replaceInlineScripts } from "./apps.js";
|
|
12
11
|
import { newPathAssigner, } from "../../../windmill-utils-internal/src/path-utils/path-assigner.js";
|
|
12
|
+
import { mergeConfigWithConfigFile } from "../../core/conf.js";
|
|
13
|
+
import { resolveWorkspace } from "../../core/context.js";
|
|
14
|
+
import { requireLogin } from "../../core/auth.js";
|
|
13
15
|
const TOP_HASH = "__app_hash";
|
|
16
|
+
export const APP_BACKEND_FOLDER = "backend";
|
|
14
17
|
/**
|
|
15
18
|
* Generates a hash for all inline scripts in an app directory
|
|
16
19
|
*/
|
|
17
|
-
async function generateAppHash(rawReqs, folder, defaultTs) {
|
|
18
|
-
const runnablesFolder =
|
|
20
|
+
async function generateAppHash(rawReqs, folder, rawApp, defaultTs) {
|
|
21
|
+
const runnablesFolder = rawApp
|
|
22
|
+
? path.join(folder, APP_BACKEND_FOLDER)
|
|
23
|
+
: folder;
|
|
19
24
|
const hashes = {};
|
|
20
25
|
try {
|
|
21
26
|
const elems = await FSFSElement(runnablesFolder, [], true);
|
|
22
27
|
for await (const f of elems.getChildren()) {
|
|
28
|
+
if (!rawApp && !f.path.includes(".inline_script.")) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
23
31
|
if (exts.some((e) => f.path.endsWith(e))) {
|
|
24
|
-
let reqs;
|
|
25
|
-
if (rawReqs) {
|
|
26
|
-
// Get language name from path
|
|
27
|
-
const lang = inferContentTypeFromFilePath(f.path, defaultTs);
|
|
28
|
-
// Get lock for that language
|
|
29
|
-
[, reqs] =
|
|
30
|
-
Object.entries(rawReqs).find(([lang2, _]) => lang == lang2) ?? [];
|
|
31
|
-
}
|
|
32
32
|
// Embed lock into hash
|
|
33
33
|
const relativePath = f.path.replace(runnablesFolder + SEP, "");
|
|
34
|
-
hashes[relativePath] = await generateHash((await f.getContentText()) + (
|
|
34
|
+
hashes[relativePath] = await generateHash((await f.getContentText()) + JSON.stringify(rawReqs));
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -46,7 +46,7 @@ async function generateAppHash(rawReqs, folder, defaultTs) {
|
|
|
46
46
|
/**
|
|
47
47
|
* Updates locks for inline scripts in an app
|
|
48
48
|
*/
|
|
49
|
-
export async function generateAppLocksInternal(appFolder, dryRun, workspace, opts, justUpdateMetadataLock, noStaleMessage) {
|
|
49
|
+
export async function generateAppLocksInternal(appFolder, rawApp, dryRun, workspace, opts, justUpdateMetadataLock, noStaleMessage) {
|
|
50
50
|
if (appFolder.endsWith(SEP)) {
|
|
51
51
|
appFolder = appFolder.substring(0, appFolder.length - 1);
|
|
52
52
|
}
|
|
@@ -55,7 +55,7 @@ export async function generateAppLocksInternal(appFolder, dryRun, workspace, opt
|
|
|
55
55
|
log.info(`Generating locks for app ${appFolder} at ${remote_path}`);
|
|
56
56
|
}
|
|
57
57
|
const rawWorkspaceDependencies = await getRawWorkspaceDependencies();
|
|
58
|
-
let hashes = await generateAppHash(rawWorkspaceDependencies, appFolder, opts.defaultTs);
|
|
58
|
+
let hashes = await generateAppHash(rawWorkspaceDependencies, appFolder, rawApp, opts.defaultTs);
|
|
59
59
|
const conf = await import("../../utils/metadata.js").then((m) => m.readLockfile());
|
|
60
60
|
if (await checkifMetadataUptodate(appFolder, hashes[TOP_HASH], conf, TOP_HASH)) {
|
|
61
61
|
if (!noStaleMessage) {
|
|
@@ -72,7 +72,7 @@ export async function generateAppLocksInternal(appFolder, dryRun, workspace, opt
|
|
|
72
72
|
.join("/")}) for ${appFolder}, using them`));
|
|
73
73
|
}
|
|
74
74
|
// Read the app file
|
|
75
|
-
const appFilePath = path.join(appFolder, "raw_app.yaml");
|
|
75
|
+
const appFilePath = path.join(appFolder, rawApp ? "raw_app.yaml" : "app.yaml");
|
|
76
76
|
const appFile = (await yamlParseFile(appFilePath));
|
|
77
77
|
if (!justUpdateMetadataLock) {
|
|
78
78
|
const changedScripts = [];
|
|
@@ -87,11 +87,21 @@ export async function generateAppLocksInternal(appFolder, dryRun, workspace, opt
|
|
|
87
87
|
}
|
|
88
88
|
if (changedScripts.length > 0) {
|
|
89
89
|
log.info(`Recomputing locks of ${changedScripts.join(", ")} in ${appFolder}`);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
if (rawApp) {
|
|
91
|
+
const runnablesPath = path.join(appFolder, APP_BACKEND_FOLDER) + SEP;
|
|
92
|
+
const rawAppFile = appFile;
|
|
93
|
+
// Replace inline scripts for changed runnables
|
|
94
|
+
replaceInlineScripts(rawAppFile.runnables, runnablesPath, false);
|
|
95
|
+
// Update the app runnables with new locks
|
|
96
|
+
rawAppFile.runnables = await updateRawAppRunnables(workspace, rawAppFile.runnables, remote_path, appFolder, rawWorkspaceDependencies, opts.defaultTs);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const normalAppFile = appFile;
|
|
100
|
+
// Replace inline scripts for normal apps
|
|
101
|
+
replaceInlineScripts(normalAppFile.value, appFolder + SEP, false);
|
|
102
|
+
// Update the app value with new locks
|
|
103
|
+
normalAppFile.value = await updateAppInlineScripts(workspace, normalAppFile.value, remote_path, appFolder, rawWorkspaceDependencies, opts.defaultTs);
|
|
104
|
+
}
|
|
95
105
|
// Write the updated app file
|
|
96
106
|
writeIfChanged(appFilePath, yamlStringify(appFile, yamlOptions));
|
|
97
107
|
}
|
|
@@ -100,7 +110,7 @@ export async function generateAppLocksInternal(appFolder, dryRun, workspace, opt
|
|
|
100
110
|
}
|
|
101
111
|
}
|
|
102
112
|
// Regenerate hashes after updates
|
|
103
|
-
hashes = await generateAppHash(rawWorkspaceDependencies, appFolder, opts.defaultTs);
|
|
113
|
+
hashes = await generateAppHash(rawWorkspaceDependencies, appFolder, rawApp, opts.defaultTs);
|
|
104
114
|
await clearGlobalLock(appFolder);
|
|
105
115
|
for (const [scriptPath, hash] of Object.entries(hashes)) {
|
|
106
116
|
await updateMetadataGlobalLock(appFolder, hash, scriptPath);
|
|
@@ -108,12 +118,45 @@ export async function generateAppLocksInternal(appFolder, dryRun, workspace, opt
|
|
|
108
118
|
log.info(colors.green(`App ${remote_path} lockfiles updated`));
|
|
109
119
|
}
|
|
110
120
|
/**
|
|
111
|
-
*
|
|
121
|
+
* Traverses an app structure (either app.value for normal apps or app.runnables for raw apps)
|
|
122
|
+
* and processes all inline scripts found, returning the updated structure
|
|
123
|
+
*/
|
|
124
|
+
async function traverseAndProcessInlineScripts(obj, processor, currentPath = []) {
|
|
125
|
+
if (!obj || typeof obj !== "object") {
|
|
126
|
+
return obj;
|
|
127
|
+
}
|
|
128
|
+
if (Array.isArray(obj)) {
|
|
129
|
+
return await Promise.all(obj.map((item, index) => traverseAndProcessInlineScripts(item, processor, [
|
|
130
|
+
...currentPath,
|
|
131
|
+
`[${index}]`,
|
|
132
|
+
])));
|
|
133
|
+
}
|
|
134
|
+
const result = {};
|
|
135
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
136
|
+
if (key === "inlineScript" && typeof value === "object") {
|
|
137
|
+
// Found an inline script - process it
|
|
138
|
+
result[key] = await processor(value, {
|
|
139
|
+
path: currentPath,
|
|
140
|
+
parentKey: key,
|
|
141
|
+
parentObject: obj,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// Recursively process nested objects
|
|
146
|
+
result[key] = await traverseAndProcessInlineScripts(value, processor, [
|
|
147
|
+
...currentPath,
|
|
148
|
+
key,
|
|
149
|
+
]);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Updates locks for all runnables in a raw app, generating locks inline script by inline script
|
|
112
156
|
* Also writes content and locks back to the runnables folder
|
|
113
157
|
*/
|
|
114
|
-
async function
|
|
115
|
-
const
|
|
116
|
-
const runnablesFolder = path.join(appFolder, "runnables");
|
|
158
|
+
async function updateRawAppRunnables(workspace, runnables, remotePath, appFolder, rawDeps, defaultTs = "bun") {
|
|
159
|
+
const runnablesFolder = path.join(appFolder, APP_BACKEND_FOLDER);
|
|
117
160
|
// Ensure runnables folder exists
|
|
118
161
|
try {
|
|
119
162
|
await dntShim.Deno.mkdir(runnablesFolder, { recursive: true });
|
|
@@ -122,31 +165,36 @@ async function updateAppRunnables(workspace, runnables, remotePath, appFolder, r
|
|
|
122
165
|
// Folder may already exist
|
|
123
166
|
}
|
|
124
167
|
const pathAssigner = newPathAssigner(defaultTs);
|
|
168
|
+
// Process each runnable
|
|
169
|
+
const updatedRunnables = {};
|
|
125
170
|
for (const [runnableId, runnable] of Object.entries(runnables)) {
|
|
126
171
|
// Only process inline scripts (runnableByName with inlineScript)
|
|
127
172
|
if (runnable?.type !== "runnableByName" || !runnable?.inlineScript) {
|
|
173
|
+
updatedRunnables[runnableId] = runnable;
|
|
128
174
|
continue;
|
|
129
175
|
}
|
|
130
176
|
const inlineScript = runnable.inlineScript;
|
|
131
177
|
const language = inlineScript.language;
|
|
132
178
|
const content = inlineScript.content;
|
|
133
179
|
if (!content || !language) {
|
|
180
|
+
updatedRunnables[runnableId] = runnable;
|
|
134
181
|
continue;
|
|
135
182
|
}
|
|
136
183
|
// Skip if content is still an !inline reference (should have been replaced by replaceInlineScripts)
|
|
137
184
|
if (typeof content === "string" && content.startsWith("!inline ")) {
|
|
138
185
|
log.warn(colors.yellow(`Runnable ${runnableId} content is still an !inline reference, skipping`));
|
|
186
|
+
updatedRunnables[runnableId] = runnable;
|
|
139
187
|
continue;
|
|
140
188
|
}
|
|
141
189
|
// Skip frontend scripts - they don't need locks
|
|
142
190
|
if (language === "frontend") {
|
|
191
|
+
updatedRunnables[runnableId] = runnable;
|
|
143
192
|
continue;
|
|
144
193
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
log.info(colors.gray(`Generating lock for runnable ${runnableId} (${language})${langRawDeps ? " with raw deps" : ""}`));
|
|
194
|
+
log.info(colors.gray(`Generating lock for runnable ${runnableId} (${language})
|
|
195
|
+
}`));
|
|
148
196
|
try {
|
|
149
|
-
const lock = await generateInlineScriptLock(workspace, content, language, `${remotePath}/${runnableId}`,
|
|
197
|
+
const lock = await generateInlineScriptLock(workspace, content, language, `${remotePath}/${runnableId}`, rawDeps);
|
|
150
198
|
// Determine file extension for this language
|
|
151
199
|
const [basePathO, ext] = pathAssigner.assignPath(runnable.name, language);
|
|
152
200
|
const basePath = basePathO.replaceAll(SEP, "/");
|
|
@@ -174,14 +222,72 @@ async function updateAppRunnables(workspace, runnables, remotePath, appFolder, r
|
|
|
174
222
|
catch (error) {
|
|
175
223
|
log.error(colors.red(`Failed to generate lock for runnable ${runnableId}: ${error.message}`));
|
|
176
224
|
// Continue with other runnables even if one fails
|
|
225
|
+
updatedRunnables[runnableId] = runnable;
|
|
177
226
|
}
|
|
178
227
|
}
|
|
179
228
|
return updatedRunnables;
|
|
180
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* Updates locks for all inline scripts in a normal app, similar to updateRawAppRunnables
|
|
232
|
+
* but for the app.value structure instead of app.runnables
|
|
233
|
+
*/
|
|
234
|
+
async function updateAppInlineScripts(workspace, appValue, remotePath, appFolder, rawDeps, defaultTs = "bun") {
|
|
235
|
+
const pathAssigner = newPathAssigner(defaultTs);
|
|
236
|
+
const processor = async (inlineScript, context) => {
|
|
237
|
+
const language = inlineScript.language;
|
|
238
|
+
const content = inlineScript.content;
|
|
239
|
+
if (!content || !language) {
|
|
240
|
+
return inlineScript;
|
|
241
|
+
}
|
|
242
|
+
// Skip if content is still an !inline reference (should have been replaced by replaceInlineScripts)
|
|
243
|
+
if (typeof content === "string" && content.startsWith("!inline ")) {
|
|
244
|
+
log.warn(colors.yellow(`Inline script at ${context.path.join(".")} is still an !inline reference, skipping`));
|
|
245
|
+
return inlineScript;
|
|
246
|
+
}
|
|
247
|
+
// Skip frontend scripts - they don't need locks
|
|
248
|
+
if (language === "frontend") {
|
|
249
|
+
return inlineScript;
|
|
250
|
+
}
|
|
251
|
+
// Get the name from the parent object (following extractInlineScriptsForApps pattern)
|
|
252
|
+
// For normal apps, the name is stored in the component's "name" property
|
|
253
|
+
const scriptName = context.parentObject?.["name"] || "unnamed";
|
|
254
|
+
const scriptPath = `${remotePath}/${context.path.join("/")}`;
|
|
255
|
+
log.info(colors.gray(`Generating lock for inline script "${scriptName}" at ${context.path.join(".")} (${language})`));
|
|
256
|
+
try {
|
|
257
|
+
const lock = await generateInlineScriptLock(workspace, content, language, scriptPath, rawDeps);
|
|
258
|
+
// Determine file extension for this language (following extractInlineScriptsForApps pattern)
|
|
259
|
+
const [basePathO, ext] = pathAssigner.assignPath(scriptName, language);
|
|
260
|
+
const basePath = basePathO.replaceAll(SEP, "/");
|
|
261
|
+
const contentPath = path.join(appFolder, `${basePath}${ext}`);
|
|
262
|
+
const lockPath = path.join(appFolder, `${basePath}lock`);
|
|
263
|
+
// Write content to file
|
|
264
|
+
writeIfChanged(contentPath, content);
|
|
265
|
+
// Write lock to file if it exists
|
|
266
|
+
if (lock && lock !== "") {
|
|
267
|
+
writeIfChanged(lockPath, lock);
|
|
268
|
+
}
|
|
269
|
+
// Update the inline script with !inline references
|
|
270
|
+
const inlineContentRef = `!inline ${basePath}${ext}`;
|
|
271
|
+
const inlineLockRef = lock && lock !== "" ? `!inline ${basePath}lock` : "";
|
|
272
|
+
log.info(colors.gray(` Written ${basePath}${ext}${lock ? ` and ${basePath}lock` : ""}`));
|
|
273
|
+
return {
|
|
274
|
+
...inlineScript,
|
|
275
|
+
content: inlineContentRef,
|
|
276
|
+
lock: inlineLockRef,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
log.error(colors.red(`Failed to generate lock for inline script at ${context.path.join(".")}: ${error.message}`));
|
|
281
|
+
// Return original on error
|
|
282
|
+
return inlineScript;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
return await traverseAndProcessInlineScripts(appValue, processor);
|
|
286
|
+
}
|
|
181
287
|
/**
|
|
182
288
|
* Generates a lock for a single inline script using the dependencies endpoint
|
|
183
289
|
*/
|
|
184
|
-
async function generateInlineScriptLock(workspace, content, language, scriptPath,
|
|
290
|
+
async function generateInlineScriptLock(workspace, content, language, scriptPath, rawWorkspaceDependencies) {
|
|
185
291
|
const extraHeaders = getHeaders();
|
|
186
292
|
const rawResponse = await fetch(`${workspace.remote}api/w/${workspace.workspaceId}/jobs/run/dependencies`, {
|
|
187
293
|
method: "POST",
|
|
@@ -198,7 +304,10 @@ async function generateInlineScriptLock(workspace, content, language, scriptPath
|
|
|
198
304
|
script_path: scriptPath,
|
|
199
305
|
},
|
|
200
306
|
],
|
|
201
|
-
|
|
307
|
+
raw_workspace_dependencies: rawWorkspaceDependencies &&
|
|
308
|
+
Object.keys(rawWorkspaceDependencies).length > 0
|
|
309
|
+
? rawWorkspaceDependencies
|
|
310
|
+
: null,
|
|
202
311
|
entrypoint: scriptPath,
|
|
203
312
|
}),
|
|
204
313
|
});
|
|
@@ -260,7 +369,7 @@ export async function inferRunnableSchemaFromFile(appFolder, runnableFilePath) {
|
|
|
260
369
|
const inlineScript = runnable.inlineScript;
|
|
261
370
|
const language = inlineScript.language;
|
|
262
371
|
// Read the actual content from the file
|
|
263
|
-
const fullFilePath = path.join(appFolder,
|
|
372
|
+
const fullFilePath = path.join(appFolder, APP_BACKEND_FOLDER, runnableFilePath);
|
|
264
373
|
let content;
|
|
265
374
|
try {
|
|
266
375
|
content = await dntShim.Deno.readTextFile(fullFilePath);
|
|
@@ -285,3 +394,85 @@ export async function inferRunnableSchemaFromFile(appFolder, runnableFilePath) {
|
|
|
285
394
|
return undefined;
|
|
286
395
|
}
|
|
287
396
|
}
|
|
397
|
+
function getAppFolders(elems, extension) {
|
|
398
|
+
return Object.keys(elems)
|
|
399
|
+
.filter((p) => p.endsWith(SEP + extension))
|
|
400
|
+
.map((p) => p.substring(0, p.length - (SEP + extension).length));
|
|
401
|
+
}
|
|
402
|
+
export async function generateLocksCommand(opts, appPath) {
|
|
403
|
+
const { generateAppLocksInternal } = await import("./app_metadata.js");
|
|
404
|
+
const { elementsToMap, FSFSElement } = await import("../sync/sync.js");
|
|
405
|
+
const { ignoreF } = await import("../sync/sync.js");
|
|
406
|
+
const { Confirm } = await import("../../../deps.js");
|
|
407
|
+
if (appPath == "") {
|
|
408
|
+
appPath = undefined;
|
|
409
|
+
}
|
|
410
|
+
const workspace = await resolveWorkspace(opts);
|
|
411
|
+
await requireLogin(opts);
|
|
412
|
+
opts = await mergeConfigWithConfigFile(opts);
|
|
413
|
+
if (appPath) {
|
|
414
|
+
//TODO: Generate metadata for a specific raw app but handle normal apps to
|
|
415
|
+
throw new Error("Not implemented");
|
|
416
|
+
// Generate metadata for a specific app
|
|
417
|
+
// await generateAppLocksInternal(
|
|
418
|
+
// appPath,
|
|
419
|
+
// true,
|
|
420
|
+
// false,
|
|
421
|
+
// workspace,
|
|
422
|
+
// opts,
|
|
423
|
+
// false,
|
|
424
|
+
// false
|
|
425
|
+
// );
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
// Generate metadata for all apps
|
|
429
|
+
const ignore = await ignoreF(opts);
|
|
430
|
+
const elems = await elementsToMap(await FSFSElement(dntShim.Deno.cwd(), [], true), (p, isD) => {
|
|
431
|
+
return (ignore(p, isD) ||
|
|
432
|
+
(!isD &&
|
|
433
|
+
!p.endsWith(SEP + "raw_app.yaml") &&
|
|
434
|
+
!p.endsWith(SEP + "app.yaml")));
|
|
435
|
+
}, false, {});
|
|
436
|
+
const rawAppFolders = getAppFolders(elems, "raw_app.yaml");
|
|
437
|
+
const appFolders = getAppFolders(elems, "app.yaml");
|
|
438
|
+
let hasAny = false;
|
|
439
|
+
log.info(`Checking metadata for all apps (${appFolders.length}) and raw apps (${rawAppFolders.length})`);
|
|
440
|
+
for (const appFolder of rawAppFolders) {
|
|
441
|
+
const candidate = await generateAppLocksInternal(appFolder, true, true, workspace, opts, false, true);
|
|
442
|
+
if (candidate) {
|
|
443
|
+
hasAny = true;
|
|
444
|
+
log.info(colors.green(`+ ${candidate}`));
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
for (const appFolder of appFolders) {
|
|
448
|
+
const candidate = await generateAppLocksInternal(appFolder, false, true, workspace, opts, false, true);
|
|
449
|
+
if (candidate) {
|
|
450
|
+
hasAny = true;
|
|
451
|
+
log.info(colors.green(`+ ${candidate}`));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (hasAny) {
|
|
455
|
+
if (opts.dryRun) {
|
|
456
|
+
log.info(colors.gray(`Dry run complete.`));
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
if (!opts.yes &&
|
|
460
|
+
!(await Confirm.prompt({
|
|
461
|
+
message: "Update the metadata of the above apps?",
|
|
462
|
+
default: true,
|
|
463
|
+
}))) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
log.info(colors.green.bold("No metadata to update"));
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
for (const appFolder of rawAppFolders) {
|
|
472
|
+
await generateAppLocksInternal(appFolder, true, false, workspace, opts, false, true);
|
|
473
|
+
}
|
|
474
|
+
for (const appFolder of appFolders) {
|
|
475
|
+
await generateAppLocksInternal(appFolder, false, false, workspace, opts, false, true);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
@@ -6,7 +6,7 @@ import * as wmill from "../../../gen/services.gen.js";
|
|
|
6
6
|
import { isSuperset } from "../../types.js";
|
|
7
7
|
import { readInlinePathSync } from "../../utils/utils.js";
|
|
8
8
|
import devCommand from "./dev.js";
|
|
9
|
-
import {
|
|
9
|
+
import { isVersionsGeq1585 } from "../sync/global.js";
|
|
10
10
|
const alreadySynced = [];
|
|
11
11
|
function respecializeFields(fields) {
|
|
12
12
|
Object.entries(fields).forEach(([k, v]) => {
|
|
@@ -33,20 +33,30 @@ export function repopulateFields(runnables) {
|
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
-
export function replaceInlineScripts(rec, localPath) {
|
|
36
|
+
export function replaceInlineScripts(rec, localPath, addType) {
|
|
37
37
|
if (!rec) {
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
40
|
if (typeof rec == "object") {
|
|
41
41
|
return Object.entries(rec).flatMap(([k, v]) => {
|
|
42
42
|
if (k == "runType") {
|
|
43
|
-
if (
|
|
44
|
-
|
|
43
|
+
if (addType) {
|
|
44
|
+
if (isVersionsGeq1585()) {
|
|
45
|
+
rec["type"] = "path";
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
rec["type"] = "runnableByPath";
|
|
49
|
+
}
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
52
|
else if (k == "inlineScript" && typeof v == "object") {
|
|
48
|
-
if (
|
|
49
|
-
|
|
53
|
+
if (addType) {
|
|
54
|
+
if (isVersionsGeq1585()) {
|
|
55
|
+
rec["type"] = "inline";
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
rec["type"] = "runnableByName";
|
|
59
|
+
}
|
|
50
60
|
}
|
|
51
61
|
const o = v;
|
|
52
62
|
if (o["content"] && o["content"].startsWith("!inline")) {
|
|
@@ -59,7 +69,7 @@ export function replaceInlineScripts(rec, localPath) {
|
|
|
59
69
|
}
|
|
60
70
|
}
|
|
61
71
|
else {
|
|
62
|
-
replaceInlineScripts(v, localPath);
|
|
72
|
+
replaceInlineScripts(v, localPath, addType);
|
|
63
73
|
}
|
|
64
74
|
});
|
|
65
75
|
}
|
|
@@ -97,7 +107,7 @@ export async function pushApp(workspace, remotePath, localPath, message) {
|
|
|
97
107
|
}
|
|
98
108
|
const path = localPath + "app.yaml";
|
|
99
109
|
const localApp = (await yamlParseFile(path));
|
|
100
|
-
replaceInlineScripts(localApp.value, localPath);
|
|
110
|
+
replaceInlineScripts(localApp.value, localPath, true);
|
|
101
111
|
await generatingPolicy(localApp, remotePath, localApp?.["public"] ??
|
|
102
112
|
localApp?.["policy"]?.["execution_mode"] == "anonymous");
|
|
103
113
|
if (app) {
|
|
@@ -186,7 +196,7 @@ const command = new Command()
|
|
|
186
196
|
.option("--dry-run", "Perform a dry run without making changes")
|
|
187
197
|
.option("--default-ts <runtime:string>", "Default TypeScript runtime (bun or deno)")
|
|
188
198
|
.action(async (opts, appFolder) => {
|
|
189
|
-
const { generateLocksCommand } = await import("./
|
|
199
|
+
const { generateLocksCommand } = await import("./app_metadata.js");
|
|
190
200
|
await generateLocksCommand(opts, appFolder);
|
|
191
201
|
});
|
|
192
202
|
export default command;
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
// deno-lint-ignore-file no-explicit-any
|
|
2
2
|
import * as dntShim from "../../../_dnt.shims.js";
|
|
3
|
-
import { Command, colors, log, getPort, open, windmillUtils, yamlParseFile, } from "../../../deps.js";
|
|
3
|
+
import { Command, colors, log, getPort, open, windmillUtils, yamlParseFile, SEP, } from "../../../deps.js";
|
|
4
4
|
import * as http from "node:http";
|
|
5
5
|
import * as fs from "node:fs";
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import process from "node:process";
|
|
8
8
|
import { writeFileSync } from "node:fs";
|
|
9
9
|
import { WebSocketServer } from "ws";
|
|
10
|
-
import { getDevBuildOptions, ensureNodeModules, createFrameworkPlugins, detectFrameworks } from "./bundle.js";
|
|
10
|
+
import { getDevBuildOptions, ensureNodeModules, createFrameworkPlugins, detectFrameworks, } from "./bundle.js";
|
|
11
11
|
import { wmillTsDev as wmillTs } from "./wmillTsDev.js";
|
|
12
12
|
import * as wmill from "../../../gen/services.gen.js";
|
|
13
13
|
import { resolveWorkspace } from "../../core/context.js";
|
|
14
14
|
import { requireLogin } from "../../core/auth.js";
|
|
15
15
|
import { GLOBAL_CONFIG_OPT } from "../../core/conf.js";
|
|
16
16
|
import { replaceInlineScripts } from "./apps.js";
|
|
17
|
-
import { inferRunnableSchemaFromFile } from "./app_metadata.js";
|
|
17
|
+
import { APP_BACKEND_FOLDER, inferRunnableSchemaFromFile, } from "./app_metadata.js";
|
|
18
18
|
const DEFAULT_PORT = 4000;
|
|
19
19
|
const DEFAULT_HOST = "localhost";
|
|
20
20
|
// HTML template with live reload
|
|
@@ -96,7 +96,7 @@ async function dev(opts) {
|
|
|
96
96
|
const shouldOpen = opts.open ?? true;
|
|
97
97
|
// Detect frameworks to determine default entry point
|
|
98
98
|
const frameworks = detectFrameworks(process.cwd());
|
|
99
|
-
const defaultEntry =
|
|
99
|
+
const defaultEntry = frameworks.svelte || frameworks.vue ? "index.ts" : "index.tsx";
|
|
100
100
|
const entryPoint = opts.entry ?? defaultEntry;
|
|
101
101
|
// Verify entry point exists
|
|
102
102
|
if (!fs.existsSync(entryPoint)) {
|
|
@@ -177,7 +177,7 @@ async function dev(opts) {
|
|
|
177
177
|
// Initial build
|
|
178
178
|
await ctx.rebuild();
|
|
179
179
|
// Watch runnables folder for changes
|
|
180
|
-
const runnablesPath = path.join(process.cwd(),
|
|
180
|
+
const runnablesPath = path.join(process.cwd(), APP_BACKEND_FOLDER);
|
|
181
181
|
let runnablesWatcher;
|
|
182
182
|
if (fs.existsSync(runnablesPath)) {
|
|
183
183
|
log.info(colors.blue(`👁️ Watching runnables folder at: ${runnablesPath}\n`));
|
|
@@ -498,7 +498,7 @@ async function loadRunnables() {
|
|
|
498
498
|
try {
|
|
499
499
|
const localPath = process.cwd();
|
|
500
500
|
const rawApp = (await yamlParseFile(path.join(localPath, "raw_app.yaml")));
|
|
501
|
-
replaceInlineScripts(rawApp.runnables, path.join(localPath,
|
|
501
|
+
replaceInlineScripts(rawApp.runnables, path.join(localPath, APP_BACKEND_FOLDER) + SEP, true);
|
|
502
502
|
return rawApp?.runnables ?? {};
|
|
503
503
|
}
|
|
504
504
|
catch (error) {
|
|
@@ -525,7 +525,8 @@ async function executeRunnable(runnable, workspace, appPath, runnableId, args) {
|
|
|
525
525
|
}
|
|
526
526
|
}
|
|
527
527
|
}
|
|
528
|
-
if ((runnable.type === "inline" || runnable.type === "runnableByName") &&
|
|
528
|
+
if ((runnable.type === "inline" || runnable.type === "runnableByName") &&
|
|
529
|
+
runnable.inlineScript) {
|
|
529
530
|
const inlineScript = runnable.inlineScript;
|
|
530
531
|
if (inlineScript.id !== undefined) {
|
|
531
532
|
requestBody.id = inlineScript.id;
|
|
@@ -538,7 +539,8 @@ async function executeRunnable(runnable, workspace, appPath, runnableId, args) {
|
|
|
538
539
|
cache_ttl: inlineScript.cache_ttl,
|
|
539
540
|
};
|
|
540
541
|
}
|
|
541
|
-
else if ((runnable.type === "path" || runnable.type === "runnableByPath") &&
|
|
542
|
+
else if ((runnable.type === "path" || runnable.type === "runnableByPath") &&
|
|
543
|
+
runnable.path) {
|
|
542
544
|
const runType = runnable.runType ?? "script";
|
|
543
545
|
requestBody.path =
|
|
544
546
|
runType !== "hubscript"
|