mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-13 08:02:47 +00:00
* fix(cli): prevent duplicate 'Using non-dotted paths' log message Add a flag to track whether the message has already been logged, so it only prints once even if setNonDottedPaths is called multiple times. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(cli): add --branch option to sync pull/push commands Add a --branch argument that allows overriding the current git branch for sync operations. This enables: - Using branch-specific settings even when not in a git repository - Overriding the detected git branch when needed The branch override is applied to: - getEffectiveSettings() for branch-specific config overrides - getSpecificItemsForCurrentBranch() for branch-specific items Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(cli): correct log message for workspace fork branches Use rawGitBranch instead of currentBranch in the log message when showing the origin of a workspace fork branch. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(cli): extend --branch support to specificItems functionality Update getBranchSpecificPath and isCurrentBranchFile to accept optional branchOverride parameter. This ensures that branch-specific file handling (for variables, resources, triggers) works correctly with --branch flag. Updated functions: - getBranchSpecificPath(): now accepts branchOverride - isCurrentBranchFile(): now accepts branchOverride - elementsToMap(): now accepts branchOverride - compareDynFSElement(): now accepts branchOverride - prettyChanges(): now accepts branchOverride All call sites updated to pass opts.branch through the call chain. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(cli): resolve TypeScript type errors - Fix Timeout type in dev.ts using ReturnType<typeof setTimeout> - Add proper type casts for unknown error types - Cast FlowModule to any to resolve generated type mismatch - Cast Uint8Array to BlobPart for Blob constructor compatibility Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(cli): add unit tests for branch detection and --branch override - Add specific_items.test.ts with 35 tests covering: - toBranchSpecificPath and fromBranchSpecificPath conversions - isSpecificItem pattern matching - isBranchSpecificFile detection - Round-trip path conversions - branchOverride parameter functionality - Add conf_branch_override.test.ts with 6 tests covering: - getEffectiveSettings with branchOverride parameter - Branch-specific overrides application - promotionOverrides precedence - Fallback to top-level settings - Fix containerized_backend.ts to use dynamic paths instead of hardcoded user home directories Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(cli): add conf.ts barrel file for test imports Re-exports from src/core/conf.ts to support existing test imports. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * all * fix(cli): pass --branch override to workspace resolution Previously, the --branch flag was only used for config resolution but not for workspace resolution. This caused confusing log messages that showed the git branch (e.g., master) before showing the override branch. Changes: - Add branchOverride parameter to tryResolveBranchWorkspace() - Add branchOverride parameter to resolveWorkspace() - Pass opts.branch from sync pull/push to resolveWorkspace() - Log "Using branch override" early in workspace resolution - Remove duplicate log from getEffectiveSettings() Now when using --branch foobar, the logs show: Using branch override: foobar Applied settings for Git branch: foobar ... Instead of the confusing previous output that mentioned both master and foobar. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(cli): remove redundant branch detection in elementsToMap isCurrentBranchFile() already validates that a branch exists (via branchOverride or git detection) before returning true. No need to pre-compute currentBranch before calling it. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
308 lines
9.8 KiB
TypeScript
308 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";
|
|
import { getActiveWorkspaceOrFallback } from "../workspace/workspace.ts";
|
|
import { generateRTNamespace } from "../resource-type/resource-type.ts";
|
|
import { CLI_COMMANDS } from "../../guidance/prompts.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 = {};
|
|
}
|
|
|
|
initialConfig.nonDottedPaths = true;
|
|
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 activeWorkspace = await getActiveWorkspaceOrFallback(
|
|
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 != true &&
|
|
(opts.useDefault || !Deno.stdin.isTerminal());
|
|
|
|
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: {} };
|
|
}
|
|
|
|
log.info(
|
|
`binding branch ${currentBranch} to workspace ${activeWorkspace.name} on ${activeWorkspace.remote}`
|
|
);
|
|
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 as 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;
|
|
const cliCommandsContent = CLI_COMMANDS;
|
|
|
|
// Create AGENTS.md file
|
|
if (!(await Deno.stat("AGENTS.md").catch(() => null))) {
|
|
await Deno.writeTextFile(
|
|
"AGENTS.md",
|
|
`
|
|
You are a helpful assistant that can help with Windmill scripts and flows creation.
|
|
|
|
## Script Guidance
|
|
${scriptGuidanceContent}
|
|
|
|
## Flow Guidance
|
|
${flowGuidanceContent}
|
|
|
|
## CLI Commands
|
|
${cliCommandsContent}
|
|
`
|
|
);
|
|
log.info(colors.green("Created AGENTS.md"));
|
|
}
|
|
|
|
// Create CLAUDE.md file, referencing AGENTS.md
|
|
if (!(await Deno.stat("CLAUDE.md").catch(() => null))) {
|
|
await Deno.writeTextFile("CLAUDE.md", "Instructions are in @AGENTS.md");
|
|
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}`);
|
|
}
|
|
}
|
|
|
|
// Generate resource type namespace
|
|
try {
|
|
await generateRTNamespace(opts as GlobalOptions);
|
|
} catch (error) {
|
|
log.warn(
|
|
`Could not pull resource types and generate TypeScript namespace: ${
|
|
error instanceof Error ? error.message : 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;
|