11import * as _ from 'lodash' ;
22import * as fs from 'fs' ;
3- import * as os from 'os' ;
4- import * as path from 'path' ;
53import * as util from 'util' ;
64import { spawn , ChildProcess , SpawnOptions } from 'child_process' ;
75import * as GSettings from 'node-gsettings-wrapper' ;
@@ -13,20 +11,15 @@ const findOsxExecutable = util.promisify(findOsxExecutableCb);
1311import { Interceptor } from '..' ;
1412import { HtkConfig } from '../../config' ;
1513import { reportError , addBreadcrumb } from '../../error-tracking' ;
16- import { OVERRIDE_BIN_PATH , getTerminalEnvVars } from './terminal-env-overrides' ;
14+ import { getTerminalEnvVars } from './terminal-env-overrides' ;
15+ import { editShellStartupScripts , resetShellStartupScripts } from './terminal-scripts' ;
1716
1817const checkAccess = util . promisify ( fs . access ) ;
1918const canAccess = ( path : string ) => checkAccess ( path ) . then ( ( ) => true ) . catch ( ( ) => false ) ;
2019
2120const commandExists = ( path : string ) : Promise < boolean > => ensureCommandExists ( path ) . then ( ( ) => true ) . catch ( ( ) => false ) ;
2221
2322const DEFAULT_GIT_BASH_PATH = 'C:/Program Files/git/git-bash.exe' ;
24- const SHELL = ( process . env . SHELL || '' ) . split ( '/' ) . slice ( - 1 ) [ 0 ] ;
25-
26- // Generate POSIX paths for git-bash on Windows (or use the normal path everywhere where)
27- const POSIX_OVERRIDE_BIN_PATH = process . platform === 'win32'
28- ? OVERRIDE_BIN_PATH . replace ( / \\ / g, '/' ) . replace ( / ^ ( \w + ) : / , ( _all , driveLetter ) => `/${ driveLetter . toLowerCase ( ) } ` )
29- : OVERRIDE_BIN_PATH ;
3023
3124interface SpawnArgs {
3225 command : string ;
@@ -219,161 +212,6 @@ const getXfceTerminalCommand = async (command = 'xfce4-terminal'): Promise<Spawn
219212 return { command, args : extraArgs } ;
220213} ;
221214
222- const appendOrCreateFile = util . promisify ( fs . appendFile ) ;
223- const appendToFirstExisting = async ( paths : string [ ] , forceWrite : boolean , contents : string ) => {
224- for ( let path of paths ) {
225- // Small race here, but end result is ok either way
226- if ( await canAccess ( path ) ) {
227- return appendOrCreateFile ( path , contents ) ;
228- }
229- }
230-
231- if ( forceWrite ) {
232- // If force write is set, write the last file anyway
233- return appendOrCreateFile ( paths . slice ( - 1 ) [ 0 ] , contents ) ;
234- }
235- } ;
236-
237- const START_CONFIG_SECTION = '# --httptoolkit--' ;
238- const END_CONFIG_SECTION = '# --httptoolkit-end--' ;
239-
240- // The shell config required to ensure every spawned shell always has the right
241- // configuration, even if it has its env vars reset somehow. This also includes
242- // fixes for winpty with git-bash. By default, winpty will intercept known
243- // commands to manage them, so our PATH overrides never get run. We avoid that
244- // with trivial aliases, and then run winpty ourselves inside the overrides.
245-
246- // Works in bash, zsh, dash, ksh, sh (not fish)
247- const SH_SHELL_PATH_CONFIG = `
248- ${ START_CONFIG_SECTION }
249- # This section will be reset each time a HTTP Toolkit terminal is opened
250- if [ -n "$HTTP_TOOLKIT_ACTIVE" ]; then
251- # When HTTP Toolkit is active, we inject various overrides into PATH
252- export PATH="${ POSIX_OVERRIDE_BIN_PATH } :$PATH"
253-
254- if command -v winpty >/dev/null 2>&1; then
255- # Work around for winpty's hijacking of certain commands
256- alias php=php
257- alias node=node
258- fi
259- fi
260- ${ END_CONFIG_SECTION } `;
261- const FISH_SHELL_PATH_CONFIG = `
262- ${ START_CONFIG_SECTION }
263- # This section will be reset each time a HTTP Toolkit terminal is opened
264- if [ -n "$HTTP_TOOLKIT_ACTIVE" ]
265- # When HTTP Toolkit is active, we inject various overrides into PATH
266- set -x PATH "${ POSIX_OVERRIDE_BIN_PATH } " $PATH;
267-
268- if command -v winpty >/dev/null 2>&1
269- # Work around for winpty's hijacking of certain commands
270- alias php=php
271- alias node=node
272- end
273- end
274- ${ END_CONFIG_SECTION } `;
275-
276- // Find the relevant user shell config file, add the above line to it, so that
277- // shells launched with HTTP_TOOLKIT_ACTIVE set use the interception PATH.
278- const editShellStartupScripts = async ( ) => {
279- await resetShellStartupScripts ( ) ;
280-
281- // The key risk here is that one of these scripts (or some other process) will be
282- // overriding PATH itself, so we need to append some PATH reset logic. The main
283- // offenders are: nvm config's in .bashrc/.bash_profile, OSX's path_helper and
284- // git-bash ignoring the inherited $PATH.
285-
286- // .profile is used by Dash, Bash sometimes, and by Sh:
287- appendOrCreateFile ( path . join ( os . homedir ( ) , '.profile' ) , SH_SHELL_PATH_CONFIG )
288- . catch ( reportError ) ;
289-
290- // Bash login shells use some other files by preference, if they exist.
291- // Note that on OSX, all shells are login - elsewhere they only are at actual login time.
292- appendToFirstExisting (
293- [
294- path . join ( os . homedir ( ) , '.bash_profile' ) ,
295- path . join ( os . homedir ( ) , '.bash_login' )
296- ] ,
297- false , // Do nothing if they don't exist - it falls back to .profile
298- SH_SHELL_PATH_CONFIG
299- ) . catch ( reportError ) ;
300-
301- // Bash non-login shells use .bashrc, if it exists:
302- appendToFirstExisting (
303- [
304- path . join ( os . homedir ( ) , '.bashrc' )
305- ] ,
306- SHELL === 'bash' , // If you use bash, we _always_ want to set this
307- SH_SHELL_PATH_CONFIG
308- ) . catch ( reportError ) ;
309-
310- // Zsh has its own files (both are actually used)
311- appendToFirstExisting (
312- [
313- path . join ( os . homedir ( ) , '.zshenv' ) ,
314- path . join ( os . homedir ( ) , '.zshrc' )
315- ] ,
316- SHELL === 'zsh' , // If you use zsh, we _always_ write a config file
317- SH_SHELL_PATH_CONFIG
318- ) . catch ( reportError ) ;
319-
320- // Fish always uses the same config file
321- appendToFirstExisting (
322- [
323- path . join ( os . homedir ( ) , '.config' , 'fish' , 'config.fish' ) ,
324- ] ,
325- SHELL === 'fish' || await canAccess ( path . join ( os . homedir ( ) , '.config' , 'fish' ) ) ,
326- FISH_SHELL_PATH_CONFIG
327- ) . catch ( reportError ) ;
328- } ;
329-
330- const readFile = util . promisify ( fs . readFile ) ;
331- const writeFile = util . promisify ( fs . writeFile ) ;
332- const renameFile = util . promisify ( fs . rename ) ;
333- const removeConfigSectionsFromFile = async ( path : string ) => {
334- let fileLines : string [ ] ;
335-
336- try {
337- fileLines = ( await readFile ( path , 'utf8' ) ) . split ( '\n' ) ;
338- } catch ( e ) {
339- // Silently skip any files we can't read
340- return ;
341- }
342-
343- // Remove everything between each pair of start/end section markers
344- let sectionStart = _ . findIndex ( fileLines , ( l ) => l . startsWith ( START_CONFIG_SECTION ) ) ;
345- while ( sectionStart !== - 1 ) {
346- let sectionEnd = _ . findIndex ( fileLines , ( l ) => l . startsWith ( END_CONFIG_SECTION ) ) ;
347-
348- if ( sectionEnd === - 1 || sectionEnd <= sectionStart ) return ; // Odd config file state - don't edit it
349- fileLines . splice ( sectionStart , ( sectionEnd - sectionStart ) + 1 ) ;
350- sectionStart = _ . findIndex ( fileLines , ( l ) => l . startsWith ( START_CONFIG_SECTION ) ) ;
351- }
352-
353- // Write & rename to ensure this is atomic, and avoid races here
354- // as much as we reasonably can.
355- const tempFile = path + Date . now ( ) + '.temp' ;
356- await writeFile ( tempFile , fileLines . join ( '\n' ) ) ;
357- return renameFile ( tempFile , path ) ;
358- } ;
359-
360- // Cleanup: strip our extra config line from all config files
361- // Good to do for tidiness, not strictly necessary (the config does nothing
362- // unless HTTP_TOOLKIT_ACTIVE is set anyway).
363- const resetShellStartupScripts = ( ) => {
364- // For each possible config file, remove our magic line, if present
365- return Promise . all ( [
366- path . join ( os . homedir ( ) , '.profile' ) ,
367- path . join ( os . homedir ( ) , '.bash_profile' ) ,
368- path . join ( os . homedir ( ) , '.bash_login' ) ,
369- path . join ( os . homedir ( ) , '.zshenv' ) ,
370- path . join ( os . homedir ( ) , '.zshrc' ) ,
371- path . join ( os . homedir ( ) , '.config' , 'fish' , 'config.fish' ) ,
372- ] . map ( ( configFile ) =>
373- removeConfigSectionsFromFile ( configFile ) . catch ( reportError )
374- ) ) ;
375- } ;
376-
377215const terminals : _ . Dictionary < ChildProcess [ ] | undefined > = { }
378216
379217export class TerminalInterceptor implements Interceptor {
0 commit comments