From b9ea46b78bd85df4e287cf7628ecff7cb1c6dc5f Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 30 Nov 2025 15:15:27 +0000 Subject: [PATCH] fix(cli): cli behave as expected in forked workspaces --- cli/src/commands/workspace/fork.ts | 17 +-- cli/src/core/context.ts | 141 ++++++++++-------- .../src/lib/components/ScriptEditor.svelte | 65 +++++--- 3 files changed, 134 insertions(+), 89 deletions(-) diff --git a/cli/src/commands/workspace/fork.ts b/cli/src/commands/workspace/fork.ts index 1a0d3a45b7..05292e58e0 100644 --- a/cli/src/commands/workspace/fork.ts +++ b/cli/src/commands/workspace/fork.ts @@ -1,7 +1,7 @@ // deno-lint-ignore-file no-explicit-any import { GlobalOptions } from "../../types.ts"; import { colors, Input, log, setClient } from "../../../deps.ts"; -import { addWorkspace, allWorkspaces, list, removeWorkspace } from "./workspace.ts"; +import { allWorkspaces, list, removeWorkspace } from "./workspace.ts"; import * as wmill from "../../../gen/services.gen.ts"; import { getCurrentGitBranch, getOriginalBranchForWorkspaceForks, isGitRepository } from "../../utils/git.ts"; import { WM_FORK_PREFIX } from "../../main.ts"; @@ -123,19 +123,14 @@ async function createWorkspaceFork( throw error; } - await addWorkspace( - { - name: workspaceName, - remote: remote, - workspaceId: trueWorkspaceId, - token: token, - }, - opts - ); const newBranchName = `${WM_FORK_PREFIX}/${clonedBranchName}/${workspaceId}` - log.info(`Created forked workspace ${trueWorkspaceId}. To start contributing to your fork, create and push edits to the branch \`${newBranchName}\` by using the command:\n\n\t`+colors.white(`git checkout -b ${newBranchName}`) + `\n\nThe changes will then be reflected in your fork if you've setup the git sync workflows correctly.`); + log.info(`Created forked workspace ${trueWorkspaceId}. To start contributing to your fork, create and push edits to the branch \`${newBranchName}\` by using the command: + +\t`+colors.white(`git checkout -b ${newBranchName}`) + ` + +When doing operations on the forked workspace, it will use the remote setup in gitBranches for the branch it was forked from.`); } async function deleteWorkspaceFork( diff --git a/cli/src/core/context.ts b/cli/src/core/context.ts index 902c9b7435..a2b31b1125 100644 --- a/cli/src/core/context.ts +++ b/cli/src/core/context.ts @@ -83,6 +83,74 @@ async function selectFromMultipleProfiles( return selectedProfile; } +/** + * Prompts the user to create a new workspace profile interactively + */ +async function createWorkspaceProfileInteractively( + normalizedBaseUrl: string, + workspaceId: string, + currentBranch: string, + opts: GlobalOptions, + context: { rawBranch: string; isForked: boolean } +): Promise { + // Log appropriate message based on context + if (!context.isForked) { + log.info(colors.yellow( + `\nNo workspace profile found for branch '${context.rawBranch}'\n` + + `(${normalizedBaseUrl}, ${workspaceId})` + )); + } else { + log.info(colors.yellow( + `\nNo workspace profile was found for this forked workspace\n` + + `(${normalizedBaseUrl}, ${workspaceId})` + )); + } + + if (!Deno.stdin.isTerminal() || !Deno.stdout.isTerminal()) { + log.info("Not a TTY, cannot create profile interactively. Use 'wmill workspace add' first."); + return undefined; + } + + const shouldCreate = await Confirm.prompt({ + message: "Would you like to create a new workspace profile?", + default: true, + }); + + if (!shouldCreate) { + return undefined; + } + + // Prompt for profile details + const profileName = await Input.prompt({ + message: "Profile name", + default: workspaceId, + }); + + const token = await loginInteractive(normalizedBaseUrl); + if (!token) { + log.error("Failed to obtain token"); + return undefined; + } + + // Create the new profile + const newWorkspace: Workspace = { + name: profileName, + remote: normalizedBaseUrl, + workspaceId: workspaceId, + token: token, + }; + + await addWorkspace(newWorkspace, opts); + + // Set as last used for this branch + await setLastUsedProfile(currentBranch, normalizedBaseUrl, workspaceId, profileName, opts.configDir); + + log.info(colors.green(`✓ Created profile '${profileName}' for ${workspaceId} on ${normalizedBaseUrl}`)); + log.info(colors.green(`✓ Profile '${profileName}' is now active`)); + + return newWorkspace; +} + export type Context = { workspace: string; baseUrl: string; @@ -134,6 +202,7 @@ export async function tryResolveBranchWorkspace( const originalBranchIfForked = getOriginalBranchForWorkspaceForks(rawBranch); const workspaceIdIfForked = getWorkspaceIdForWorkspaceForkFromBranchName(rawBranch); if (originalBranchIfForked) { + log.info(`Using original branch \`${originalBranchIfForked}\` for finding workspace profile from gitBranches section in wmill.yaml`); currentBranch = originalBranchIfForked; } else { currentBranch = rawBranch; @@ -149,10 +218,7 @@ export async function tryResolveBranchWorkspace( } let { baseUrl, workspaceId } = branchConfig; - if (workspaceIdIfForked) { - workspaceId = workspaceIdIfForked; - log.info(`Inferred workspace id \`${workspaceId}\` from branch name because this is a workspace fork branch (\`${rawBranch}\`). `); - } + let normalizedBaseUrl: string; try { normalizedBaseUrl = new URL(baseUrl).toString(); @@ -169,61 +235,18 @@ export async function tryResolveBranchWorkspace( if (matchingProfiles.length === 0) { // No matching profile exists - prompt to create one - if (!originalBranchIfForked) { - log.info(colors.yellow( - `\nNo workspace profile found for branch '${rawBranch}'\n` + - `(${normalizedBaseUrl}, ${workspaceId})` - )); - } else { - log.info(colors.yellow( - `\nNo workspace profile was found for this forked workspace\n` + - `(${normalizedBaseUrl}, ${workspaceId})` - )); - } + return await createWorkspaceProfileInteractively( + normalizedBaseUrl, + workspaceId, + currentBranch, + opts, + { rawBranch, isForked: !!originalBranchIfForked } + ); + } - if (!Deno.stdin.isTerminal() || !Deno.stdout.isTerminal()) { - log.info("Not a TTY, cannot create profile interactively. Use 'wmill workspace add' first."); - return undefined; - } - - const shouldCreate = await Confirm.prompt({ - message: "Would you like to create a new workspace profile?", - default: true, - }); - - if (!shouldCreate) { - return undefined; - } - - // Prompt for profile details - const profileName = await Input.prompt({ - message: "Profile name", - default: workspaceId, - }); - - const token = await loginInteractive(normalizedBaseUrl); - if (!token) { - log.error("Failed to obtain token"); - return undefined; - } - - // Create the new profile - const newWorkspace: Workspace = { - name: profileName, - remote: normalizedBaseUrl, - workspaceId: workspaceId, - token: token, - }; - - await addWorkspace(newWorkspace, opts); - - // Set as last used for this branch - await setLastUsedProfile(currentBranch, normalizedBaseUrl, workspaceId, profileName, opts.configDir); - - log.info(colors.green(`✓ Created profile '${profileName}' for ${workspaceId} on ${normalizedBaseUrl}`)); - log.info(colors.green(`✓ Profile '${profileName}' is now active`)); - - return newWorkspace; + if (workspaceIdIfForked) { + workspaceId = workspaceIdIfForked; + log.info(`Inferred workspace id \`${workspaceId}\` from branch name because this is a workspace fork branch (\`${rawBranch}\`). `); } // Handle multiple profiles - use special branch-aware logic @@ -231,7 +254,7 @@ export async function tryResolveBranchWorkspace( if (matchingProfiles.length === 1) { selectedProfile = matchingProfiles[0]; - log.info(colors.green(`Using workspace profile '${selectedProfile.name}' for branch '${currentBranch}'`)); + log.info(colors.green(`Using workspace profile '${selectedProfile.name}' for branch '${currentBranch} with workspace id \`${workspaceId}\``)); } else { // For multiple profiles, check branch-specific last used first const lastUsedName = await getLastUsedProfile( diff --git a/frontend/src/lib/components/ScriptEditor.svelte b/frontend/src/lib/components/ScriptEditor.svelte index 1e1c50c232..78cf810eb1 100644 --- a/frontend/src/lib/components/ScriptEditor.svelte +++ b/frontend/src/lib/components/ScriptEditor.svelte @@ -56,6 +56,8 @@ import GitRepoResourcePicker from './GitRepoResourcePicker.svelte' import { updateDelegateToGitRepoConfig, insertAdditionalInventories } from '$lib/ansibleUtils' import { copilotInfo } from '$lib/aiStore' + import JsonInputs from '$lib/components/JsonInputs.svelte' + import Toggle from './Toggle.svelte' interface Props { // Exported @@ -124,6 +126,9 @@ }: Props = $props() let initialArgs = structuredClone($state.snapshot(args)) + let jsonEditor: JsonInputs | undefined = $state(undefined) + let jsonView = $state(false) + let schemaHeight = $state(0) $effect.pre(() => { if (schema == undefined) { @@ -707,29 +712,51 @@ {/if} {/if} +
-
-
- {#key argsRender} - - {/key} + {#if jsonView} +
+ { + if (e.detail) { + args = e.detail + } + }} + updateOnBlur={false} + placeholder={`Write args as JSON.

Example:

{
  "foo": "12"
}`} + />
-
+ {:else} +
+
+ {#key argsRender} + + {/key} +
+
+ {/if}