mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 00:02:19 +00:00
a41b9e47e2
* feat: branch specific items for cli * error on wmill.yaml parsing errors * also search for wmill.yaml in parent dirs when git * git_branches -> gitBranches * Update cli/src/core/specific_items.ts Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * sanitize branch name (regex + fs path) * improve sanitatino * robust relative paths * hubpath --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
319 lines
9.8 KiB
TypeScript
319 lines
9.8 KiB
TypeScript
import {
|
|
colors,
|
|
Command,
|
|
log,
|
|
yamlStringify,
|
|
Confirm,
|
|
} from "../../../deps.ts";
|
|
import { GlobalOptions } from "../../types.ts";
|
|
import { readLockfile } from "../../utils/metadata.ts";
|
|
import { SCRIPT_GUIDANCE } from "../../guidance/script_guidance.ts";
|
|
import { FLOW_GUIDANCE } from "../../guidance/flow_guidance.ts";
|
|
|
|
export interface InitOptions {
|
|
useDefault?: boolean;
|
|
useBackend?: boolean;
|
|
repository?: string;
|
|
workspace?: string;
|
|
debug?: unknown;
|
|
showDiffs?: boolean;
|
|
token?: string;
|
|
baseUrl?: string;
|
|
configDir?: string;
|
|
bindProfile?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Bootstrap a windmill project with a wmill.yaml file
|
|
*/
|
|
async function initAction(opts: InitOptions) {
|
|
if (await Deno.stat("wmill.yaml").catch(() => null)) {
|
|
log.error(colors.red("wmill.yaml already exists"));
|
|
} else {
|
|
// Import DEFAULT_SYNC_OPTIONS from conf.ts
|
|
const { DEFAULT_SYNC_OPTIONS } = await import("../../core/conf.ts");
|
|
|
|
// Create initial config with defaults
|
|
const initialConfig = { ...DEFAULT_SYNC_OPTIONS } as any;
|
|
|
|
// Add branch structure
|
|
const { isGitRepository, getCurrentGitBranch } = await import(
|
|
"../../utils/git.ts"
|
|
);
|
|
if (isGitRepository()) {
|
|
const currentBranch = getCurrentGitBranch();
|
|
if (currentBranch) {
|
|
initialConfig.gitBranches = {
|
|
[currentBranch]: { overrides: {} },
|
|
};
|
|
} else {
|
|
initialConfig.gitBranches = {};
|
|
}
|
|
} else {
|
|
initialConfig.gitBranches = {};
|
|
}
|
|
|
|
await Deno.writeTextFile("wmill.yaml", yamlStringify(initialConfig));
|
|
log.info(colors.green("wmill.yaml created with default settings"));
|
|
|
|
// Create lock file
|
|
await readLockfile();
|
|
|
|
// Offer to bind workspace profile to current branch
|
|
if (isGitRepository()) {
|
|
const { getActiveWorkspace } = await import("../workspace/workspace.ts");
|
|
const activeWorkspace = await getActiveWorkspace(
|
|
opts as GlobalOptions
|
|
);
|
|
const currentBranch = getCurrentGitBranch();
|
|
|
|
if (activeWorkspace && currentBranch) {
|
|
// Determine binding behavior based on flags
|
|
const shouldBind = opts.bindProfile === true;
|
|
const shouldPrompt =
|
|
opts.bindProfile === undefined &&
|
|
Deno.stdin.isTerminal() &&
|
|
!opts.useDefault;
|
|
const shouldSkip =
|
|
opts.bindProfile === false ||
|
|
opts.useDefault ||
|
|
(!Deno.stdin.isTerminal() && opts.bindProfile === undefined);
|
|
|
|
if (shouldSkip) {
|
|
return;
|
|
}
|
|
|
|
// Show workspace info if we're binding or prompting
|
|
if (shouldBind || shouldPrompt) {
|
|
log.info(
|
|
colors.yellow(
|
|
`\nCurrent Git branch: ${colors.bold(currentBranch)}`
|
|
)
|
|
);
|
|
log.info(
|
|
colors.yellow(
|
|
`Active workspace profile: ${colors.bold(
|
|
activeWorkspace.name
|
|
)}`
|
|
)
|
|
);
|
|
log.info(
|
|
colors.yellow(
|
|
` ${activeWorkspace.workspaceId} on ${activeWorkspace.remote}`
|
|
)
|
|
);
|
|
}
|
|
|
|
if (
|
|
shouldBind ||
|
|
(shouldPrompt &&
|
|
(await Confirm.prompt({
|
|
message: "Bind workspace profile to current Git branch?",
|
|
default: true,
|
|
})))
|
|
) {
|
|
// Update the config with workspace binding
|
|
const currentConfig = await import("../../core/conf.ts").then((m) =>
|
|
m.readConfigFile()
|
|
);
|
|
if (!currentConfig.gitBranches) {
|
|
currentConfig.gitBranches = {};
|
|
}
|
|
if (!currentConfig.gitBranches[currentBranch]) {
|
|
currentConfig.gitBranches[currentBranch] = { overrides: {} };
|
|
}
|
|
|
|
currentConfig.gitBranches[currentBranch].baseUrl =
|
|
activeWorkspace.remote;
|
|
currentConfig.gitBranches[currentBranch].workspaceId =
|
|
activeWorkspace.workspaceId;
|
|
|
|
await Deno.writeTextFile(
|
|
"wmill.yaml",
|
|
yamlStringify(currentConfig)
|
|
);
|
|
|
|
log.info(
|
|
colors.green(
|
|
`✓ Bound branch '${currentBranch}' to workspace '${activeWorkspace.name}'`
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for backend git-sync settings unless --use-default is specified
|
|
if (!opts.useDefault) {
|
|
try {
|
|
const { requireLogin } = await import("../../core/auth.ts");
|
|
const { resolveWorkspace } = await import("../../core/context.ts");
|
|
|
|
// Check if user has workspace configured
|
|
const { getActiveWorkspace } = await import("../workspace/workspace.ts");
|
|
const activeWorkspace = await getActiveWorkspace(
|
|
opts as GlobalOptions
|
|
);
|
|
|
|
if (!activeWorkspace) {
|
|
log.info("No workspace configured. Using default settings.");
|
|
log.info(
|
|
"You can configure a workspace later with 'wmill workspace add'"
|
|
);
|
|
return;
|
|
}
|
|
|
|
await requireLogin(opts as GlobalOptions);
|
|
const workspace = await resolveWorkspace(opts as GlobalOptions);
|
|
|
|
const wmill = await import("../../../gen/services.gen.ts");
|
|
const settings = await wmill.getSettings({
|
|
workspace: workspace.workspaceId,
|
|
});
|
|
|
|
if (
|
|
settings.git_sync?.repositories &&
|
|
settings.git_sync.repositories.length > 0
|
|
) {
|
|
let useBackendSettings = opts.useBackend;
|
|
|
|
// If repository is specified, implicitly use backend settings
|
|
if (opts.repository && !opts.useDefault) {
|
|
useBackendSettings = true;
|
|
}
|
|
|
|
if (useBackendSettings === undefined) {
|
|
// Interactive prompt
|
|
const { Select } = await import("../../../deps.ts");
|
|
const choice = await Select.prompt({
|
|
message:
|
|
"Git-sync settings found on backend. What would you like to do?",
|
|
options: [
|
|
{
|
|
name: "Use backend git-sync settings",
|
|
value: "backend",
|
|
},
|
|
{
|
|
name: "Use default settings",
|
|
value: "default",
|
|
},
|
|
{
|
|
name: "Cancel",
|
|
value: "cancel",
|
|
},
|
|
],
|
|
});
|
|
|
|
if (choice === "cancel") {
|
|
// Clean up the created files
|
|
try {
|
|
await Deno.remove("wmill.yaml");
|
|
await Deno.remove("wmill-lock.yaml");
|
|
} catch (e) {
|
|
// Ignore cleanup errors
|
|
}
|
|
log.info("Init cancelled");
|
|
Deno.exit(0);
|
|
}
|
|
|
|
useBackendSettings = choice === "backend";
|
|
}
|
|
|
|
if (useBackendSettings) {
|
|
log.info("Applying git-sync settings from backend...");
|
|
|
|
// Import and run the pull git-sync settings logic
|
|
const { pullGitSyncSettings } = await import(
|
|
"../gitsync-settings/gitsync-settings.ts"
|
|
);
|
|
await pullGitSyncSettings({
|
|
...(opts as GlobalOptions),
|
|
repository: opts.repository,
|
|
jsonOutput: false,
|
|
diff: false,
|
|
replace: true, // Auto-replace when using backend settings during init
|
|
});
|
|
|
|
log.info(
|
|
colors.green("Git-sync settings applied from backend")
|
|
);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// If there's an error checking backend settings, just continue with defaults
|
|
log.warn(
|
|
`Could not check backend for git-sync settings: ${error.message}`
|
|
);
|
|
log.info("Continuing with default settings");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create .cursor/rules directory and files with SCRIPT_GUIDANCE content
|
|
try {
|
|
const scriptGuidanceContent = SCRIPT_GUIDANCE;
|
|
const flowGuidanceContent = FLOW_GUIDANCE;
|
|
|
|
// Create .cursor/rules directory
|
|
await Deno.mkdir(".cursor/rules", { recursive: true });
|
|
|
|
// Create windmill.mdc file
|
|
if (!(await Deno.stat(".cursor/rules/script.mdc").catch(() => null))) {
|
|
await Deno.writeTextFile(
|
|
".cursor/rules/script.mdc",
|
|
scriptGuidanceContent
|
|
);
|
|
log.info(colors.green("Created .cursor/rules/script.mdc"));
|
|
}
|
|
|
|
if (!(await Deno.stat(".cursor/rules/flow.mdc").catch(() => null))) {
|
|
await Deno.writeTextFile(
|
|
".cursor/rules/flow.mdc",
|
|
flowGuidanceContent
|
|
);
|
|
log.info(colors.green("Created .cursor/rules/flow.mdc"));
|
|
}
|
|
|
|
// Create CLAUDE.md file
|
|
if (!(await Deno.stat("CLAUDE.md").catch(() => null))) {
|
|
await Deno.writeTextFile(
|
|
"CLAUDE.md",
|
|
`
|
|
# Claude
|
|
|
|
You are a helpful assistant that can help with Windmill scripts and flows creation.
|
|
|
|
## Script Guidance
|
|
${scriptGuidanceContent}
|
|
|
|
## Flow Guidance
|
|
${flowGuidanceContent}
|
|
`
|
|
);
|
|
log.info(colors.green("Created CLAUDE.md"));
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
log.warn(`Could not create guidance files: ${error.message}`);
|
|
} else {
|
|
log.warn(`Could not create guidance files: ${error}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
const command = new Command()
|
|
.description("Bootstrap a windmill project with a wmill.yaml file")
|
|
.option("--use-default", "Use default settings without checking backend")
|
|
.option("--use-backend", "Use backend git-sync settings if available")
|
|
.option(
|
|
"--repository <repo:string>",
|
|
"Specify repository path (e.g., u/user/repo) when using backend settings"
|
|
)
|
|
.option(
|
|
"--bind-profile",
|
|
"Automatically bind active workspace profile to current Git branch"
|
|
)
|
|
.option("--no-bind-profile", "Skip workspace profile binding prompt")
|
|
.action(initAction as any);
|
|
|
|
export default command;
|