diff --git a/app/config.ts b/app/config.ts index b4613d575c8..1c47202d297 100644 --- a/app/config.ts +++ b/app/config.ts @@ -9,6 +9,7 @@ import _openConfig from './config/open'; import {cfgPath, cfgDir} from './config/paths'; import notify from './notify'; import {getColorMap} from './utils/colors'; +import {resolvePlatformConfig} from './utils/resolve-platform-config'; const watchers: Function[] = []; let cfg: parsedConfig = {} as any; @@ -76,8 +77,9 @@ export const subscribe = (fn: Function) => { }; }; +export {resolvePlatformConfig} from './utils/resolve-platform-config'; + export const getConfigDir = () => { - // expose config directory to load plugin from the right place return cfgDir; }; @@ -104,7 +106,7 @@ export const getProfileConfig = (profileName: string): configOptions => { baseConfig[key] = profileConfig[key]; } } - return {...baseConfig, defaultProfile, profiles}; + return resolvePlatformConfig({...baseConfig, defaultProfile, profiles}); }; export const openConfig = () => { diff --git a/app/config/schema.json b/app/config/schema.json index 6bcf850036e..14b60d23d7c 100644 --- a/app/config/schema.json +++ b/app/config/schema.json @@ -168,6 +168,27 @@ "description": "for environment variables", "type": "object" }, + "envLinux": { + "additionalProperties": { + "type": "string" + }, + "description": "platform-specific env override for Linux (merged with `env` on Linux)", + "type": "object" + }, + "envOsx": { + "additionalProperties": { + "type": "string" + }, + "description": "platform-specific env override for macOS (merged with `env` on macOS)", + "type": "object" + }, + "envWindows": { + "additionalProperties": { + "type": "string" + }, + "description": "platform-specific env override for Windows (merged with `env` on Windows)", + "type": "object" + }, "fontFamily": { "description": "font family with optional fallbacks", "type": "string" @@ -253,6 +274,39 @@ }, "type": "array" }, + "shellArgsLinux": { + "description": "platform-specific shellArgs override for Linux (overrides `shellArgs` on Linux)", + "items": { + "type": "string" + }, + "type": "array" + }, + "shellArgsOsx": { + "description": "platform-specific shellArgs override for macOS (overrides `shellArgs` on macOS)", + "items": { + "type": "string" + }, + "type": "array" + }, + "shellArgsWindows": { + "description": "platform-specific shellArgs override for Windows (overrides `shellArgs` on Windows)", + "items": { + "type": "string" + }, + "type": "array" + }, + "shellLinux": { + "description": "platform-specific shell override for Linux (overrides `shell` on Linux)", + "type": "string" + }, + "shellOsx": { + "description": "platform-specific shell override for macOS (overrides `shell` on macOS)", + "type": "string" + }, + "shellWindows": { + "description": "platform-specific shell override for Windows (overrides `shell` on Windows)", + "type": "string" + }, "showHamburgerMenu": { "description": "if you're using a Linux setup which show native menus, set to false\n\ndefault: `true` on Linux, `true` on Windows, ignored on macOS", "enum": [ @@ -495,6 +549,27 @@ "description": "for environment variables", "type": "object" }, + "envLinux": { + "additionalProperties": { + "type": "string" + }, + "description": "platform-specific env override for Linux (merged with `env` on Linux)", + "type": "object" + }, + "envOsx": { + "additionalProperties": { + "type": "string" + }, + "description": "platform-specific env override for macOS (merged with `env` on macOS)", + "type": "object" + }, + "envWindows": { + "additionalProperties": { + "type": "string" + }, + "description": "platform-specific env override for Windows (merged with `env` on Windows)", + "type": "object" + }, "fontFamily": { "description": "font family with optional fallbacks", "type": "string" @@ -580,6 +655,39 @@ }, "type": "array" }, + "shellArgsLinux": { + "description": "platform-specific shellArgs override for Linux (overrides `shellArgs` on Linux)", + "items": { + "type": "string" + }, + "type": "array" + }, + "shellArgsOsx": { + "description": "platform-specific shellArgs override for macOS (overrides `shellArgs` on macOS)", + "items": { + "type": "string" + }, + "type": "array" + }, + "shellArgsWindows": { + "description": "platform-specific shellArgs override for Windows (overrides `shellArgs` on Windows)", + "items": { + "type": "string" + }, + "type": "array" + }, + "shellLinux": { + "description": "platform-specific shell override for Linux (overrides `shell` on Linux)", + "type": "string" + }, + "shellOsx": { + "description": "platform-specific shell override for macOS (overrides `shell` on macOS)", + "type": "string" + }, + "shellWindows": { + "description": "platform-specific shell override for Windows (overrides `shell` on Windows)", + "type": "string" + }, "showHamburgerMenu": { "description": "if you're using a Linux setup which show native menus, set to false\n\ndefault: `true` on Linux, `true` on Windows, ignored on macOS", "enum": [ diff --git a/app/utils/resolve-platform-config.ts b/app/utils/resolve-platform-config.ts new file mode 100644 index 00000000000..d1634291a27 --- /dev/null +++ b/app/utils/resolve-platform-config.ts @@ -0,0 +1,28 @@ +import type {configOptions} from '../../typings/config'; + +type PlatformSuffix = 'Osx' | 'Windows' | 'Linux'; + +const PLATFORM_SUFFIX_MAP: Partial> = { + darwin: 'Osx', + win32: 'Windows', + linux: 'Linux' +}; + +export const resolvePlatformConfig = ( + config: configOptions, + platform: NodeJS.Platform = process.platform +): configOptions => { + const suffix = PLATFORM_SUFFIX_MAP[platform]; + if (!suffix) return config; + + const result = {...config}; + const shellKey = `shell${suffix}` as `shell${PlatformSuffix}`; + const shellArgsKey = `shellArgs${suffix}` as `shellArgs${PlatformSuffix}`; + const envKey = `env${suffix}` as `env${PlatformSuffix}`; + + if (result[shellKey] !== undefined) result.shell = result[shellKey]!; + if (result[shellArgsKey] !== undefined) result.shellArgs = result[shellArgsKey]!; + if (result[envKey] !== undefined) result.env = {...(result.env || {}), ...result[envKey]}; + + return result; +}; diff --git a/test/unit/config-platform.test.ts b/test/unit/config-platform.test.ts new file mode 100644 index 00000000000..71b7d81d229 --- /dev/null +++ b/test/unit/config-platform.test.ts @@ -0,0 +1,112 @@ +import test from 'ava'; + +import {resolvePlatformConfig} from '../../app/utils/resolve-platform-config'; + +const baseConfig = () => ({ + shell: '/bin/sh', + shellArgs: ['--login'], + env: {BASE: '1'}, + updateChannel: 'stable' as const, + autoUpdatePlugins: true, + defaultSSHApp: true, + disableAutoUpdates: false, + useConpty: undefined, + backgroundColor: '#000', + bell: 'SOUND' as const, + bellSound: null, + bellSoundURL: null, + borderColor: '#333', + colors: {} as any, + copyOnSelect: false, + css: '', + cursorAccentColor: '#000', + cursorBlink: false, + cursorColor: '#fff', + cursorShape: 'BLOCK' as const, + disableLigatures: true, + fontFamily: 'monospace', + fontSize: 12, + fontWeight: 'normal' as const, + fontWeightBold: 'bold' as const, + foregroundColor: '#fff', + imageSupport: false, + letterSpacing: 0, + lineHeight: 1, + macOptionSelectionMode: 'vertical', + padding: '12px', + preserveCWD: true, + quickEdit: false, + screenReaderMode: false, + scrollback: 1000, + selectionColor: 'rgba(0,0,0,0.3)', + showHamburgerMenu: '' as const, + showWindowControls: '' as const, + termCSS: '', + webGLRenderer: false, + webLinksActivationKey: '' as const, + workingDirectory: '', + defaultProfile: 'default', + profiles: [] +}); + +test('resolvePlatformConfig: linux uses shellLinux', (t) => { + const cfg = {...baseConfig(), shellLinux: '/bin/bash'}; + const result = resolvePlatformConfig(cfg, 'linux'); + t.is(result.shell, '/bin/bash'); +}); + +test('resolvePlatformConfig: darwin uses shellOsx', (t) => { + const cfg = {...baseConfig(), shellOsx: '/bin/zsh'}; + const result = resolvePlatformConfig(cfg, 'darwin'); + t.is(result.shell, '/bin/zsh'); +}); + +test('resolvePlatformConfig: win32 uses shellWindows', (t) => { + const cfg = {...baseConfig(), shellWindows: 'C:\\Windows\\System32\\cmd.exe'}; + const result = resolvePlatformConfig(cfg, 'win32'); + t.is(result.shell, 'C:\\Windows\\System32\\cmd.exe'); +}); + +test('resolvePlatformConfig: shellArgsLinux overrides shellArgs', (t) => { + const cfg = {...baseConfig(), shellArgsLinux: ['-i']}; + const result = resolvePlatformConfig(cfg, 'linux'); + t.deepEqual(result.shellArgs, ['-i']); +}); + +test('resolvePlatformConfig: empty shellArgsWindows overrides shellArgs', (t) => { + const cfg = {...baseConfig(), shellArgsWindows: []}; + const result = resolvePlatformConfig(cfg, 'win32'); + t.deepEqual(result.shellArgs, []); +}); + +test('resolvePlatformConfig: envLinux is merged with env', (t) => { + const cfg = {...baseConfig(), env: {BASE: '1', SHARED: 'x'}, envLinux: {LINUX_ONLY: 'y', SHARED: 'z'}}; + const result = resolvePlatformConfig(cfg, 'linux'); + t.deepEqual(result.env, {BASE: '1', SHARED: 'z', LINUX_ONLY: 'y'}); +}); + +test('resolvePlatformConfig: envOsx does not affect linux', (t) => { + const cfg = {...baseConfig(), envOsx: {MAC_ONLY: '1'}}; + const result = resolvePlatformConfig(cfg, 'linux'); + t.is(result.env['MAC_ONLY'], undefined); +}); + +test('resolvePlatformConfig: no platform-specific keys leaves config unchanged', (t) => { + const cfg = baseConfig(); + const result = resolvePlatformConfig(cfg, 'linux'); + t.is(result.shell, '/bin/sh'); + t.deepEqual(result.shellArgs, ['--login']); + t.deepEqual(result.env, {BASE: '1'}); +}); + +test('resolvePlatformConfig: unsupported platform returns config unchanged', (t) => { + const cfg = {...baseConfig(), shellLinux: '/bin/bash'}; + const result = resolvePlatformConfig(cfg, 'freebsd' as NodeJS.Platform); + t.is(result.shell, '/bin/sh'); +}); + +test('resolvePlatformConfig: empty string shellWindows still overrides shell', (t) => { + const cfg = {...baseConfig(), shellWindows: ''}; + const result = resolvePlatformConfig(cfg, 'win32'); + t.is(result.shell, ''); +}); diff --git a/typings/config.d.ts b/typings/config.d.ts index 7a6053e93fe..cfe84afcd24 100644 --- a/typings/config.d.ts +++ b/typings/config.d.ts @@ -82,6 +82,12 @@ type profileConfigOptions = { disableLigatures: boolean; /** for environment variables */ env: {[k: string]: string}; + /** platform-specific env override for Linux (merged with `env` on Linux) */ + envLinux?: {[k: string]: string}; + /** platform-specific env override for macOS (merged with `env` on macOS) */ + envOsx?: {[k: string]: string}; + /** platform-specific env override for Windows (merged with `env` on Windows) */ + envWindows?: {[k: string]: string}; /** font family with optional fallbacks */ fontFamily: string; /** default font size in pixels for all tabs */ @@ -153,11 +159,23 @@ type profileConfigOptions = { * Then Add `--command=usr/bin/bash.exe` to shellArgs */ shell: string; + /** platform-specific shell override for Linux (overrides `shell` on Linux) */ + shellLinux?: string; + /** platform-specific shell override for macOS (overrides `shell` on macOS) */ + shellOsx?: string; + /** platform-specific shell override for Windows (overrides `shell` on Windows) */ + shellWindows?: string; /** * for setting shell arguments (e.g. for using interactive shellArgs: `['-i']`) * by default `['--login']` will be used */ shellArgs: string[]; + /** platform-specific shellArgs override for Linux (overrides `shellArgs` on Linux) */ + shellArgsLinux?: string[]; + /** platform-specific shellArgs override for macOS (overrides `shellArgs` on macOS) */ + shellArgsOsx?: string[]; + /** platform-specific shellArgs override for Windows (overrides `shellArgs` on Windows) */ + shellArgsWindows?: string[]; /** * if you're using a Linux setup which show native menus, set to false *