From f381eeb2cd267debd330ec0e2fc31bed8eecace2 Mon Sep 17 00:00:00 2001 From: Pascal Breuninger Date: Fri, 19 Apr 2024 21:49:35 +0200 Subject: [PATCH] feat(desktop): add experimental extra environment variables setting --- desktop/src-tauri/Cargo.toml | 2 +- desktop/src-tauri/src/settings.rs | 1 + desktop/src/client/command.ts | 18 ++- desktop/src/contexts/SettingsContext.tsx | 5 +- desktop/src/gen/Settings.ts | 1 + desktop/src/views/Settings/Settings.tsx | 14 ++- .../src/views/Settings/useContextOptions.tsx | 105 ++++++++++++++---- 7 files changed, 122 insertions(+), 24 deletions(-) diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index ba2d5429b..b6778f579 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -12,7 +12,7 @@ tauri-build = { version = "1.2", features = [] } [dependencies] # Tauri -tauri = { version = "1.2.4", features = [ "updater", +tauri = { version = "1.2.4", features = [ "process-relaunch", "window-close", "notification-all", diff --git a/desktop/src-tauri/src/settings.rs b/desktop/src-tauri/src/settings.rs index 61c861d9c..648cb1235 100644 --- a/desktop/src-tauri/src/settings.rs +++ b/desktop/src-tauri/src/settings.rs @@ -29,6 +29,7 @@ pub struct Settings { #[serde(rename = "experimental_devPodPro")] experimental_devpod_pro: bool, additional_cli_flags: String, + additional_env_vars: String, } #[derive(Debug, Serialize, TS)] diff --git a/desktop/src/client/command.ts b/desktop/src/client/command.ts index 002a0b6a2..13982a395 100644 --- a/desktop/src/client/command.ts +++ b/desktop/src/client/command.ts @@ -20,10 +20,26 @@ export class Command implements TCommand { private childProcess?: Child private args: string[] + public static ADDITIONAL_ENV_VARS: string = "" + constructor(args: string[]) { debug("commands", "Creating Devpod command with args: ", args) + const extraEnvVars = Command.ADDITIONAL_ENV_VARS.split(",") + .map((envVarStr) => envVarStr.split("=")) + .reduce((acc, pair) => { + const [key, value] = pair + if (key === undefined || value === undefined) { + return acc + } + + return { ...acc, [key]: value } + }, {}) + this.sidecarCommand = ShellCommand.sidecar(DEVPOD_BINARY, args, { - env: { [DEVPOD_UI_ENV_VAR]: "true" }, + env: { + ...extraEnvVars, + [DEVPOD_UI_ENV_VAR]: "true", + }, }) this.args = args } diff --git a/desktop/src/contexts/SettingsContext.tsx b/desktop/src/contexts/SettingsContext.tsx index 66af48777..3d36746be 100644 --- a/desktop/src/contexts/SettingsContext.tsx +++ b/desktop/src/contexts/SettingsContext.tsx @@ -37,6 +37,7 @@ const initialSettings: TSettings = { experimental_vscodeInsiders: true, experimental_devPodPro: false, additionalCliFlags: "", + additionalEnvVars: "", } function getSettingKeys(): readonly TSetting[] { return getKeys(initialSettings) @@ -44,7 +45,9 @@ function getSettingKeys(): readonly TSetting[] { // WARN: needs to match the filename on the rust side const SETTING_STORE_KEY = "settings" -const settingsStore = new Store(new LocalStorageToFileMigrationBackend(SETTING_STORE_KEY)) +const settingsStore = new Store>( + new LocalStorageToFileMigrationBackend(SETTING_STORE_KEY) +) export function SettingsProvider({ children }: Readonly<{ children?: ReactNode }>) { const [settings, setSettings] = useState(initialSettings) diff --git a/desktop/src/gen/Settings.ts b/desktop/src/gen/Settings.ts index ff402da91..917656665 100644 --- a/desktop/src/gen/Settings.ts +++ b/desktop/src/gen/Settings.ts @@ -16,4 +16,5 @@ export interface Settings { experimental_vscodeInsiders: boolean experimental_devPodPro: boolean additionalCliFlags: string + additionalEnvVars: string } diff --git a/desktop/src/views/Settings/Settings.tsx b/desktop/src/views/Settings/Settings.tsx index 0e2078cc6..d7d831d4c 100644 --- a/desktop/src/views/Settings/Settings.tsx +++ b/desktop/src/views/Settings/Settings.tsx @@ -37,7 +37,12 @@ import { useVersion, } from "../../lib" import { useWelcomeModal } from "../../useWelcomeModal" -import { useCLIFlagsOption, useAgentURLOption, useTelemetryOption } from "./useContextOptions" +import { + useCLIFlagsOption, + useAgentURLOption, + useTelemetryOption, + useExtraEnvVarsOption, +} from "./useContextOptions" import { useIDESettings } from "./useIDESettings" const SETTINGS_TABS = [ @@ -319,6 +324,7 @@ function UpdateSettings() { function ExperimentalSettings() { const { input: cliFlagsInput, helpText: cliFlagsHelpText } = useCLIFlagsOption() + const { input: extraEnvVarsInput, helpText: extraEnvVarsHelpText } = useExtraEnvVarsOption() const { settings, set } = useChangeSettings() return ( @@ -365,10 +371,14 @@ function ExperimentalSettings() { - + {cliFlagsInput} + + {extraEnvVarsInput} + + { + const updateOption = useCallback( + (value: string) => { set("additionalCliFlags", value) client.setSetting("additionalCliFlags", value) }, - }) - - return useMemo( - () => ({ - settings, - updateOption, - }), - [settings, updateOption] + [set] ) -} - -export function useCLIFlagsOption() { - const { settings, updateOption } = useSettingsOptions() const [hasFocus, setHasFocus] = useState(false) const inputRef = useRef(null) const handleBlur = useCallback( (e: FocusEvent) => { const value = e.target.value.trim() - updateOption({ value }) + updateOption(value) setHasFocus(false) }, [updateOption] @@ -96,7 +85,7 @@ export function useCLIFlagsOption() { <>Set the additional CLI Flags to use., []) + const helpText = useMemo(() => <>Set additional CLI Flags to use., []) return { input, helpText } } + export function useAgentURLOption() { const { options, updateOption } = useContextOptions() const [hasFocus, setHasFocus] = useState(false) @@ -244,3 +234,80 @@ export function useTelemetryOption() { return { input, helpText } } + +export function useExtraEnvVarsOption() { + const { settings, set } = useChangeSettings() + const [hasFocus, setHasFocus] = useState(false) + const inputRef = useRef(null) + + const handleBlur = useCallback( + (e: FocusEvent) => { + const value = e.target.value.trim() + set("additionalEnvVars", value) + Command.ADDITIONAL_ENV_VARS = value + setHasFocus(false) + }, + [set] + ) + + const handleKeyUp = useCallback((e: KeyboardEvent) => { + if (e.key !== "Enter") return + + e.currentTarget.blur() + }, []) + + const handleFocus = useCallback(() => { + setHasFocus(true) + }, []) + + const handleClear = useCallback(() => { + const el = inputRef.current + if (!el) return + + el.value = "" + }, []) + + const input = useMemo( + () => ( + + + + } + aria-label="clear" + onMouseDown={(e) => { + // needed to prevent losing focus from input + e.stopPropagation() + e.preventDefault() + }} + onClick={handleClear} + /> + + + ), + [settings.additionalEnvVars, handleBlur, handleKeyUp, handleFocus, hasFocus, handleClear] + ) + + const helpText = useMemo( + () => ( + <> + Set additional environment variables DevPod passes to all commands. Accepts a comma + separated list, e.g. FOO=bar,BAZ=false + + ), + [] + ) + + return { input, helpText } +}