mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-05 08:02:18 +00:00
Merge remote-tracking branch 'origin/main' into fork-datatable-schema-export
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT (config->>'native_mode')::boolean FROM config WHERE name = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "bool",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "36b95bc7956eb7bba7cd6fa9cd829980a0bf4970b919cabad1daab16627404fc"
|
||||
}
|
||||
@@ -20,6 +20,7 @@ pub async fn connect_db(
|
||||
server_mode: bool,
|
||||
indexer_mode: bool,
|
||||
worker_mode: bool,
|
||||
num_workers: i32,
|
||||
#[cfg(feature = "private")] mut killpill_rx: tokio::sync::broadcast::Receiver<()>,
|
||||
) -> anyhow::Result<sqlx::Pool<sqlx::Postgres>> {
|
||||
use anyhow::Context;
|
||||
@@ -34,13 +35,7 @@ pub async fn connect_db(
|
||||
} else if indexer_mode {
|
||||
DEFAULT_MAX_CONNECTIONS_INDEXER
|
||||
} else {
|
||||
DEFAULT_MAX_CONNECTIONS_WORKER
|
||||
+ std::env::var("NUM_WORKERS")
|
||||
.ok()
|
||||
.map(|x| x.parse().ok())
|
||||
.flatten()
|
||||
.unwrap_or(1)
|
||||
- 1
|
||||
DEFAULT_MAX_CONNECTIONS_WORKER + (num_workers.max(1) as u32) - 1
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -103,7 +98,7 @@ pub async fn connect(
|
||||
use sqlx::Executor;
|
||||
use std::time::Duration;
|
||||
let mut pool_options = sqlx::postgres::PgPoolOptions::new()
|
||||
.min_connections((max_connections / 5).clamp(1, max_connections))
|
||||
.min_connections(0)
|
||||
.max_connections(max_connections)
|
||||
.max_lifetime(Duration::from_secs(30 * 60)); // 30 mins
|
||||
if worker_mode {
|
||||
|
||||
+27
-11
@@ -694,6 +694,7 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
let mut num_workers = if mode == Mode::Server || mode == Mode::Indexer || mode == Mode::MCP {
|
||||
0
|
||||
} else if is_native_mode_from_env() {
|
||||
NATIVE_MODE_RESOLVED.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
println!("Native mode enabled: forcing NUM_WORKERS=8");
|
||||
8
|
||||
} else {
|
||||
@@ -866,6 +867,30 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve native mode early (before connect_db) so connection pool size accounts for it.
|
||||
// native_mode can come from env OR from the DB worker group config.
|
||||
if worker_mode && !is_native_mode_from_env() {
|
||||
if let Some(db) = conn.as_sql() {
|
||||
let native_from_db: bool = sqlx::query_scalar!(
|
||||
"SELECT (config->>'native_mode')::boolean FROM config WHERE name = $1",
|
||||
format!("worker__{}", *windmill_common::worker::WORKER_GROUP)
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.flatten()
|
||||
.unwrap_or(false);
|
||||
if native_from_db {
|
||||
NATIVE_MODE_RESOLVED.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
num_workers = 8;
|
||||
tracing::info!(
|
||||
"Native mode detected from worker config (early): forcing NUM_WORKERS=8"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let conn = if mode == Mode::Agent {
|
||||
conn
|
||||
} else {
|
||||
@@ -878,6 +903,7 @@ async fn windmill_main() -> anyhow::Result<()> {
|
||||
server_mode,
|
||||
indexer_mode,
|
||||
worker_mode,
|
||||
num_workers,
|
||||
#[cfg(feature = "private")]
|
||||
killpill_rx.resubscribe(),
|
||||
)
|
||||
@@ -982,16 +1008,6 @@ Windmill Community Edition {GIT_VERSION}
|
||||
)
|
||||
.await;
|
||||
|
||||
// native_mode may also be set via DB worker group config (not just env).
|
||||
// NATIVE_MODE_RESOLVED is updated by load_worker_config during initial_load.
|
||||
if worker_mode
|
||||
&& !is_native_mode_from_env()
|
||||
&& NATIVE_MODE_RESOLVED.load(std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
num_workers = 8;
|
||||
tracing::info!("Native mode detected from worker config: forcing NUM_WORKERS=8");
|
||||
}
|
||||
|
||||
monitor_db(
|
||||
&conn,
|
||||
&base_internal_url,
|
||||
@@ -1884,7 +1900,7 @@ pub async fn run_workers(
|
||||
|
||||
tracing::info!(
|
||||
"Starting {num_workers} workers and SLEEP_QUEUE={}ms",
|
||||
*windmill_worker::SLEEP_QUEUE
|
||||
windmill_worker::sleep_queue()
|
||||
);
|
||||
|
||||
for i in 1..(num_workers + 1) {
|
||||
|
||||
@@ -251,9 +251,8 @@ pub async fn initial_load(
|
||||
.map(|x| x.tags.clone())
|
||||
.unwrap_or_default();
|
||||
// we only check from env as native_mode is not stored in the token
|
||||
// NATIVE_MODE_RESOLVED is already set in main.rs during startup
|
||||
let native_mode = windmill_common::worker::is_native_mode_from_env();
|
||||
windmill_common::worker::NATIVE_MODE_RESOLVED
|
||||
.store(native_mode, std::sync::atomic::Ordering::Relaxed);
|
||||
*config = WorkerConfig {
|
||||
worker_tags,
|
||||
env_vars: load_env_vars(
|
||||
|
||||
@@ -129,11 +129,12 @@ async fn update_config(
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
let config = if name.starts_with("worker__") {
|
||||
// In CE, only allow setting worker_tags, cache_clear, and init_bash
|
||||
// In CE, only allow setting worker_tags, cache_clear, init_bash, and native_mode
|
||||
serde_json::json!({
|
||||
"worker_tags": config.get("worker_tags"),
|
||||
"cache_clear": config.get("cache_clear"),
|
||||
"init_bash": config.get("init_bash")
|
||||
"init_bash": config.get("init_bash"),
|
||||
"native_mode": config.get("native_mode")
|
||||
})
|
||||
} else {
|
||||
config
|
||||
|
||||
@@ -303,7 +303,7 @@ pub struct PowershellRepo {
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
|
||||
pub static ref SLEEP_QUEUE: u64 = std::env::var("SLEEP_QUEUE")
|
||||
static ref SLEEP_QUEUE_BASE: u64 = std::env::var("SLEEP_QUEUE")
|
||||
.ok()
|
||||
.and_then(|x| x.parse::<u64>().ok())
|
||||
.unwrap_or_else(|| {
|
||||
@@ -647,6 +647,14 @@ lazy_static::lazy_static! {
|
||||
pub static ref FLOW_RUNNER_RUNNING: Mutex<bool> = Mutex::new(false);
|
||||
}
|
||||
|
||||
pub fn sleep_queue() -> u64 {
|
||||
if NATIVE_MODE_RESOLVED.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
300
|
||||
} else {
|
||||
*SLEEP_QUEUE_BASE
|
||||
}
|
||||
}
|
||||
|
||||
type Envs = Vec<(String, String)>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -1373,7 +1381,7 @@ fn start_interactive_worker_shell(
|
||||
{
|
||||
Duration::from_secs(WORKER_SHELL_NAP_TIME_DURATION)
|
||||
}
|
||||
_ => Duration::from_millis(*SLEEP_QUEUE * 10),
|
||||
_ => Duration::from_millis(sleep_queue() * 10),
|
||||
};
|
||||
tokio::select! {
|
||||
_ = tokio::time::sleep(nap_time) => {
|
||||
@@ -1386,7 +1394,7 @@ fn start_interactive_worker_shell(
|
||||
|
||||
Err(err) => {
|
||||
tracing::error!(worker = %worker_name, hostname = %hostname, "Failed to pull jobs: {}", err);
|
||||
tokio::time::sleep(Duration::from_millis(*SLEEP_QUEUE * 20)).await;
|
||||
tokio::time::sleep(Duration::from_millis(sleep_queue() * 20)).await;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -2699,7 +2707,7 @@ pub async fn run_worker(
|
||||
None
|
||||
};
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(*SLEEP_QUEUE)).await;
|
||||
tokio::time::sleep(Duration::from_millis(sleep_queue())).await;
|
||||
|
||||
#[cfg(feature = "benchmark")]
|
||||
{
|
||||
@@ -2720,7 +2728,7 @@ pub async fn run_worker(
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!(worker = %worker_name, hostname = %hostname, "Failed to pull jobs: {}", err);
|
||||
tokio::time::sleep(Duration::from_millis(*SLEEP_QUEUE * 5)).await;
|
||||
tokio::time::sleep(Duration::from_millis(sleep_queue() * 5)).await;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -56,13 +56,20 @@ export async function pushFlow(
|
||||
}
|
||||
const localFlow = (await yamlParseFile(localPath + "flow.yaml")) as FlowFile;
|
||||
|
||||
const fileReader = async (path: string) => await readFile(localPath + path, "utf-8");
|
||||
await replaceInlineScripts(
|
||||
localFlow.value.modules,
|
||||
async (path: string) => await readFile(localPath + path, "utf-8"),
|
||||
fileReader,
|
||||
log,
|
||||
localPath,
|
||||
SEP
|
||||
);
|
||||
if (localFlow.value.failure_module) {
|
||||
await replaceInlineScripts([localFlow.value.failure_module], fileReader, log, localPath, SEP);
|
||||
}
|
||||
if (localFlow.value.preprocessor_module) {
|
||||
await replaceInlineScripts([localFlow.value.preprocessor_module], fileReader, log, localPath, SEP);
|
||||
}
|
||||
|
||||
if (flow) {
|
||||
if (isSuperset(localFlow, flow)) {
|
||||
@@ -252,13 +259,20 @@ async function preview(
|
||||
const localFlow = (await yamlParseFile(flowPath + "flow.yaml")) as FlowFile;
|
||||
|
||||
// Replace inline scripts with their actual content
|
||||
const fileReader = async (path: string) => await readFile(flowPath + path, "utf-8");
|
||||
await replaceInlineScripts(
|
||||
localFlow.value.modules,
|
||||
async (path: string) => await readFile(flowPath + path, "utf-8"),
|
||||
fileReader,
|
||||
log,
|
||||
flowPath,
|
||||
SEP
|
||||
);
|
||||
if (localFlow.value.failure_module) {
|
||||
await replaceInlineScripts([localFlow.value.failure_module], fileReader, log, flowPath, SEP);
|
||||
}
|
||||
if (localFlow.value.preprocessor_module) {
|
||||
await replaceInlineScripts([localFlow.value.preprocessor_module], fileReader, log, flowPath, SEP);
|
||||
}
|
||||
|
||||
const input = opts.data ? await resolve(opts.data) : {};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
} from "../../utils/metadata.ts";
|
||||
import { ScriptLanguage } from "../../utils/script_common.ts";
|
||||
import { extractInlineScripts as extractInlineScriptsForFlows } from "../../../windmill-utils-internal/src/inline-scripts/extractor.ts";
|
||||
import { newPathAssigner } from "../../../windmill-utils-internal/src/path-utils/path-assigner.ts";
|
||||
|
||||
import { generateHash, getHeaders, writeIfChanged } from "../../utils/utils.ts";
|
||||
import { exts } from "../script/script.ts";
|
||||
@@ -121,14 +122,21 @@ export async function generateFlowLockInternal(
|
||||
}
|
||||
|
||||
log.info(`Recomputing locks of ${changedScripts.join(", ")} in ${folder}`);
|
||||
const fileReader = async (path: string) => await readFile(folder + SEP + path, "utf-8");
|
||||
await replaceInlineScripts(
|
||||
flowValue.value.modules,
|
||||
async (path: string) => await readFile(folder + SEP + path, "utf-8"),
|
||||
fileReader,
|
||||
log,
|
||||
folder + SEP!,
|
||||
SEP,
|
||||
changedScripts
|
||||
);
|
||||
if (flowValue.value.failure_module) {
|
||||
await replaceInlineScripts([flowValue.value.failure_module], fileReader, log, folder + SEP!, SEP, changedScripts);
|
||||
}
|
||||
if (flowValue.value.preprocessor_module) {
|
||||
await replaceInlineScripts([flowValue.value.preprocessor_module], fileReader, log, folder + SEP!, SEP, changedScripts);
|
||||
}
|
||||
|
||||
//removeChangedLocks
|
||||
flowValue.value = await updateFlow(
|
||||
@@ -138,12 +146,20 @@ export async function generateFlowLockInternal(
|
||||
filteredDeps
|
||||
);
|
||||
|
||||
const lockAssigner = newPathAssigner(opts.defaultTs ?? "bun");
|
||||
const inlineScripts = extractInlineScriptsForFlows(
|
||||
flowValue.value.modules,
|
||||
{},
|
||||
SEP,
|
||||
opts.defaultTs
|
||||
opts.defaultTs,
|
||||
lockAssigner
|
||||
);
|
||||
if (flowValue.value.failure_module) {
|
||||
inlineScripts.push(...extractInlineScriptsForFlows([flowValue.value.failure_module], {}, SEP, opts.defaultTs, lockAssigner));
|
||||
}
|
||||
if (flowValue.value.preprocessor_module) {
|
||||
inlineScripts.push(...extractInlineScriptsForFlows([flowValue.value.preprocessor_module], {}, SEP, opts.defaultTs, lockAssigner));
|
||||
}
|
||||
inlineScripts.forEach((s) => {
|
||||
writeIfChanged(process.cwd() + SEP + folder + SEP + s.path, s.content);
|
||||
});
|
||||
@@ -176,7 +192,15 @@ async function filterWorkspaceDependenciesForFlow(
|
||||
rawWorkspaceDependencies: Record<string, string>,
|
||||
folder: string
|
||||
): Promise<Record<string, string>> {
|
||||
const inlineScripts = extractInlineScriptsForFlows(structuredClone(flowValue.modules), {}, SEP, undefined);
|
||||
const clonedValue = structuredClone(flowValue);
|
||||
const depAssigner = newPathAssigner("bun");
|
||||
const inlineScripts = extractInlineScriptsForFlows(clonedValue.modules, {}, SEP, undefined, depAssigner);
|
||||
if (clonedValue.failure_module) {
|
||||
inlineScripts.push(...extractInlineScriptsForFlows([clonedValue.failure_module], {}, SEP, undefined, depAssigner));
|
||||
}
|
||||
if (clonedValue.preprocessor_module) {
|
||||
inlineScripts.push(...extractInlineScriptsForFlows([clonedValue.preprocessor_module], {}, SEP, undefined, depAssigner));
|
||||
}
|
||||
|
||||
// Filter out lock files and map to common interface
|
||||
const scripts = inlineScripts
|
||||
|
||||
@@ -592,14 +592,35 @@ function ZipFSElement(
|
||||
}
|
||||
let inlineScripts;
|
||||
try {
|
||||
const assigner = newPathAssigner(defaultTs, { skipInlineScriptSuffix: getNonDottedPaths() });
|
||||
inlineScripts = extractInlineScriptsForFlows(
|
||||
flow.value.modules as any,
|
||||
{},
|
||||
SEP,
|
||||
defaultTs,
|
||||
undefined, // pathAssigner - let it create one
|
||||
assigner,
|
||||
{ skipInlineScriptSuffix: getNonDottedPaths() },
|
||||
);
|
||||
if (flow.value.failure_module) {
|
||||
inlineScripts.push(...extractInlineScriptsForFlows(
|
||||
[flow.value.failure_module],
|
||||
{},
|
||||
SEP,
|
||||
defaultTs,
|
||||
assigner,
|
||||
{ skipInlineScriptSuffix: getNonDottedPaths() },
|
||||
));
|
||||
}
|
||||
if (flow.value.preprocessor_module) {
|
||||
inlineScripts.push(...extractInlineScriptsForFlows(
|
||||
[flow.value.preprocessor_module],
|
||||
{},
|
||||
SEP,
|
||||
defaultTs,
|
||||
assigner,
|
||||
{ skipInlineScriptSuffix: getNonDottedPaths() },
|
||||
));
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(
|
||||
`Failed to extract inline scripts for flow at path: ${p}`,
|
||||
|
||||
+77
-24
@@ -43,10 +43,12 @@ description: MUST use when writing Go scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# Go
|
||||
@@ -115,10 +117,12 @@ description: MUST use when writing Java scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# Java
|
||||
@@ -167,10 +171,12 @@ description: MUST use when writing GraphQL queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# GraphQL
|
||||
@@ -226,10 +232,12 @@ description: MUST use when writing Rust scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# Rust
|
||||
@@ -315,10 +323,12 @@ description: MUST use when writing Bun Native scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# TypeScript (Bun Native)
|
||||
@@ -967,10 +977,12 @@ description: MUST use when writing PostgreSQL queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# PostgreSQL
|
||||
@@ -992,10 +1004,12 @@ description: MUST use when writing PHP scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# PHP
|
||||
@@ -1063,10 +1077,12 @@ description: MUST use when writing BigQuery queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# BigQuery
|
||||
@@ -1088,10 +1104,12 @@ description: MUST use when writing Bun/TypeScript scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# TypeScript (Bun)
|
||||
@@ -1742,10 +1760,12 @@ description: MUST use when writing C# scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# C#
|
||||
@@ -1797,10 +1817,12 @@ description: MUST use when writing MS SQL Server queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# Microsoft SQL Server (MSSQL)
|
||||
@@ -1822,10 +1844,12 @@ description: MUST use when writing Deno/TypeScript scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# TypeScript (Deno)
|
||||
@@ -2480,10 +2504,12 @@ description: MUST use when writing MySQL queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# MySQL
|
||||
@@ -2505,10 +2531,12 @@ description: MUST use when writing PowerShell scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# PowerShell
|
||||
@@ -2574,10 +2602,12 @@ description: MUST use when writing Snowflake queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# Snowflake
|
||||
@@ -2599,10 +2629,12 @@ description: MUST use when writing Python scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# Python
|
||||
@@ -3404,10 +3436,12 @@ description: MUST use when writing DuckDB queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# DuckDB
|
||||
@@ -3469,10 +3503,12 @@ description: MUST use when writing Bash scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# Bash
|
||||
@@ -3532,10 +3568,12 @@ description: MUST use when writing Native TypeScript scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use \`wmill resource-type list --schema\` to discover available resource types.
|
||||
|
||||
# TypeScript (Native)
|
||||
@@ -4155,10 +4193,12 @@ description: MUST use when creating flows.
|
||||
|
||||
Create a folder ending with \`.flow\` and add a YAML file with the flow definition.
|
||||
For rawscript modules, use \`!inline path/to/script.ts\` for the content key.
|
||||
After writing:
|
||||
After writing, tell the user they can run:
|
||||
- \`wmill flow generate-locks <path_to_flow_folder> --yes\` - Generate lock files for the specific flow you modified (e.g. \`wmill flow generate-locks f/my_folder/my_flow.flow --yes\`)
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
## OpenFlow Schema
|
||||
|
||||
The OpenFlow schema (openflow.openapi.yaml) is the source of truth for flow structure. Refer to OPENFLOW_SCHEMA for the complete type definitions.
|
||||
@@ -4356,7 +4396,7 @@ export async function main(user_id: string) {
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
After creating, generate lock files:
|
||||
After creating, tell the user they can generate lock files by running:
|
||||
\`\`\`bash
|
||||
wmill app generate-locks
|
||||
\`\`\`
|
||||
@@ -4509,6 +4549,8 @@ data:
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Tell the user they can run these commands (do NOT run them yourself):
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| \`wmill app new\` | Create a new raw app interactively |
|
||||
@@ -4525,7 +4567,7 @@ data:
|
||||
3. **Keep runnables focused** - one function per file
|
||||
4. **Use descriptive IDs** - \`get_user.ts\` not \`a.ts\`
|
||||
5. **Always whitelist tables** - add to \`data.tables\` before querying
|
||||
6. **Generate locks** - run \`wmill app generate-locks\` after adding/modifying backend runnables
|
||||
6. **Generate locks** - tell the user to run \`wmill app generate-locks\` after adding/modifying backend runnables
|
||||
`,
|
||||
"triggers": `---
|
||||
name: triggers
|
||||
@@ -4547,6 +4589,8 @@ Examples:
|
||||
|
||||
## CLI Commands
|
||||
|
||||
After writing, tell the user they can run these commands (do NOT run them yourself):
|
||||
|
||||
\`\`\`bash
|
||||
# Push trigger configuration
|
||||
wmill sync push
|
||||
@@ -4596,6 +4640,8 @@ Windmill uses 6-field cron expressions (includes seconds):
|
||||
|
||||
## CLI Commands
|
||||
|
||||
After writing, tell the user they can run these commands (do NOT run them yourself):
|
||||
|
||||
\`\`\`bash
|
||||
# Push schedules to Windmill
|
||||
wmill sync push
|
||||
@@ -4851,7 +4897,7 @@ wmill resource-type list --schema
|
||||
# Get specific resource type schema
|
||||
wmill resource-type get postgresql
|
||||
|
||||
# Push resources
|
||||
# Push resources (tell the user to run this, do NOT run it yourself)
|
||||
wmill sync push
|
||||
\`\`\`
|
||||
`,
|
||||
@@ -4864,7 +4910,7 @@ description: MUST use when using the CLI.
|
||||
|
||||
The Windmill CLI (\`wmill\`) provides commands for managing scripts, flows, apps, and other resources.
|
||||
|
||||
Current version: 1.651.1
|
||||
Current version: 1.653.0
|
||||
|
||||
## Global Options
|
||||
|
||||
@@ -5613,6 +5659,13 @@ properties:
|
||||
key:
|
||||
type: string
|
||||
value: {}
|
||||
auto_offset_reset:
|
||||
type: string
|
||||
enum:
|
||||
- latest
|
||||
- earliest
|
||||
description: Initial offset behavior when consumer group has no committed offset.
|
||||
'latest' starts from new messages only, 'earliest' starts from the beginning.
|
||||
error_handler_path:
|
||||
type: string
|
||||
description: Path to a script or flow to run when the triggered job fails
|
||||
|
||||
@@ -0,0 +1,498 @@
|
||||
/**
|
||||
* Unit tests for failure_module and preprocessor_module inline script
|
||||
* extraction (pull) and replacement (push).
|
||||
*
|
||||
* These tests verify that rawscript content in failure_module and
|
||||
* preprocessor_module is correctly extracted to !inline references
|
||||
* and resolved back, matching the existing behavior for regular modules.
|
||||
*/
|
||||
|
||||
import { expect, test, describe } from "bun:test";
|
||||
import { extractInlineScripts, extractCurrentMapping } from "../windmill-utils-internal/src/inline-scripts/extractor.ts";
|
||||
import { replaceInlineScripts } from "../windmill-utils-internal/src/inline-scripts/replacer.ts";
|
||||
import { newPathAssigner } from "../windmill-utils-internal/src/path-utils/path-assigner.ts";
|
||||
import type { FlowModule } from "../windmill-utils-internal/src/gen/types.gen.ts";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function makeRawscriptModule(
|
||||
id: string,
|
||||
content: string,
|
||||
language: "bun" | "python3" | "deno" = "bun",
|
||||
lock?: string,
|
||||
): FlowModule {
|
||||
return {
|
||||
id,
|
||||
value: {
|
||||
type: "rawscript" as const,
|
||||
content,
|
||||
language,
|
||||
lock: lock,
|
||||
input_transforms: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const noopLogger = {
|
||||
info: () => {},
|
||||
error: () => {},
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// extractInlineScripts — PULL direction
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("extractInlineScripts for failure_module / preprocessor_module", () => {
|
||||
test("extracts rawscript from failure_module wrapped in array", () => {
|
||||
const failureModule = makeRawscriptModule(
|
||||
"failure",
|
||||
'export function main() { throw new Error("handler"); }',
|
||||
"bun",
|
||||
);
|
||||
|
||||
const scripts = extractInlineScripts([failureModule], {}, "/", "bun");
|
||||
|
||||
expect(scripts.length).toBeGreaterThanOrEqual(1);
|
||||
const script = scripts.find((s) => !s.is_lock);
|
||||
expect(script).toBeDefined();
|
||||
expect(script!.content).toBe(
|
||||
'export function main() { throw new Error("handler"); }',
|
||||
);
|
||||
// The module content should have been replaced with an !inline reference
|
||||
expect(failureModule.value.content).toStartWith("!inline ");
|
||||
});
|
||||
|
||||
test("extracts rawscript from preprocessor_module wrapped in array", () => {
|
||||
const preprocessorModule = makeRawscriptModule(
|
||||
"preprocessor",
|
||||
"export function main() { return {}; }",
|
||||
"python3",
|
||||
);
|
||||
|
||||
const scripts = extractInlineScripts(
|
||||
[preprocessorModule],
|
||||
{},
|
||||
"/",
|
||||
"bun",
|
||||
);
|
||||
|
||||
expect(scripts.length).toBeGreaterThanOrEqual(1);
|
||||
const script = scripts.find((s) => !s.is_lock);
|
||||
expect(script).toBeDefined();
|
||||
expect(script!.content).toBe("export function main() { return {}; }");
|
||||
expect(script!.language).toBe("python3");
|
||||
expect(preprocessorModule.value.content).toStartWith("!inline ");
|
||||
});
|
||||
|
||||
test("extracts lock alongside content", () => {
|
||||
const mod = makeRawscriptModule(
|
||||
"failure",
|
||||
"console.log('hi')",
|
||||
"bun",
|
||||
"some-lock-content",
|
||||
);
|
||||
|
||||
const scripts = extractInlineScripts([mod], {}, "/", "bun");
|
||||
|
||||
const contentScript = scripts.find((s) => !s.is_lock);
|
||||
const lockScript = scripts.find((s) => s.is_lock);
|
||||
expect(contentScript).toBeDefined();
|
||||
expect(lockScript).toBeDefined();
|
||||
expect(lockScript!.content).toBe("some-lock-content");
|
||||
expect((mod.value as any).lock).toStartWith("!inline ");
|
||||
});
|
||||
|
||||
test("shared pathAssigner prevents collisions when summaries match", () => {
|
||||
// If a regular module and failure_module share the same summary,
|
||||
// a shared PathAssigner deduplicates via its internal counter.
|
||||
const regular = makeRawscriptModule("a", "code_a", "bun");
|
||||
regular.summary = "my step";
|
||||
const failure = makeRawscriptModule("failure", "code_failure", "bun");
|
||||
failure.summary = "my step"; // same summary — would collide without shared assigner
|
||||
|
||||
const assigner = newPathAssigner("bun");
|
||||
const scripts1 = extractInlineScripts([regular], {}, "/", "bun", assigner);
|
||||
const scripts2 = extractInlineScripts([failure], {}, "/", "bun", assigner);
|
||||
|
||||
const allPaths = [...scripts1, ...scripts2]
|
||||
.filter((s) => !s.is_lock)
|
||||
.map((s) => s.path);
|
||||
|
||||
// All paths should be unique despite identical summaries
|
||||
expect(allPaths.length).toBe(2);
|
||||
expect(new Set(allPaths).size).toBe(2);
|
||||
});
|
||||
|
||||
test("without shared pathAssigner, identical summaries produce duplicate paths", () => {
|
||||
// Demonstrates the problem that sharing a PathAssigner solves.
|
||||
const regular = makeRawscriptModule("a", "code_a", "bun");
|
||||
regular.summary = "my step";
|
||||
const failure = makeRawscriptModule("failure", "code_failure", "bun");
|
||||
failure.summary = "my step";
|
||||
|
||||
// Separate assigners — each starts with a fresh counter
|
||||
const scripts1 = extractInlineScripts([regular], {}, "/", "bun");
|
||||
const scripts2 = extractInlineScripts([failure], {}, "/", "bun");
|
||||
|
||||
const allPaths = [...scripts1, ...scripts2]
|
||||
.filter((s) => !s.is_lock)
|
||||
.map((s) => s.path);
|
||||
|
||||
// Without a shared assigner, the paths collide
|
||||
expect(allPaths.length).toBe(2);
|
||||
expect(new Set(allPaths).size).toBe(1); // both got the same path
|
||||
});
|
||||
|
||||
test("skips non-rawscript failure_module (identity type)", () => {
|
||||
const identityModule: FlowModule = {
|
||||
id: "failure",
|
||||
value: { type: "identity" as any },
|
||||
};
|
||||
const scripts = extractInlineScripts([identityModule], {}, "/", "bun");
|
||||
expect(scripts).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// replaceInlineScripts — PUSH direction
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("replaceInlineScripts for failure_module / preprocessor_module", () => {
|
||||
test("resolves !inline reference back to file content", async () => {
|
||||
const failureModule = makeRawscriptModule(
|
||||
"failure",
|
||||
"!inline failure.inline_script.ts",
|
||||
"bun",
|
||||
);
|
||||
|
||||
const files: Record<string, string> = {
|
||||
"failure.inline_script.ts": 'export function main() { return "error handled"; }',
|
||||
};
|
||||
|
||||
await replaceInlineScripts(
|
||||
[failureModule],
|
||||
async (path) => {
|
||||
if (!(path in files)) throw new Error(`File not found: ${path}`);
|
||||
return files[path];
|
||||
},
|
||||
noopLogger,
|
||||
"/tmp/test/",
|
||||
"/",
|
||||
);
|
||||
|
||||
expect(failureModule.value.content).toBe(
|
||||
'export function main() { return "error handled"; }',
|
||||
);
|
||||
});
|
||||
|
||||
test("resolves !inline reference for preprocessor_module", async () => {
|
||||
const preprocessorModule = makeRawscriptModule(
|
||||
"preprocessor",
|
||||
"!inline preprocessor.inline_script.py",
|
||||
"python3",
|
||||
);
|
||||
|
||||
const files: Record<string, string> = {
|
||||
"preprocessor.inline_script.py": "def main(): return {}",
|
||||
};
|
||||
|
||||
await replaceInlineScripts(
|
||||
[preprocessorModule],
|
||||
async (path) => {
|
||||
if (!(path in files)) throw new Error(`File not found: ${path}`);
|
||||
return files[path];
|
||||
},
|
||||
noopLogger,
|
||||
"/tmp/test/",
|
||||
"/",
|
||||
);
|
||||
|
||||
expect(preprocessorModule.value.content).toBe("def main(): return {}");
|
||||
});
|
||||
|
||||
test("resolves !inline lock reference", async () => {
|
||||
const mod = makeRawscriptModule(
|
||||
"failure",
|
||||
"!inline failure.inline_script.ts",
|
||||
"bun",
|
||||
"!inline failure.inline_script.lock",
|
||||
);
|
||||
|
||||
const files: Record<string, string> = {
|
||||
"failure.inline_script.ts": "code here",
|
||||
"failure.inline_script.lock": "lock-data-here",
|
||||
};
|
||||
|
||||
await replaceInlineScripts(
|
||||
[mod],
|
||||
async (path) => {
|
||||
if (!(path in files)) throw new Error(`File not found: ${path}`);
|
||||
return files[path];
|
||||
},
|
||||
noopLogger,
|
||||
"/tmp/test/",
|
||||
"/",
|
||||
);
|
||||
|
||||
expect(mod.value.content).toBe("code here");
|
||||
expect((mod.value as any).lock).toBe("lock-data-here");
|
||||
});
|
||||
|
||||
test("leaves non-inline content untouched", async () => {
|
||||
const mod = makeRawscriptModule(
|
||||
"failure",
|
||||
"export function main() { return 1; }",
|
||||
"bun",
|
||||
);
|
||||
|
||||
await replaceInlineScripts(
|
||||
[mod],
|
||||
async () => {
|
||||
throw new Error("should not be called");
|
||||
},
|
||||
noopLogger,
|
||||
"/tmp/test/",
|
||||
"/",
|
||||
);
|
||||
|
||||
expect(mod.value.content).toBe("export function main() { return 1; }");
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Round-trip: extract then replace
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("round-trip extract → replace for failure_module / preprocessor_module", () => {
|
||||
test("failure_module content survives extract + replace", async () => {
|
||||
const originalContent = 'export function main(error: any) {\n console.error(error);\n return { handled: true };\n}';
|
||||
const failureModule = makeRawscriptModule(
|
||||
"failure",
|
||||
originalContent,
|
||||
"bun",
|
||||
);
|
||||
|
||||
// PULL: extract inline scripts (mutates module in place)
|
||||
const extracted = extractInlineScripts([failureModule], {}, "/", "bun");
|
||||
expect(failureModule.value.content).toStartWith("!inline ");
|
||||
|
||||
// Build a virtual filesystem from extracted scripts
|
||||
const files: Record<string, string> = {};
|
||||
for (const s of extracted) {
|
||||
files[s.path] = s.content;
|
||||
}
|
||||
|
||||
// PUSH: replace inline references back
|
||||
await replaceInlineScripts(
|
||||
[failureModule],
|
||||
async (path) => {
|
||||
if (!(path in files)) throw new Error(`File not found: ${path}`);
|
||||
return files[path];
|
||||
},
|
||||
noopLogger,
|
||||
"/tmp/test/",
|
||||
"/",
|
||||
);
|
||||
|
||||
expect(failureModule.value.content).toBe(originalContent);
|
||||
});
|
||||
|
||||
test("preprocessor_module content survives extract + replace", async () => {
|
||||
const originalContent = "def main():\n return {\"preprocessed\": True}";
|
||||
const preprocessorModule = makeRawscriptModule(
|
||||
"preprocessor",
|
||||
originalContent,
|
||||
"python3",
|
||||
);
|
||||
|
||||
const extracted = extractInlineScripts(
|
||||
[preprocessorModule],
|
||||
{},
|
||||
"/",
|
||||
"bun",
|
||||
);
|
||||
expect(preprocessorModule.value.content).toStartWith("!inline ");
|
||||
|
||||
const files: Record<string, string> = {};
|
||||
for (const s of extracted) {
|
||||
files[s.path] = s.content;
|
||||
}
|
||||
|
||||
await replaceInlineScripts(
|
||||
[preprocessorModule],
|
||||
async (path) => {
|
||||
if (!(path in files)) throw new Error(`File not found: ${path}`);
|
||||
return files[path];
|
||||
},
|
||||
noopLogger,
|
||||
"/tmp/test/",
|
||||
"/",
|
||||
);
|
||||
|
||||
expect(preprocessorModule.value.content).toBe(originalContent);
|
||||
});
|
||||
|
||||
test("failure_module with lock survives extract + replace", async () => {
|
||||
const originalContent = "export function main() { return 42; }";
|
||||
const originalLock = "package-lock-contents-here";
|
||||
const mod = makeRawscriptModule(
|
||||
"failure",
|
||||
originalContent,
|
||||
"bun",
|
||||
originalLock,
|
||||
);
|
||||
|
||||
const extracted = extractInlineScripts([mod], {}, "/", "bun");
|
||||
|
||||
const files: Record<string, string> = {};
|
||||
for (const s of extracted) {
|
||||
files[s.path] = s.content;
|
||||
}
|
||||
|
||||
await replaceInlineScripts(
|
||||
[mod],
|
||||
async (path) => {
|
||||
if (!(path in files)) throw new Error(`File not found: ${path}`);
|
||||
return files[path];
|
||||
},
|
||||
noopLogger,
|
||||
"/tmp/test/",
|
||||
"/",
|
||||
);
|
||||
|
||||
expect(mod.value.content).toBe(originalContent);
|
||||
expect((mod.value as any).lock).toBe(originalLock);
|
||||
});
|
||||
|
||||
test("full flow with modules + failure_module + preprocessor_module round-trips", async () => {
|
||||
const regularContent = "export function main() { return 'step1'; }";
|
||||
const failureContent = "export function main(e: any) { return e; }";
|
||||
const preprocessorContent = "def main():\n pass";
|
||||
|
||||
const modules = [makeRawscriptModule("a", regularContent, "bun")];
|
||||
const failureModule = makeRawscriptModule("failure", failureContent, "bun");
|
||||
const preprocessorModule = makeRawscriptModule("preprocessor", preprocessorContent, "python3");
|
||||
|
||||
// Extract all (mimicking sync.ts pull logic)
|
||||
const allExtracted = [
|
||||
...extractInlineScripts(modules, {}, "/", "bun"),
|
||||
...extractInlineScripts([failureModule], {}, "/", "bun"),
|
||||
...extractInlineScripts([preprocessorModule], {}, "/", "bun"),
|
||||
];
|
||||
|
||||
// All modules should now have !inline references
|
||||
expect(modules[0].value.content).toStartWith("!inline ");
|
||||
expect(failureModule.value.content).toStartWith("!inline ");
|
||||
expect(preprocessorModule.value.content).toStartWith("!inline ");
|
||||
|
||||
// All paths should be unique
|
||||
const paths = allExtracted.filter((s) => !s.is_lock).map((s) => s.path);
|
||||
expect(new Set(paths).size).toBe(paths.length);
|
||||
|
||||
// Build filesystem
|
||||
const files: Record<string, string> = {};
|
||||
for (const s of allExtracted) {
|
||||
files[s.path] = s.content;
|
||||
}
|
||||
|
||||
const fileReader = async (path: string) => {
|
||||
if (!(path in files)) throw new Error(`File not found: ${path}`);
|
||||
return files[path];
|
||||
};
|
||||
|
||||
// Replace all (mimicking flow.ts push logic)
|
||||
await replaceInlineScripts(modules, fileReader, noopLogger, "/tmp/", "/");
|
||||
await replaceInlineScripts([failureModule], fileReader, noopLogger, "/tmp/", "/");
|
||||
await replaceInlineScripts([preprocessorModule], fileReader, noopLogger, "/tmp/", "/");
|
||||
|
||||
expect(modules[0].value.content).toBe(regularContent);
|
||||
expect(failureModule.value.content).toBe(failureContent);
|
||||
expect(preprocessorModule.value.content).toBe(preprocessorContent);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// extractCurrentMapping
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("extractCurrentMapping for failure_module / preprocessor_module", () => {
|
||||
test("extracts mapping from failure_module via optional param", () => {
|
||||
const failureModule: FlowModule = makeRawscriptModule(
|
||||
"failure",
|
||||
"!inline failure.inline_script.ts",
|
||||
"bun",
|
||||
);
|
||||
|
||||
const mapping = extractCurrentMapping(
|
||||
undefined,
|
||||
{},
|
||||
failureModule,
|
||||
undefined,
|
||||
);
|
||||
|
||||
expect(mapping["failure"]).toBe("failure.inline_script.ts");
|
||||
});
|
||||
|
||||
test("extracts mapping from preprocessor_module via optional param", () => {
|
||||
const preprocessorModule: FlowModule = makeRawscriptModule(
|
||||
"preprocessor",
|
||||
"!inline preprocessor.inline_script.py",
|
||||
"python3",
|
||||
);
|
||||
|
||||
const mapping = extractCurrentMapping(
|
||||
undefined,
|
||||
{},
|
||||
undefined,
|
||||
preprocessorModule,
|
||||
);
|
||||
|
||||
expect(mapping["preprocessor"]).toBe("preprocessor.inline_script.py");
|
||||
});
|
||||
|
||||
test("extracts mapping from modules + failure + preprocessor combined", () => {
|
||||
const modules: FlowModule[] = [
|
||||
makeRawscriptModule("a", "!inline a.inline_script.ts", "bun"),
|
||||
];
|
||||
const failureModule = makeRawscriptModule(
|
||||
"failure",
|
||||
"!inline failure.inline_script.ts",
|
||||
"bun",
|
||||
);
|
||||
const preprocessorModule = makeRawscriptModule(
|
||||
"preprocessor",
|
||||
"!inline preprocessor.inline_script.py",
|
||||
"python3",
|
||||
);
|
||||
|
||||
const mapping = extractCurrentMapping(
|
||||
modules,
|
||||
{},
|
||||
failureModule,
|
||||
preprocessorModule,
|
||||
);
|
||||
|
||||
expect(mapping["a"]).toBe("a.inline_script.ts");
|
||||
expect(mapping["failure"]).toBe("failure.inline_script.ts");
|
||||
expect(mapping["preprocessor"]).toBe("preprocessor.inline_script.py");
|
||||
});
|
||||
|
||||
test("ignores non-inline content in failure_module", () => {
|
||||
const failureModule = makeRawscriptModule(
|
||||
"failure",
|
||||
"export function main() {}",
|
||||
"bun",
|
||||
);
|
||||
|
||||
const mapping = extractCurrentMapping(
|
||||
undefined,
|
||||
{},
|
||||
failureModule,
|
||||
undefined,
|
||||
);
|
||||
|
||||
expect(mapping["failure"]).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -150,8 +150,17 @@ export function extractInlineScripts(
|
||||
*/
|
||||
export function extractCurrentMapping(
|
||||
modules: FlowModule[] | undefined,
|
||||
mapping: Record<string, string> = {}
|
||||
mapping: Record<string, string> = {},
|
||||
failureModule?: FlowModule,
|
||||
preprocessorModule?: FlowModule,
|
||||
): Record<string, string> {
|
||||
if (failureModule) {
|
||||
extractCurrentMapping([failureModule], mapping);
|
||||
}
|
||||
if (preprocessorModule) {
|
||||
extractCurrentMapping([preprocessorModule], mapping);
|
||||
}
|
||||
|
||||
if (!modules || !Array.isArray(modules)) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
@@ -102,8 +102,8 @@
|
||||
})
|
||||
}
|
||||
|
||||
if (defaultLang !== undefined) {
|
||||
setupModel(defaultLang, defaultOriginal, defaultModified, defaultModifiedLang)
|
||||
if (defaultLang !== undefined || defaultOriginal !== undefined || defaultModified !== undefined) {
|
||||
setupModel(defaultLang ?? 'plaintext', defaultOriginal, defaultModified, defaultModifiedLang)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
The Windmill CLI (`wmill`) provides commands for managing scripts, flows, apps, and other resources.
|
||||
|
||||
Current version: 1.651.1
|
||||
Current version: 1.653.0
|
||||
|
||||
## Global Options
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
|
||||
Create a folder ending with `.flow` and add a YAML file with the flow definition.
|
||||
For rawscript modules, use `!inline path/to/script.ts` for the content key.
|
||||
After writing:
|
||||
After writing, tell the user they can run:
|
||||
- `wmill flow generate-locks <path_to_flow_folder> --yes` - Generate lock files for the specific flow you modified (e.g. `wmill flow generate-locks f/my_folder/my_flow.flow --yes`)
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
## OpenFlow Schema
|
||||
|
||||
The OpenFlow schema (openflow.openapi.yaml) is the source of truth for flow structure. Refer to OPENFLOW_SCHEMA for the complete type definitions.
|
||||
|
||||
@@ -35,10 +35,12 @@ export const FLOW_BASE = `# Windmill Flow Building Guide
|
||||
|
||||
Create a folder ending with \`.flow\` and add a YAML file with the flow definition.
|
||||
For rawscript modules, use \`!inline path/to/script.ts\` for the content key.
|
||||
After writing:
|
||||
After writing, tell the user they can run:
|
||||
- \`wmill flow generate-locks <path_to_flow_folder> --yes\` - Generate lock files for the specific flow you modified (e.g. \`wmill flow generate-locks f/my_folder/my_flow.flow --yes\`)
|
||||
- \`wmill sync push\` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
## OpenFlow Schema
|
||||
|
||||
The OpenFlow schema (openflow.openapi.yaml) is the source of truth for flow structure. Refer to OPENFLOW_SCHEMA for the complete type definitions.
|
||||
@@ -1356,7 +1358,7 @@ export const CLI_COMMANDS = `# Windmill CLI Commands
|
||||
|
||||
The Windmill CLI (\`wmill\`) provides commands for managing scripts, flows, apps, and other resources.
|
||||
|
||||
Current version: 1.651.1
|
||||
Current version: 1.653.0
|
||||
|
||||
## Global Options
|
||||
|
||||
|
||||
@@ -25,6 +25,13 @@ properties:
|
||||
key:
|
||||
type: string
|
||||
value: {}
|
||||
auto_offset_reset:
|
||||
type: string
|
||||
enum:
|
||||
- latest
|
||||
- earliest
|
||||
description: Initial offset behavior when consumer group has no committed offset.
|
||||
'latest' starts from new messages only, 'earliest' starts from the beginning.
|
||||
error_handler_path:
|
||||
type: string
|
||||
description: Path to a script or flow to run when the triggered job fails
|
||||
|
||||
@@ -7,7 +7,7 @@ description: MUST use when using the CLI.
|
||||
|
||||
The Windmill CLI (`wmill`) provides commands for managing scripts, flows, apps, and other resources.
|
||||
|
||||
Current version: 1.651.1
|
||||
Current version: 1.653.0
|
||||
|
||||
## Global Options
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ export async function main(user_id: string) {
|
||||
}
|
||||
```
|
||||
|
||||
After creating, generate lock files:
|
||||
After creating, tell the user they can generate lock files by running:
|
||||
```bash
|
||||
wmill app generate-locks
|
||||
```
|
||||
@@ -237,6 +237,8 @@ data:
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Tell the user they can run these commands (do NOT run them yourself):
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `wmill app new` | Create a new raw app interactively |
|
||||
@@ -253,4 +255,4 @@ data:
|
||||
3. **Keep runnables focused** - one function per file
|
||||
4. **Use descriptive IDs** - `get_user.ts` not `a.ts`
|
||||
5. **Always whitelist tables** - add to `data.tables` before querying
|
||||
6. **Generate locks** - run `wmill app generate-locks` after adding/modifying backend runnables
|
||||
6. **Generate locks** - tell the user to run `wmill app generate-locks` after adding/modifying backend runnables
|
||||
|
||||
@@ -242,6 +242,6 @@ wmill resource-type list --schema
|
||||
# Get specific resource type schema
|
||||
wmill resource-type get postgresql
|
||||
|
||||
# Push resources
|
||||
# Push resources (tell the user to run this, do NOT run it yourself)
|
||||
wmill sync push
|
||||
```
|
||||
|
||||
@@ -39,6 +39,8 @@ Windmill uses 6-field cron expressions (includes seconds):
|
||||
|
||||
## CLI Commands
|
||||
|
||||
After writing, tell the user they can run these commands (do NOT run them yourself):
|
||||
|
||||
```bash
|
||||
# Push schedules to Windmill
|
||||
wmill sync push
|
||||
|
||||
@@ -18,6 +18,8 @@ Examples:
|
||||
|
||||
## CLI Commands
|
||||
|
||||
After writing, tell the user they can run these commands (do NOT run them yourself):
|
||||
|
||||
```bash
|
||||
# Push trigger configuration
|
||||
wmill sync push
|
||||
|
||||
@@ -9,10 +9,12 @@ description: MUST use when creating flows.
|
||||
|
||||
Create a folder ending with `.flow` and add a YAML file with the flow definition.
|
||||
For rawscript modules, use `!inline path/to/script.ts` for the content key.
|
||||
After writing:
|
||||
After writing, tell the user they can run:
|
||||
- `wmill flow generate-locks <path_to_flow_folder> --yes` - Generate lock files for the specific flow you modified (e.g. `wmill flow generate-locks f/my_folder/my_flow.flow --yes`)
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
## OpenFlow Schema
|
||||
|
||||
The OpenFlow schema (openflow.openapi.yaml) is the source of truth for flow structure. Refer to OPENFLOW_SCHEMA for the complete type definitions.
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing Bash scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# Bash
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing BigQuery queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# BigQuery
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing Bun/TypeScript scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# TypeScript (Bun)
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing Bun Native scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# TypeScript (Bun Native)
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing C# scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# C#
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing Deno/TypeScript scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# TypeScript (Deno)
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing DuckDB queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# DuckDB
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing Go scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# Go
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing GraphQL queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# GraphQL
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing Java scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# Java
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing MS SQL Server queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# Microsoft SQL Server (MSSQL)
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing MySQL queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# MySQL
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing Native TypeScript scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# TypeScript (Native)
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing PHP scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# PHP
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing PostgreSQL queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# PostgreSQL
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing PowerShell scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# PowerShell
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing Python scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# Python
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing Rust scripts.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# Rust
|
||||
|
||||
@@ -5,10 +5,12 @@ description: MUST use when writing Snowflake queries.
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types.
|
||||
|
||||
# Snowflake
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
|
||||
Create a folder ending with `.flow` and add a YAML file with the flow definition.
|
||||
For rawscript modules, use `!inline path/to/script.ts` for the content key.
|
||||
After writing:
|
||||
After writing, tell the user they can run:
|
||||
- `wmill flow generate-locks <path_to_flow_folder> --yes` - Generate lock files for the specific flow you modified (e.g. `wmill flow generate-locks f/my_folder/my_flow.flow --yes`)
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
## OpenFlow Schema
|
||||
|
||||
The OpenFlow schema (openflow.openapi.yaml) is the source of truth for flow structure. Refer to OPENFLOW_SCHEMA for the complete type definitions.
|
||||
|
||||
@@ -79,7 +79,7 @@ export async function main(user_id: string) {
|
||||
}
|
||||
```
|
||||
|
||||
After creating, generate lock files:
|
||||
After creating, tell the user they can generate lock files by running:
|
||||
```bash
|
||||
wmill app generate-locks
|
||||
```
|
||||
@@ -232,6 +232,8 @@ data:
|
||||
|
||||
## CLI Commands
|
||||
|
||||
Tell the user they can run these commands (do NOT run them yourself):
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `wmill app new` | Create a new raw app interactively |
|
||||
@@ -248,4 +250,4 @@ data:
|
||||
3. **Keep runnables focused** - one function per file
|
||||
4. **Use descriptive IDs** - `get_user.ts` not `a.ts`
|
||||
5. **Always whitelist tables** - add to `data.tables` before querying
|
||||
6. **Generate locks** - run `wmill app generate-locks` after adding/modifying backend runnables
|
||||
6. **Generate locks** - tell the user to run `wmill app generate-locks` after adding/modifying backend runnables
|
||||
|
||||
@@ -237,6 +237,6 @@ wmill resource-type list --schema
|
||||
# Get specific resource type schema
|
||||
wmill resource-type get postgresql
|
||||
|
||||
# Push resources
|
||||
# Push resources (tell the user to run this, do NOT run it yourself)
|
||||
wmill sync push
|
||||
```
|
||||
|
||||
@@ -34,6 +34,8 @@ Windmill uses 6-field cron expressions (includes seconds):
|
||||
|
||||
## CLI Commands
|
||||
|
||||
After writing, tell the user they can run these commands (do NOT run them yourself):
|
||||
|
||||
```bash
|
||||
# Push schedules to Windmill
|
||||
wmill sync push
|
||||
|
||||
@@ -13,6 +13,8 @@ Examples:
|
||||
|
||||
## CLI Commands
|
||||
|
||||
After writing, tell the user they can run these commands (do NOT run them yourself):
|
||||
|
||||
```bash
|
||||
# Push trigger configuration
|
||||
wmill sync push
|
||||
|
||||
@@ -763,10 +763,12 @@ def generate_skills(
|
||||
# CLI intro for script skills
|
||||
script_cli_intro = """## CLI Commands
|
||||
|
||||
Place scripts in a folder. After writing, run:
|
||||
Place scripts in a folder. After writing, tell the user they can run:
|
||||
- `wmill script generate-metadata` - Generate .script.yaml and .lock files
|
||||
- `wmill sync push` - Deploy to Windmill
|
||||
|
||||
Do NOT run these commands yourself. Instead, inform the user that they should run them.
|
||||
|
||||
Use `wmill resource-type list --schema` to discover available resource types."""
|
||||
|
||||
skills_generated = []
|
||||
|
||||
Reference in New Issue
Block a user