From f6c1b34b8f013bb6e4f9fac4bcacd3280cc0b432 Mon Sep 17 00:00:00 2001 From: Eric Promislow Date: Mon, 29 Nov 2021 16:49:10 -0800 Subject: [PATCH 1/8] Expand the concept of the homeDir on Windows Allow env vars HOME, HOMEDRIVE/HOMEPATH, and USERCONFIG to all denote a possible home directory Favor an existing .kube/config file Favor existing, writable home-ish directories And finally favor existing home-ish directories (which at this point aren't writable, but that's how the Kubernetes go-client does it). Use the same determination of the .kube/config-based home dir on Windows that the --- src/config.ts | 41 ++++++++----- src/config_test.ts | 141 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 149 insertions(+), 33 deletions(-) diff --git a/src/config.ts b/src/config.ts index 9dfee8e7293..0593b864c3e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -518,32 +518,45 @@ export function bufferFromFileOrString(file?: string, data?: string): Buffer | n // Only public for testing. export function findHomeDir(): string | null { - if (process.env.HOME) { - try { - fs.accessSync(process.env.HOME); - return process.env.HOME; - // tslint:disable-next-line:no-empty - } catch (ignore) {} - } if (process.platform !== 'win32') { + if (process.env.HOME) { + try { + fs.accessSync(process.env.HOME); + return process.env.HOME; + // tslint:disable-next-line:no-empty + } catch (ignore) {} + } return null; } - if (process.env.HOMEDRIVE && process.env.HOMEPATH) { - const dir = path.join(process.env.HOMEDRIVE, process.env.HOMEPATH); + const homeDrivePath = process.env.HOMEDRIVE && process.env.HOMEPATH ? path.join(process.env.HOMEDRIVE, process.env.HOMEPATH) : null; + const dirList1 = [process.env.HOME, homeDrivePath, process.env.USERPROFILE].filter(x => x); + const dirList2 = [process.env.HOME, process.env.USERPROFILE, homeDrivePath].filter(x => x); + // 1. the first of %HOME%, %HOMEDRIVE%%HOMEPATH%, %USERPROFILE% containing a `.kube\config` file is returned. + for (const dir of dirList1) { try { - fs.accessSync(dir); + fs.accessSync(path.join(dir, '.kube', 'config')); return dir; // tslint:disable-next-line:no-empty } catch (ignore) {} } - if (process.env.USERPROFILE) { + // 2. ...the first of %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that exists and is writeable is returned + for (const dir of dirList2) { + const lstat = fs.lstatSync(dir, { throwIfNoEntry: false }); + // tslint:disable-next-line:no-bitwise + if (lstat && (lstat.mode & fs.constants.S_IXUSR) === fs.constants.S_IXUSR) { + return dir; + } + } + // 3. ...the first of %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that exists is returned. + for (const dir of dirList2) { try { - fs.accessSync(process.env.USERPROFILE); - return process.env.USERPROFILE; + fs.accessSync(dir); + return dir; // tslint:disable-next-line:no-empty } catch (ignore) {} } - return null; + // 4. if none of those locations exists, the first of %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that is set is returned. + return dirList2[0] ?? null; } export interface Named { diff --git a/src/config_test.ts b/src/config_test.ts index 65dc3e2f8bf..2e484ecf3f5 100644 --- a/src/config_test.ts +++ b/src/config_test.ts @@ -466,34 +466,137 @@ describe('KubeConfig', () => { expect(dir).to.equal(null); }); - it('should load from HOMEDRIVE/HOMEPATH if present', () => { - process.env.HOMEDRIVE = 'foo'; - process.env.HOMEPATH = 'bar'; - const dir = join(process.env.HOMEDRIVE, process.env.HOMEPATH); - const arg = {}; - arg[dir] = { config: 'data' }; - mockfs(arg); + describe('look for an existing .kube/config', () => { + let allDirs; + let homeDrive; + before(() => { + allDirs = {}; + process.env.HOME = 'home'; + process.env.HOMEDRIVE = 'drive'; + process.env.HOMEPATH = 'a-path'; + process.env.USERPROFILE = 'a-userprofile'; + homeDrive = join(process.env.HOMEDRIVE, process.env.HOMEPATH); + allDirs[process.env.HOME] = {}; + allDirs[homeDrive] = {}; + allDirs[process.env.USERPROFILE] = {}; + }); + it('should load from HOME if present', () => { + const dir = process.env.HOME; + allDirs[dir]['.kube'] = { config: 'data' }; + mockfs(allDirs); - const home = findHomeDir(); + const home = findHomeDir(); - mockfs.restore(); - expect(home).to.equal(dir); + mockfs.restore(); + expect(home).to.equal(dir); + }); + it('should favor HOME when present', () => { + const dir = process.env.HOME; + allDirs[dir]['.kube'] = { config: 'data' }; + allDirs[homeDrive]['.kube'] = { config: 'data' }; + allDirs[process.env.USERPROFILE]['.kube'] = { config: 'data' }; + mockfs(allDirs); + + const home = findHomeDir(); + + mockfs.restore(); + expect(home).to.equal(dir); + }); + + it('should load from HOMEDRIVE/HOMEPATH if present', () => { + const dir = homeDrive; + allDirs[dir]['.kube'] = { config: 'data' }; + mockfs(arg); + + const home = findHomeDir(); + + mockfs.restore(); + expect(home).to.equal(dir); + }); + + it('should favor HOMEDRIVE/HOMEPATH over USERPROFILE', () => { + const dir = homeDrive; + allDirs[dir]['.kube'] = { config: 'data' }; + allDirs[process.env.USERPROFILE]['.kube'] = { config: 'data' }; + mockfs(arg); + + const home = findHomeDir(); + + mockfs.restore(); + expect(home).to.equal(dir); + }); + + it('should load from USERPROFILE if present', () => { + const dir = process.env.USERPROFILE; + allDirs[dir]['.kube'] = { config: 'data' }; + mockfs(arg); + + const home = findHomeDir(); + + mockfs.restore(); + expect(home).to.equal(dir); + }); }); - it('should load from USERPROFILE if present', () => { - const dir = 'someplace'; + // Just test for existence,but this will include the writability order + describe('look for an existing directory', () => { + let allDirs; + let homeDrive; + before(() => { + allDirs = {}; + process.env.HOME = 'home'; + process.env.HOMEDRIVE = 'drive'; + process.env.HOMEPATH = 'a-path'; + process.env.USERPROFILE = 'a-userprofile'; + homeDrive = join(process.env.HOMEDRIVE, process.env.HOMEPATH); + }); + it('should load from HOME if present', () => { + allDirs = { + [process.env.HOME]: 'data', + [homeDrive]: 'data', + [process.env.USERPROFILE]: 'data' + } + const dir = process.env.HOME + mockfs(allDirs); + + const home = findHomeDir(); - process.env.HOMEDRIVE = 'foo'; - process.env.HOMEPATH = 'bar'; - process.env.USERPROFILE = dir; - const arg = {}; - arg[dir] = { config: 'data' }; - mockfs(arg); + mockfs.restore(); + expect(home).to.equal(dir); + }); + it('should load from homeDrive if present', () => { + allDirs = { + [homeDrive]: 'data', + [process.env.USERPROFILE]: 'data' + } + const dir = process.env.HOME + mockfs(allDirs); + + const home = findHomeDir(); + + mockfs.restore(); + expect(home).to.equal(homeDrive); + }); + it('should load from USERPROFILE if present', () => { + allDirs = { + [process.env.USERPROFILE]: 'data' + } + const dir = process.env.USERPROFILE; + mockfs(allDirs); + + const home = findHomeDir(); + + mockfs.restore(); + expect(home).to.equal(dir); + }); + }); + it('should return null if nothing is present', () => { + mockfs({}); const home = findHomeDir(); mockfs.restore(); - expect(home).to.equal(dir); + expect(home).to.equal(null); }); }); From a0202e3f87a59643341e4da05f2aac3ee4de582e Mon Sep 17 00:00:00 2001 From: Eric Promislow Date: Tue, 30 Nov 2021 10:45:52 -0800 Subject: [PATCH 2/8] Remove duplicates from the list of home-ish env vars. Also tighten up code to work with Node 12. --- src/config.ts | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/config.ts b/src/config.ts index 0593b864c3e..1d063c7d18f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -516,6 +516,18 @@ export function bufferFromFileOrString(file?: string, data?: string): Buffer | n return null; } +function dropDuplicatesAndNils(a): string[] { + return a.reduce((acceptedValues, currentValue) => { + // Good-enough algorithm for reducing a small (3 items at this point) array into an ordered list + // of unique non-empty strings. + if (currentValue && !acceptedValues.includes(currentValue)) { + return acceptedValues.concat(currentValue); + } else { + return acceptedValues; + } + }, [] as string[]); +} + // Only public for testing. export function findHomeDir(): string | null { if (process.platform !== 'win32') { @@ -529,8 +541,8 @@ export function findHomeDir(): string | null { return null; } const homeDrivePath = process.env.HOMEDRIVE && process.env.HOMEPATH ? path.join(process.env.HOMEDRIVE, process.env.HOMEPATH) : null; - const dirList1 = [process.env.HOME, homeDrivePath, process.env.USERPROFILE].filter(x => x); - const dirList2 = [process.env.HOME, process.env.USERPROFILE, homeDrivePath].filter(x => x); + const dirList1: string[] = dropDuplicatesAndNils([process.env.HOME, homeDrivePath, process.env.USERPROFILE]); + const dirList2: string[] = dropDuplicatesAndNils([process.env.HOME, process.env.USERPROFILE, homeDrivePath]); // 1. the first of %HOME%, %HOMEDRIVE%%HOMEPATH%, %USERPROFILE% containing a `.kube\config` file is returned. for (const dir of dirList1) { try { @@ -541,11 +553,14 @@ export function findHomeDir(): string | null { } // 2. ...the first of %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that exists and is writeable is returned for (const dir of dirList2) { - const lstat = fs.lstatSync(dir, { throwIfNoEntry: false }); - // tslint:disable-next-line:no-bitwise - if (lstat && (lstat.mode & fs.constants.S_IXUSR) === fs.constants.S_IXUSR) { - return dir; - } + try { + const lstat = fs.lstatSync(dir); + // tslint:disable-next-line:no-bitwise + if (lstat && (lstat.mode & fs.constants.S_IXUSR) === fs.constants.S_IXUSR) { + return dir; + } + // tslint:disable-next-line:no-empty + } catch (ignore) {} } // 3. ...the first of %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that exists is returned. for (const dir of dirList2) { From 514bea3d0d39d2d29b7184686409e50c7d5e5729 Mon Sep 17 00:00:00 2001 From: Eric Promislow Date: Wed, 1 Dec 2021 13:22:49 -0800 Subject: [PATCH 3/8] Lint-driven fixes --- src/config.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/config.ts b/src/config.ts index 1d063c7d18f..b47a8335e04 100644 --- a/src/config.ts +++ b/src/config.ts @@ -516,7 +516,7 @@ export function bufferFromFileOrString(file?: string, data?: string): Buffer | n return null; } -function dropDuplicatesAndNils(a): string[] { +function dropDuplicatesAndNils(a: Array): string[] { return a.reduce((acceptedValues, currentValue) => { // Good-enough algorithm for reducing a small (3 items at this point) array into an ordered list // of unique non-empty strings. @@ -540,7 +540,8 @@ export function findHomeDir(): string | null { } return null; } - const homeDrivePath = process.env.HOMEDRIVE && process.env.HOMEPATH ? path.join(process.env.HOMEDRIVE, process.env.HOMEPATH) : null; + const homeDrivePath = process.env.HOMEDRIVE && process.env.HOMEPATH ? + path.join(process.env.HOMEDRIVE, process.env.HOMEPATH) : null; const dirList1: string[] = dropDuplicatesAndNils([process.env.HOME, homeDrivePath, process.env.USERPROFILE]); const dirList2: string[] = dropDuplicatesAndNils([process.env.HOME, process.env.USERPROFILE, homeDrivePath]); // 1. the first of %HOME%, %HOMEDRIVE%%HOMEPATH%, %USERPROFILE% containing a `.kube\config` file is returned. @@ -555,7 +556,7 @@ export function findHomeDir(): string | null { for (const dir of dirList2) { try { const lstat = fs.lstatSync(dir); - // tslint:disable-next-line:no-bitwise +// tslint:disable-next-line:no-bitwise if (lstat && (lstat.mode & fs.constants.S_IXUSR) === fs.constants.S_IXUSR) { return dir; } @@ -570,8 +571,9 @@ export function findHomeDir(): string | null { // tslint:disable-next-line:no-empty } catch (ignore) {} } - // 4. if none of those locations exists, the first of %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that is set is returned. - return dirList2[0] ?? null; + // 4. if none of those locations exists, the first of + // %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that is set is returned. + return dirList2[0] || null; } export interface Named { From 2d9f717209979095ad544f0cbc53af1b79dc9039 Mon Sep 17 00:00:00 2001 From: Eric Promislow Date: Wed, 1 Dec 2021 13:23:23 -0800 Subject: [PATCH 4/8] Fix unit tests --- src/config_test.ts | 66 ++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/src/config_test.ts b/src/config_test.ts index 2e484ecf3f5..19eedaf9bb1 100644 --- a/src/config_test.ts +++ b/src/config_test.ts @@ -461,7 +461,7 @@ describe('KubeConfig', () => { process.env.HOMEPATH = originalEnvVars.HOMEPATH; }); - it('should return null if no home is present', () => { + it('should return null if no home-ish env vars are set', () => { const dir = findHomeDir(); expect(dir).to.equal(null); }); @@ -469,7 +469,7 @@ describe('KubeConfig', () => { describe('look for an existing .kube/config', () => { let allDirs; let homeDrive; - before(() => { + beforeEach(() => { allDirs = {}; process.env.HOME = 'home'; process.env.HOMEDRIVE = 'drive'; @@ -481,7 +481,7 @@ describe('KubeConfig', () => { allDirs[process.env.USERPROFILE] = {}; }); it('should load from HOME if present', () => { - const dir = process.env.HOME; + const dir = process.env.HOME as string; allDirs[dir]['.kube'] = { config: 'data' }; mockfs(allDirs); @@ -491,10 +491,10 @@ describe('KubeConfig', () => { expect(home).to.equal(dir); }); it('should favor HOME when present', () => { - const dir = process.env.HOME; + const dir = process.env.HOME as string; allDirs[dir]['.kube'] = { config: 'data' }; allDirs[homeDrive]['.kube'] = { config: 'data' }; - allDirs[process.env.USERPROFILE]['.kube'] = { config: 'data' }; + allDirs[process.env.USERPROFILE as string]['.kube'] = { config: 'data' }; mockfs(allDirs); const home = findHomeDir(); @@ -506,7 +506,7 @@ describe('KubeConfig', () => { it('should load from HOMEDRIVE/HOMEPATH if present', () => { const dir = homeDrive; allDirs[dir]['.kube'] = { config: 'data' }; - mockfs(arg); + mockfs(allDirs); const home = findHomeDir(); @@ -517,8 +517,8 @@ describe('KubeConfig', () => { it('should favor HOMEDRIVE/HOMEPATH over USERPROFILE', () => { const dir = homeDrive; allDirs[dir]['.kube'] = { config: 'data' }; - allDirs[process.env.USERPROFILE]['.kube'] = { config: 'data' }; - mockfs(arg); + allDirs[process.env.USERPROFILE as string]['.kube'] = { config: 'data' }; + mockfs(allDirs); const home = findHomeDir(); @@ -527,9 +527,9 @@ describe('KubeConfig', () => { }); it('should load from USERPROFILE if present', () => { - const dir = process.env.USERPROFILE; + const dir = process.env.USERPROFILE as string; allDirs[dir]['.kube'] = { config: 'data' }; - mockfs(arg); + mockfs(allDirs); const home = findHomeDir(); @@ -538,11 +538,11 @@ describe('KubeConfig', () => { }); }); - // Just test for existence,but this will include the writability order + // Just test for existence,but this will include the writeability order describe('look for an existing directory', () => { let allDirs; let homeDrive; - before(() => { + beforeEach(() => { allDirs = {}; process.env.HOME = 'home'; process.env.HOMEDRIVE = 'drive'; @@ -551,12 +551,10 @@ describe('KubeConfig', () => { homeDrive = join(process.env.HOMEDRIVE, process.env.HOMEPATH); }); it('should load from HOME if present', () => { - allDirs = { - [process.env.HOME]: 'data', - [homeDrive]: 'data', - [process.env.USERPROFILE]: 'data' - } - const dir = process.env.HOME + allDirs[process.env.HOME as string] = 'data'; + allDirs[homeDrive] = 'data'; + allDirs[process.env.USERPROFILE as string] = 'data'; + const dir = process.env.HOME; mockfs(allDirs); const home = findHomeDir(); @@ -564,39 +562,33 @@ describe('KubeConfig', () => { mockfs.restore(); expect(home).to.equal(dir); }); - it('should load from homeDrive if present', () => { - allDirs = { - [homeDrive]: 'data', - [process.env.USERPROFILE]: 'data' - } - const dir = process.env.HOME + it('should load from USERPROFILE if present', () => { + allDirs[homeDrive] = 'data'; + allDirs[process.env.USERPROFILE as string] = 'data'; mockfs(allDirs); const home = findHomeDir(); mockfs.restore(); - expect(home).to.equal(homeDrive); + expect(home).to.equal(process.env.USERPROFILE); }); - it('should load from USERPROFILE if present', () => { - allDirs = { - [process.env.USERPROFILE]: 'data' - } - const dir = process.env.USERPROFILE; + it('should load from homeDrive if present', () => { + allDirs[homeDrive] = 'data'; mockfs(allDirs); const home = findHomeDir(); mockfs.restore(); - expect(home).to.equal(dir); + expect(home).to.equal(homeDrive); }); - }); - it('should return null if nothing is present', () => { - mockfs({}); + it('should return HOME when no home-ish directories are present', () => { + mockfs({}); - const home = findHomeDir(); + const home = findHomeDir(); - mockfs.restore(); - expect(home).to.equal(null); + mockfs.restore(); + expect(home).to.equal(process.env.HOME); + }); }); }); From da0e7cb172e3510252bc26b6ca2f87256e0f99e9 Mon Sep 17 00:00:00 2001 From: Eric Promislow Date: Wed, 1 Dec 2021 13:36:34 -0800 Subject: [PATCH 5/8] Improve variable names --- src/config.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/config.ts b/src/config.ts index b47a8335e04..f0da986bda5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -540,12 +540,16 @@ export function findHomeDir(): string | null { } return null; } + // $HOME is always favored, but the k8s go-client prefers the other two env vars + // differently depending on whether .kube/config exists or not. const homeDrivePath = process.env.HOMEDRIVE && process.env.HOMEPATH ? path.join(process.env.HOMEDRIVE, process.env.HOMEPATH) : null; - const dirList1: string[] = dropDuplicatesAndNils([process.env.HOME, homeDrivePath, process.env.USERPROFILE]); - const dirList2: string[] = dropDuplicatesAndNils([process.env.HOME, process.env.USERPROFILE, homeDrivePath]); + const favourHomeDrivePathList: string[] = + dropDuplicatesAndNils([process.env.HOME, homeDrivePath, process.env.USERPROFILE]); + const favourUserProfileList: string[] = + dropDuplicatesAndNils([process.env.HOME, process.env.USERPROFILE, homeDrivePath]); // 1. the first of %HOME%, %HOMEDRIVE%%HOMEPATH%, %USERPROFILE% containing a `.kube\config` file is returned. - for (const dir of dirList1) { + for (const dir of favourHomeDrivePathList) { try { fs.accessSync(path.join(dir, '.kube', 'config')); return dir; @@ -553,7 +557,7 @@ export function findHomeDir(): string | null { } catch (ignore) {} } // 2. ...the first of %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that exists and is writeable is returned - for (const dir of dirList2) { + for (const dir of favourUserProfileList) { try { const lstat = fs.lstatSync(dir); // tslint:disable-next-line:no-bitwise @@ -564,7 +568,7 @@ export function findHomeDir(): string | null { } catch (ignore) {} } // 3. ...the first of %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that exists is returned. - for (const dir of dirList2) { + for (const dir of favourUserProfileList) { try { fs.accessSync(dir); return dir; @@ -573,7 +577,7 @@ export function findHomeDir(): string | null { } // 4. if none of those locations exists, the first of // %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that is set is returned. - return dirList2[0] || null; + return favourUserProfileList[0] || null; } export interface Named { From 9b51e59ace51a6848d4b91fe5072fd94ba9e6eb8 Mon Sep 17 00:00:00 2001 From: Eric Promislow Date: Thu, 2 Dec 2021 10:30:33 -0800 Subject: [PATCH 6/8] Reformulate the code so there are only arrays of simple types --- src/config.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/config.ts b/src/config.ts index f0da986bda5..7cedaba52a5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -516,7 +516,7 @@ export function bufferFromFileOrString(file?: string, data?: string): Buffer | n return null; } -function dropDuplicatesAndNils(a: Array): string[] { +function dropDuplicatesAndNils(a: string[]): string[] { return a.reduce((acceptedValues, currentValue) => { // Good-enough algorithm for reducing a small (3 items at this point) array into an ordered list // of unique non-empty strings. @@ -543,11 +543,11 @@ export function findHomeDir(): string | null { // $HOME is always favored, but the k8s go-client prefers the other two env vars // differently depending on whether .kube/config exists or not. const homeDrivePath = process.env.HOMEDRIVE && process.env.HOMEPATH ? - path.join(process.env.HOMEDRIVE, process.env.HOMEPATH) : null; - const favourHomeDrivePathList: string[] = - dropDuplicatesAndNils([process.env.HOME, homeDrivePath, process.env.USERPROFILE]); - const favourUserProfileList: string[] = - dropDuplicatesAndNils([process.env.HOME, process.env.USERPROFILE, homeDrivePath]); + path.join(process.env.HOMEDRIVE, process.env.HOMEPATH) : ''; + const homePath = process.env.HOME || ''; + const userProfile = process.env.USERPROFILE || ''; + const favourHomeDrivePathList: string[] = dropDuplicatesAndNils([homePath, homeDrivePath, userProfile]); + const favourUserProfileList: string[] = dropDuplicatesAndNils([homePath, userProfile, homeDrivePath]); // 1. the first of %HOME%, %HOMEDRIVE%%HOMEPATH%, %USERPROFILE% containing a `.kube\config` file is returned. for (const dir of favourHomeDrivePathList) { try { From e8be08b8e7d991d0f19f4a2126d316a87f28223a Mon Sep 17 00:00:00 2001 From: Eric Promislow Date: Fri, 3 Dec 2021 10:49:43 -0800 Subject: [PATCH 7/8] Conform to ws conventions by running prettier. --- src/config.ts | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/config.ts b/src/config.ts index 7cedaba52a5..10b862522a5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -517,15 +517,18 @@ export function bufferFromFileOrString(file?: string, data?: string): Buffer | n } function dropDuplicatesAndNils(a: string[]): string[] { - return a.reduce((acceptedValues, currentValue) => { - // Good-enough algorithm for reducing a small (3 items at this point) array into an ordered list - // of unique non-empty strings. - if (currentValue && !acceptedValues.includes(currentValue)) { - return acceptedValues.concat(currentValue); - } else { - return acceptedValues; - } - }, [] as string[]); + return a.reduce( + (acceptedValues, currentValue) => { + // Good-enough algorithm for reducing a small (3 items at this point) array into an ordered list + // of unique non-empty strings. + if (currentValue && !acceptedValues.includes(currentValue)) { + return acceptedValues.concat(currentValue); + } else { + return acceptedValues; + } + }, + [] as string[], + ); } // Only public for testing. @@ -542,8 +545,10 @@ export function findHomeDir(): string | null { } // $HOME is always favored, but the k8s go-client prefers the other two env vars // differently depending on whether .kube/config exists or not. - const homeDrivePath = process.env.HOMEDRIVE && process.env.HOMEPATH ? - path.join(process.env.HOMEDRIVE, process.env.HOMEPATH) : ''; + const homeDrivePath = + process.env.HOMEDRIVE && process.env.HOMEPATH + ? path.join(process.env.HOMEDRIVE, process.env.HOMEPATH) + : ''; const homePath = process.env.HOME || ''; const userProfile = process.env.USERPROFILE || ''; const favourHomeDrivePathList: string[] = dropDuplicatesAndNils([homePath, homeDrivePath, userProfile]); @@ -560,7 +565,7 @@ export function findHomeDir(): string | null { for (const dir of favourUserProfileList) { try { const lstat = fs.lstatSync(dir); -// tslint:disable-next-line:no-bitwise + // tslint:disable-next-line:no-bitwise if (lstat && (lstat.mode & fs.constants.S_IXUSR) === fs.constants.S_IXUSR) { return dir; } From ad25e51b53e78c476a08e597167962f6447c7068 Mon Sep 17 00:00:00 2001 From: Eric Promislow Date: Fri, 3 Dec 2021 13:59:17 -0800 Subject: [PATCH 8/8] Prefer fs.access(..., 'w') to fs.lstat() & BITS --- src/config.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/config.ts b/src/config.ts index 10b862522a5..3af34dd3e75 100644 --- a/src/config.ts +++ b/src/config.ts @@ -543,7 +543,7 @@ export function findHomeDir(): string | null { } return null; } - // $HOME is always favored, but the k8s go-client prefers the other two env vars + // $HOME is always favoured, but the k8s go-client prefers the other two env vars // differently depending on whether .kube/config exists or not. const homeDrivePath = process.env.HOMEDRIVE && process.env.HOMEPATH @@ -564,11 +564,8 @@ export function findHomeDir(): string | null { // 2. ...the first of %HOME%, %USERPROFILE%, %HOMEDRIVE%%HOMEPATH% that exists and is writeable is returned for (const dir of favourUserProfileList) { try { - const lstat = fs.lstatSync(dir); - // tslint:disable-next-line:no-bitwise - if (lstat && (lstat.mode & fs.constants.S_IXUSR) === fs.constants.S_IXUSR) { - return dir; - } + fs.accessSync(dir, fs.constants.W_OK); + return dir; // tslint:disable-next-line:no-empty } catch (ignore) {} }