From eeed11b6130361c363b37a59e0ff28df855c3117 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Wed, 29 Nov 2023 14:49:53 -0800 Subject: [PATCH 01/26] WIP create vitals command --- packages/cli/src/commands/doctor/vitals.ts | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 packages/cli/src/commands/doctor/vitals.ts diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts new file mode 100644 index 0000000000..38f6beca40 --- /dev/null +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -0,0 +1,56 @@ +/* eslint-disable prefer-const */ +import color from '@heroku-cli/color' +import {Command, flags} from '@heroku-cli/command' +import * as Heroku from '@heroku-cli/schema' +import {Args, ux} from '@oclif/core' +import {sortBy} from 'lodash' + +export default class DoctorVitals extends Command { + static description = 'list local user setup for debugging' + static topic = 'doctor' + + // static flags = { + // app: flags.app({required: false}), + // json: flags.boolean({description: 'display as json', required: false}), + // } + + async run() { + const {args, flags} = await this.parse(DoctorVitals) + const time = new Date() + let dateChecked = time.toISOString().split('T')[0] + let cliInstallMethod = null + let os = null + let cliVersion = null + let nodeVersion = null + let networkConfig = { + httpsProxy: null, + } + let installedPlugins = null + let herokuStatus = { + apps: 'No known issues at this time.', + data: 'No known issues at this time.', + tools: 'No known issues at this time.', + } + + const appsUp = true + const dataUp = true + const toolsUp = true + + ux.styledHeader(`${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}`) + ux.log(`CLI Install Method: ${cliInstallMethod}`) + ux.log(`OS: ${os}`) + ux.log(`Heroku CLI Version: ${cliVersion}`) + ux.log(`Node Version: ${nodeVersion}`) + ux.log('Network Config:') + ux.log(`- HTTPSProxy: ${networkConfig.httpsProxy}`) + ux.log('Installed Plugins:') + ux.log(`- ${installedPlugins}`) + ux.log('\n') + + ux.log(`${color.heroku('Heroku Status')}`) + ux.log(`${color.heroku('----------------------------------------')}`) + ux.log(`${appsUp ? color.green(`App: ${herokuStatus.apps}`) : color.red(`App: ${herokuStatus.apps}`)}`) + ux.log(`${dataUp ? color.green(`Data: ${herokuStatus.data}`) : color.red(`Data: ${herokuStatus.data}`)}`) + ux.log(`${toolsUp ? color.green(`Tools: ${herokuStatus.tools}`) : color.red(`Tools: ${herokuStatus.tools}`)}`) + } +} From 90393685a0fbbeb899ae8d1bd082579923f5e565 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Wed, 29 Nov 2023 15:07:02 -0800 Subject: [PATCH 02/26] WIP add color scheme to output --- packages/cli/src/commands/doctor/vitals.ts | 25 +++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 38f6beca40..47687404ea 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -4,6 +4,7 @@ import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' import {Args, ux} from '@oclif/core' import {sortBy} from 'lodash' +const {version} = require('../../../package.json') export default class DoctorVitals extends Command { static description = 'list local user setup for debugging' @@ -18,14 +19,14 @@ export default class DoctorVitals extends Command { const {args, flags} = await this.parse(DoctorVitals) const time = new Date() let dateChecked = time.toISOString().split('T')[0] - let cliInstallMethod = null - let os = null - let cliVersion = null - let nodeVersion = null + let cliInstallMethod = 'brew' + let os = 'darwin-x64' + let cliVersion = `v${version}` + let nodeVersion = 'v16.19.0' let networkConfig = { httpsProxy: null, } - let installedPlugins = null + let installedPlugins = 'myplugin' let herokuStatus = { apps: 'No known issues at this time.', data: 'No known issues at this time.', @@ -37,13 +38,13 @@ export default class DoctorVitals extends Command { const toolsUp = true ux.styledHeader(`${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}`) - ux.log(`CLI Install Method: ${cliInstallMethod}`) - ux.log(`OS: ${os}`) - ux.log(`Heroku CLI Version: ${cliVersion}`) - ux.log(`Node Version: ${nodeVersion}`) - ux.log('Network Config:') - ux.log(`- HTTPSProxy: ${networkConfig.httpsProxy}`) - ux.log('Installed Plugins:') + ux.log(`${color.cyan('CLI Install Method:')} ${cliInstallMethod}`) + ux.log(`${color.cyan('OS:')} ${os}`) + ux.log(`${color.cyan('Heroku CLI Version:')} ${cliVersion}`) + ux.log(`${color.cyan('Node Version:')} ${nodeVersion}`) + ux.log(`${color.cyan('Network Config:')}`) + ux.log(`- ${color.cyan('HTTPSProxy:')} ${networkConfig.httpsProxy}`) + ux.log(`${color.cyan('Installed Plugins:')}`) ux.log(`- ${installedPlugins}`) ux.log('\n') From 958a3d729f62e784c8553b6666a0dc8b41472e58 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Wed, 29 Nov 2023 15:19:35 -0800 Subject: [PATCH 03/26] WIP add os & cli version --- packages/cli/src/commands/doctor/vitals.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 47687404ea..eaa653c65d 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -4,7 +4,6 @@ import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' import {Args, ux} from '@oclif/core' import {sortBy} from 'lodash' -const {version} = require('../../../package.json') export default class DoctorVitals extends Command { static description = 'list local user setup for debugging' @@ -20,8 +19,8 @@ export default class DoctorVitals extends Command { const time = new Date() let dateChecked = time.toISOString().split('T')[0] let cliInstallMethod = 'brew' - let os = 'darwin-x64' - let cliVersion = `v${version}` + let os = this.config.platform + let cliVersion = `v${this.config.version}` let nodeVersion = 'v16.19.0' let networkConfig = { httpsProxy: null, From 09b8be6ffb89691050b3968b69eb4001e7ade29d Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Wed, 29 Nov 2023 15:28:23 -0800 Subject: [PATCH 04/26] WIP get node version locally --- packages/cli/src/commands/doctor/vitals.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index eaa653c65d..ee173a01de 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -4,6 +4,7 @@ import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' import {Args, ux} from '@oclif/core' import {sortBy} from 'lodash' +const exec = require('child_process').exec export default class DoctorVitals extends Command { static description = 'list local user setup for debugging' @@ -17,6 +18,15 @@ export default class DoctorVitals extends Command { async run() { const {args, flags} = await this.parse(DoctorVitals) const time = new Date() + + exec('node -v', + (error: any, stdout: any, stderr: any) => { + console.log('stdout: ' + stdout) + console.log('stderr: ' + stderr) + if (error !== null) { + console.log('exec error: ' + error) + } + }) let dateChecked = time.toISOString().split('T')[0] let cliInstallMethod = 'brew' let os = this.config.platform From 9d9600f0f653ea3ac7152c4f8500164780b28f94 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Wed, 29 Nov 2023 15:41:16 -0800 Subject: [PATCH 05/26] WIP add functions to retrieve necessary information --- packages/cli/src/commands/doctor/vitals.ts | 58 ++++++++++++++-------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index ee173a01de..1281f46038 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -2,38 +2,52 @@ import color from '@heroku-cli/color' import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' -import {Args, ux} from '@oclif/core' +import {ux} from '@oclif/core' import {sortBy} from 'lodash' const exec = require('child_process').exec +const getLocalNodeVersion = () => { + exec('node -v', + (error: any, stdout: any) => { + if (error !== null) { + return 'unavailable' + } + + return stdout + }) +} + +const getInstallMethod = () => { + return 'brew' +} + +const getLocalProxySettings = (unmasked = false) => { + if (unmasked) { + return null + } + + return 'xxxxx.proxy' +} + export default class DoctorVitals extends Command { static description = 'list local user setup for debugging' static topic = 'doctor' - // static flags = { - // app: flags.app({required: false}), - // json: flags.boolean({description: 'display as json', required: false}), - // } + static flags = { + unmasked: flags.boolean({required: false}), + json: flags.boolean({description: 'display as json', required: false}), + } async run() { - const {args, flags} = await this.parse(DoctorVitals) + const {flags} = await this.parse(DoctorVitals) const time = new Date() - - exec('node -v', - (error: any, stdout: any, stderr: any) => { - console.log('stdout: ' + stdout) - console.log('stderr: ' + stderr) - if (error !== null) { - console.log('exec error: ' + error) - } - }) - let dateChecked = time.toISOString().split('T')[0] - let cliInstallMethod = 'brew' - let os = this.config.platform - let cliVersion = `v${this.config.version}` - let nodeVersion = 'v16.19.0' - let networkConfig = { - httpsProxy: null, + const dateChecked = time.toISOString().split('T')[0] + const cliInstallMethod = getInstallMethod() + const os = this.config.platform + const cliVersion = `v${this.config.version}` + const nodeVersion = getLocalNodeVersion() + const networkConfig = { + httpsProxy: getLocalProxySettings(flags.unmasked), } let installedPlugins = 'myplugin' let herokuStatus = { From 55b8dadbad8ce861059b7c9ab14eb15715720761 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 07:43:26 -0800 Subject: [PATCH 06/26] WIP add install path --- packages/cli/src/commands/doctor/vitals.ts | 27 +++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 1281f46038..0f055c0386 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -4,23 +4,23 @@ import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' import {ux} from '@oclif/core' import {sortBy} from 'lodash' -const exec = require('child_process').exec +const {exec} = require('child_process'); +const {promisify} = require('util') +const execAsync = promisify(exec) -const getLocalNodeVersion = () => { - exec('node -v', - (error: any, stdout: any) => { - if (error !== null) { - return 'unavailable' - } - - return stdout - }) +const getLocalNodeVersion = async () => { + const {stdout} = await execAsync('node -v') + return stdout } const getInstallMethod = () => { return 'brew' } +const getInstallLocation = () => { + return null +} + const getLocalProxySettings = (unmasked = false) => { if (unmasked) { return null @@ -40,12 +40,16 @@ export default class DoctorVitals extends Command { async run() { const {flags} = await this.parse(DoctorVitals) + + console.log(Object.entries(this.config.plugins)) + const time = new Date() const dateChecked = time.toISOString().split('T')[0] const cliInstallMethod = getInstallMethod() + const cliInstallLocation = getInstallLocation() const os = this.config.platform const cliVersion = `v${this.config.version}` - const nodeVersion = getLocalNodeVersion() + const nodeVersion = await getLocalNodeVersion() const networkConfig = { httpsProxy: getLocalProxySettings(flags.unmasked), } @@ -62,6 +66,7 @@ export default class DoctorVitals extends Command { ux.styledHeader(`${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}`) ux.log(`${color.cyan('CLI Install Method:')} ${cliInstallMethod}`) + ux.log(`${color.cyan('CLI Install Location:')} ${cliInstallLocation}`) ux.log(`${color.cyan('OS:')} ${os}`) ux.log(`${color.cyan('Heroku CLI Version:')} ${cliVersion}`) ux.log(`${color.cyan('Node Version:')} ${nodeVersion}`) From 696c554fd36ffe9ece8b527bbd67c3f4d5356c0c Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 08:57:26 -0800 Subject: [PATCH 07/26] Add install path logic --- packages/cli/src/commands/doctor/vitals.ts | 27 +++++++++++----------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 0f055c0386..15cf80c143 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -17,8 +17,10 @@ const getInstallMethod = () => { return 'brew' } -const getInstallLocation = () => { - return null +const getInstallLocation = async () => { + const {stdout} = await execAsync('which heroku') + const formattedOutput = stdout.replace(/\n/g, '') + return formattedOutput } const getLocalProxySettings = (unmasked = false) => { @@ -29,6 +31,11 @@ const getLocalProxySettings = (unmasked = false) => { return 'xxxxx.proxy' } +const getHerokuStatus = async () => { + const {stdout} = await execAsync('heroku status') + return stdout +} + export default class DoctorVitals extends Command { static description = 'list local user setup for debugging' static topic = 'doctor' @@ -46,7 +53,7 @@ export default class DoctorVitals extends Command { const time = new Date() const dateChecked = time.toISOString().split('T')[0] const cliInstallMethod = getInstallMethod() - const cliInstallLocation = getInstallLocation() + const cliInstallLocation = await getInstallLocation() const os = this.config.platform const cliVersion = `v${this.config.version}` const nodeVersion = await getLocalNodeVersion() @@ -54,15 +61,9 @@ export default class DoctorVitals extends Command { httpsProxy: getLocalProxySettings(flags.unmasked), } let installedPlugins = 'myplugin' - let herokuStatus = { - apps: 'No known issues at this time.', - data: 'No known issues at this time.', - tools: 'No known issues at this time.', - } + const herokuStatus = await getHerokuStatus() - const appsUp = true - const dataUp = true - const toolsUp = true + const herokuUp = true ux.styledHeader(`${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}`) ux.log(`${color.cyan('CLI Install Method:')} ${cliInstallMethod}`) @@ -78,8 +79,6 @@ export default class DoctorVitals extends Command { ux.log(`${color.heroku('Heroku Status')}`) ux.log(`${color.heroku('----------------------------------------')}`) - ux.log(`${appsUp ? color.green(`App: ${herokuStatus.apps}`) : color.red(`App: ${herokuStatus.apps}`)}`) - ux.log(`${dataUp ? color.green(`Data: ${herokuStatus.data}`) : color.red(`Data: ${herokuStatus.data}`)}`) - ux.log(`${toolsUp ? color.green(`Tools: ${herokuStatus.tools}`) : color.red(`Tools: ${herokuStatus.tools}`)}`) + ux.log(herokuUp ? color.green(herokuStatus) : color.red(herokuStatus)) } } From f68b04db38d72ef5d4891aa7055ce4e3d849d6ca Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 09:40:56 -0800 Subject: [PATCH 08/26] Add getInstalledPlugins logic --- packages/cli/src/commands/doctor/vitals.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 15cf80c143..9d9d3b1986 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -23,14 +23,21 @@ const getInstallLocation = async () => { return formattedOutput } -const getLocalProxySettings = (unmasked = false) => { +const getLocalProxySettings = async (unmasked = false) => { + const {stdout} = await execAsync('scutil --proxy') + if (unmasked) { - return null + return stdout } return 'xxxxx.proxy' } +const getInstalledPLugins = async () => { + const {stdout} = await execAsync('heroku plugins') + return stdout +} + const getHerokuStatus = async () => { const {stdout} = await execAsync('heroku status') return stdout @@ -58,9 +65,9 @@ export default class DoctorVitals extends Command { const cliVersion = `v${this.config.version}` const nodeVersion = await getLocalNodeVersion() const networkConfig = { - httpsProxy: getLocalProxySettings(flags.unmasked), + httpsProxy: await getLocalProxySettings(flags.unmasked), } - let installedPlugins = 'myplugin' + const installedPlugins = await getInstalledPLugins() const herokuStatus = await getHerokuStatus() const herokuUp = true From 3dad51cff3a9c2ddb9f167a72d55f674cb51f084 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 09:45:27 -0800 Subject: [PATCH 09/26] Remove console.log noise --- packages/cli/src/commands/doctor/vitals.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 9d9d3b1986..ce9df26976 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -54,9 +54,6 @@ export default class DoctorVitals extends Command { async run() { const {flags} = await this.parse(DoctorVitals) - - console.log(Object.entries(this.config.plugins)) - const time = new Date() const dateChecked = time.toISOString().split('T')[0] const cliInstallMethod = getInstallMethod() From a6e17eb510fa35e18ff5c19fdb053c988c941310 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 10:38:16 -0800 Subject: [PATCH 10/26] Update herokuUp variable naming --- packages/cli/src/commands/doctor/vitals.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index ce9df26976..1f36b87b16 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -67,7 +67,7 @@ export default class DoctorVitals extends Command { const installedPlugins = await getInstalledPLugins() const herokuStatus = await getHerokuStatus() - const herokuUp = true + const isHerokuUp = true ux.styledHeader(`${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}`) ux.log(`${color.cyan('CLI Install Method:')} ${cliInstallMethod}`) @@ -76,13 +76,13 @@ export default class DoctorVitals extends Command { ux.log(`${color.cyan('Heroku CLI Version:')} ${cliVersion}`) ux.log(`${color.cyan('Node Version:')} ${nodeVersion}`) ux.log(`${color.cyan('Network Config:')}`) - ux.log(`- ${color.cyan('HTTPSProxy:')} ${networkConfig.httpsProxy}`) + ux.log(`${color.cyan('HTTPSProxy:')} ${networkConfig.httpsProxy}`) ux.log(`${color.cyan('Installed Plugins:')}`) - ux.log(`- ${installedPlugins}`) + ux.log(`${installedPlugins}`) ux.log('\n') ux.log(`${color.heroku('Heroku Status')}`) ux.log(`${color.heroku('----------------------------------------')}`) - ux.log(herokuUp ? color.green(herokuStatus) : color.red(herokuStatus)) + ux.log(isHerokuUp ? color.green(herokuStatus) : color.red(herokuStatus)) } } From bd7caad366396e1584672ac67d16d8b3f37b931d Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 11:04:27 -0800 Subject: [PATCH 11/26] Code clean up --- packages/cli/src/commands/doctor/vitals.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 1f36b87b16..ac7361cafe 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -30,7 +30,7 @@ const getLocalProxySettings = async (unmasked = false) => { return stdout } - return 'xxxxx.proxy' + return 'xxxxx.proxy\n' } const getInstalledPLugins = async () => { @@ -75,11 +75,12 @@ export default class DoctorVitals extends Command { ux.log(`${color.cyan('OS:')} ${os}`) ux.log(`${color.cyan('Heroku CLI Version:')} ${cliVersion}`) ux.log(`${color.cyan('Node Version:')} ${nodeVersion}`) - ux.log(`${color.cyan('Network Config:')}`) - ux.log(`${color.cyan('HTTPSProxy:')} ${networkConfig.httpsProxy}`) - ux.log(`${color.cyan('Installed Plugins:')}`) + + ux.log(`${color.cyan('Network Config')}`) + ux.log(`HTTPSProxy: ${networkConfig.httpsProxy}`) + + ux.log(`${color.cyan('Installed Plugins')}`) ux.log(`${installedPlugins}`) - ux.log('\n') ux.log(`${color.heroku('Heroku Status')}`) ux.log(`${color.heroku('----------------------------------------')}`) From 930469a892e28128e5649d563a18fc164dec8c1a Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 11:56:28 -0800 Subject: [PATCH 12/26] Bold heroku status --- packages/cli/src/commands/doctor/vitals.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index ac7361cafe..eb8700a32c 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -4,7 +4,7 @@ import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' import {ux} from '@oclif/core' import {sortBy} from 'lodash' -const {exec} = require('child_process'); +const {exec} = require('child_process') const {promisify} = require('util') const execAsync = promisify(exec) @@ -82,8 +82,8 @@ export default class DoctorVitals extends Command { ux.log(`${color.cyan('Installed Plugins')}`) ux.log(`${installedPlugins}`) - ux.log(`${color.heroku('Heroku Status')}`) - ux.log(`${color.heroku('----------------------------------------')}`) + ux.log(`${color.bold(color.heroku('Heroku Status'))}`) + ux.log(`${color.bold(color.heroku('----------------------------------------'))}`) ux.log(isHerokuUp ? color.green(herokuStatus) : color.red(herokuStatus)) } } From fd7f67bf4257db3b3bc91fddb462ea4ff27020ee Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 13:21:03 -0800 Subject: [PATCH 13/26] Add proxy logic to getLocalProxySettings --- packages/cli/src/commands/doctor/vitals.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index eb8700a32c..4a592ef59a 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -24,7 +24,16 @@ const getInstallLocation = async () => { } const getLocalProxySettings = async (unmasked = false) => { - const {stdout} = await execAsync('scutil --proxy') + const command = `httpsProxy=$(scutil --proxy | awk -F': ' '/HTTPSProxy/ {print $2}') + + # Check if HTTPSProxy has a value + if [ -n "$httpsProxy" ]; then + echo "$httpsProxy" + else + echo "no proxy set" + fi` + + const {stdout} = await execAsync(command) if (unmasked) { return stdout From 188790354b2c70f5172443196989588a50924af2 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 13:41:02 -0800 Subject: [PATCH 14/26] Update masking logic --- packages/cli/src/commands/doctor/vitals.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 4a592ef59a..0d7401c3d7 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -35,11 +35,13 @@ const getLocalProxySettings = async (unmasked = false) => { const {stdout} = await execAsync(command) + const hasProxySet = !stdout.includes('no proxy set') + if (unmasked) { return stdout } - return 'xxxxx.proxy\n' + return hasProxySet ? 'xxxxx\n' : stdout } const getInstalledPLugins = async () => { From b0b79fd752cc82588683241598c004660dcb2f8b Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 13:57:45 -0800 Subject: [PATCH 15/26] Clean up code --- packages/cli/src/commands/doctor/vitals.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 0d7401c3d7..2f7c71a5c3 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -1,9 +1,8 @@ -/* eslint-disable prefer-const */ import color from '@heroku-cli/color' import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' import {ux} from '@oclif/core' -import {sortBy} from 'lodash' +import * as lodash from 'lodash' const {exec} = require('child_process') const {promisify} = require('util') const execAsync = promisify(exec) From da6ef3ce47851993055d098b41238f34636a8737 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 14:32:19 -0800 Subject: [PATCH 16/26] Add copy-results flag --- packages/cli/src/commands/doctor/vitals.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 2f7c71a5c3..d6201757cc 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -59,6 +59,7 @@ export default class DoctorVitals extends Command { static flags = { unmasked: flags.boolean({required: false}), + 'copy-results': flags.boolean({description: 'copies results to clipboard', required: false}), json: flags.boolean({description: 'display as json', required: false}), } From 81732a25360db80440832b1eef36b1230c70faed Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 14:53:45 -0800 Subject: [PATCH 17/26] Add logic for copying results to clipboard --- packages/cli/package.json | 1 + packages/cli/src/commands/doctor/vitals.ts | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index d4deefab9e..e262891fd5 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -46,6 +46,7 @@ "ansi-escapes": "3.2.0", "async-file": "^2.0.2", "chalk": "^2.4.2", + "clipboardy": "^3.0.0", "date-fns": "^2.30.0", "debug": "4.1.1", "dotenv": "^16.3.1", diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index d6201757cc..131cd7b16e 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -3,6 +3,7 @@ import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' import {ux} from '@oclif/core' import * as lodash from 'lodash' +import * as clipboardy from 'clipboardy' const {exec} = require('child_process') const {promisify} = require('util') const execAsync = promisify(exec) @@ -33,7 +34,6 @@ const getLocalProxySettings = async (unmasked = false) => { fi` const {stdout} = await execAsync(command) - const hasProxySet = !stdout.includes('no proxy set') if (unmasked) { @@ -65,6 +65,7 @@ export default class DoctorVitals extends Command { async run() { const {flags} = await this.parse(DoctorVitals) + const copyResults = flags['copy-results'] const time = new Date() const dateChecked = time.toISOString().split('T')[0] const cliInstallMethod = getInstallMethod() @@ -96,5 +97,22 @@ export default class DoctorVitals extends Command { ux.log(`${color.bold(color.heroku('Heroku Status'))}`) ux.log(`${color.bold(color.heroku('----------------------------------------'))}`) ux.log(isHerokuUp ? color.green(herokuStatus) : color.red(herokuStatus)) + + if (copyResults) { + // copy results to clipboard here + clipboardy.default.writeSync(`${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}`) + clipboardy.default.writeSync(`${color.cyan('CLI Install Method:')} ${cliInstallMethod}`) + clipboardy.default.writeSync(`${color.cyan('CLI Install Location:')} ${cliInstallLocation}`) + clipboardy.default.writeSync(`${color.cyan('OS:')} ${os}`) + clipboardy.default.writeSync(`${color.cyan('Heroku CLI Version:')} ${cliVersion}`) + clipboardy.default.writeSync(`${color.cyan('Node Version:')} ${nodeVersion}`) + clipboardy.default.writeSync(`${color.cyan('Network Config')}`) + clipboardy.default.writeSync(`HTTPSProxy: ${networkConfig.httpsProxy}`) + clipboardy.default.writeSync(`${color.cyan('Installed Plugins')}`) + clipboardy.default.writeSync(`${installedPlugins}`) + clipboardy.default.writeSync(`${color.bold(color.heroku('Heroku Status'))}`) + clipboardy.default.writeSync(`${color.bold(color.heroku('----------------------------------------'))}`) + clipboardy.default.writeSync(isHerokuUp ? color.green(herokuStatus) : color.red(herokuStatus)) + } } } From 69ee2debc9b9ee1638a09489e0a311ceb4c75987 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 16:13:09 -0800 Subject: [PATCH 18/26] Add logic for copying results to clipboard part 2 --- packages/cli/src/commands/doctor/vitals.ts | 42 ++++++++++++++-------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 131cd7b16e..d1d51e31ef 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -3,7 +3,7 @@ import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' import {ux} from '@oclif/core' import * as lodash from 'lodash' -import * as clipboardy from 'clipboardy' +// import clipboardy from 'clipboardy' const {exec} = require('child_process') const {promisify} = require('util') const execAsync = promisify(exec) @@ -53,6 +53,17 @@ const getHerokuStatus = async () => { return stdout } +const copyToClipboard = async (value: any) => { + const isWindows = process.platform === 'win32' + const copyCommand = isWindows ? 'clip' : 'pbcopy' + + const {stdout} = await execAsync(`echo ${value} | ${copyCommand}`) + console.log('value', value) + console.log('copyCommand', copyCommand) + console.log('stdout HERE', stdout) + return stdout +} + export default class DoctorVitals extends Command { static description = 'list local user setup for debugging' static topic = 'doctor' @@ -80,6 +91,7 @@ export default class DoctorVitals extends Command { const herokuStatus = await getHerokuStatus() const isHerokuUp = true + let copiedResults = '' ux.styledHeader(`${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}`) ux.log(`${color.cyan('CLI Install Method:')} ${cliInstallMethod}`) @@ -100,19 +112,21 @@ export default class DoctorVitals extends Command { if (copyResults) { // copy results to clipboard here - clipboardy.default.writeSync(`${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}`) - clipboardy.default.writeSync(`${color.cyan('CLI Install Method:')} ${cliInstallMethod}`) - clipboardy.default.writeSync(`${color.cyan('CLI Install Location:')} ${cliInstallLocation}`) - clipboardy.default.writeSync(`${color.cyan('OS:')} ${os}`) - clipboardy.default.writeSync(`${color.cyan('Heroku CLI Version:')} ${cliVersion}`) - clipboardy.default.writeSync(`${color.cyan('Node Version:')} ${nodeVersion}`) - clipboardy.default.writeSync(`${color.cyan('Network Config')}`) - clipboardy.default.writeSync(`HTTPSProxy: ${networkConfig.httpsProxy}`) - clipboardy.default.writeSync(`${color.cyan('Installed Plugins')}`) - clipboardy.default.writeSync(`${installedPlugins}`) - clipboardy.default.writeSync(`${color.bold(color.heroku('Heroku Status'))}`) - clipboardy.default.writeSync(`${color.bold(color.heroku('----------------------------------------'))}`) - clipboardy.default.writeSync(isHerokuUp ? color.green(herokuStatus) : color.red(herokuStatus)) + copiedResults += `${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}\n` + copiedResults += `${color.cyan('CLI Install Method:')} ${cliInstallMethod}\n` + copiedResults += `${color.cyan('CLI Install Location:')} ${cliInstallLocation}\n` + copiedResults += `${color.cyan('OS:')} ${os}\n` + copiedResults += `${color.cyan('Heroku CLI Version:')} ${cliVersion}\n` + copiedResults += `${color.cyan('Node Version:')} ${nodeVersion}\n` + copiedResults += `${color.cyan('Network Config')}\n` + copiedResults += `HTTPSProxy: ${networkConfig.httpsProxy}\n` + copiedResults += `${color.cyan('Installed Plugins')}\n` + copiedResults += `${installedPlugins}\n` + copiedResults += `${color.bold(color.heroku('Heroku Status'))}\n` + copiedResults += `${color.bold(color.heroku('----------------------------------------'))}\n` + copiedResults += isHerokuUp ? color.green(herokuStatus) : color.red(herokuStatus) } + + await copyToClipboard(copiedResults) } } From dd3a36b9d3fc33c4823d70c1e41fd3b1395cfa33 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 16:43:16 -0800 Subject: [PATCH 19/26] Successfully add copyToClipboard functionality --- packages/cli/package.json | 2 +- packages/cli/src/commands/doctor/vitals.ts | 33 +++++++++------------- yarn.lock | 12 +++++++- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index e262891fd5..59effb5357 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -46,7 +46,7 @@ "ansi-escapes": "3.2.0", "async-file": "^2.0.2", "chalk": "^2.4.2", - "clipboardy": "^3.0.0", + "copy-paste": "^1.5.3", "date-fns": "^2.30.0", "debug": "4.1.1", "dotenv": "^16.3.1", diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index d1d51e31ef..5ee560a71b 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -3,7 +3,7 @@ import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' import {ux} from '@oclif/core' import * as lodash from 'lodash' -// import clipboardy from 'clipboardy' +const clipboard = require('copy-paste') const {exec} = require('child_process') const {promisify} = require('util') const execAsync = promisify(exec) @@ -54,14 +54,7 @@ const getHerokuStatus = async () => { } const copyToClipboard = async (value: any) => { - const isWindows = process.platform === 'win32' - const copyCommand = isWindows ? 'clip' : 'pbcopy' - - const {stdout} = await execAsync(`echo ${value} | ${copyCommand}`) - console.log('value', value) - console.log('copyCommand', copyCommand) - console.log('stdout HERE', stdout) - return stdout + clipboard.copy(value) } export default class DoctorVitals extends Command { @@ -112,19 +105,19 @@ export default class DoctorVitals extends Command { if (copyResults) { // copy results to clipboard here - copiedResults += `${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}\n` - copiedResults += `${color.cyan('CLI Install Method:')} ${cliInstallMethod}\n` - copiedResults += `${color.cyan('CLI Install Location:')} ${cliInstallLocation}\n` - copiedResults += `${color.cyan('OS:')} ${os}\n` - copiedResults += `${color.cyan('Heroku CLI Version:')} ${cliVersion}\n` - copiedResults += `${color.cyan('Node Version:')} ${nodeVersion}\n` - copiedResults += `${color.cyan('Network Config')}\n` + copiedResults += `Heroku CLI Doctor · User Local Setup on ${dateChecked}\n` + copiedResults += `CLI Install Method: ${cliInstallMethod}\n` + copiedResults += `CLI Install Location: ${cliInstallLocation}\n` + copiedResults += `OS: ${os}\n` + copiedResults += `Heroku CLI Version: ${cliVersion}\n` + copiedResults += `Node Version: ${nodeVersion}\n` + copiedResults += 'Network Config\n' copiedResults += `HTTPSProxy: ${networkConfig.httpsProxy}\n` - copiedResults += `${color.cyan('Installed Plugins')}\n` + copiedResults += 'Installed Plugins\n' copiedResults += `${installedPlugins}\n` - copiedResults += `${color.bold(color.heroku('Heroku Status'))}\n` - copiedResults += `${color.bold(color.heroku('----------------------------------------'))}\n` - copiedResults += isHerokuUp ? color.green(herokuStatus) : color.red(herokuStatus) + copiedResults += 'Heroku Status\n' + copiedResults += '----------------------------------------\n' + copiedResults += herokuStatus } await copyToClipboard(copiedResults) diff --git a/yarn.lock b/yarn.lock index 3abdab80d1..85a0282d33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6215,6 +6215,15 @@ __metadata: languageName: node linkType: hard +"copy-paste@npm:^1.5.3": + version: 1.5.3 + resolution: "copy-paste@npm:1.5.3" + dependencies: + iconv-lite: ^0.4.8 + checksum: 97fac26ea222478e93f2177fdb29f4e90687698a600a128032aa96f15956afe45d03b4c226756264d9266ddee39926f177992cc1809cbfe659c622275095eddd + languageName: node + linkType: hard + "core-util-is@npm:1.0.2, core-util-is@npm:~1.0.0": version: 1.0.2 resolution: "core-util-is@npm:1.0.2" @@ -9317,6 +9326,7 @@ __metadata: bats: ^1.1.0 chai: ^4.2.0 chalk: ^2.4.2 + copy-paste: ^1.5.3 date-fns: ^2.30.0 debug: 4.1.1 dotenv: ^16.3.1 @@ -9564,7 +9574,7 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:^0.4.17, iconv-lite@npm:^0.4.24": +"iconv-lite@npm:^0.4.17, iconv-lite@npm:^0.4.24, iconv-lite@npm:^0.4.8": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" dependencies: From 900dd16a6292803d03aef2b985c6165f7131d10f Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 16:50:05 -0800 Subject: [PATCH 20/26] Update unmask flag description --- packages/cli/src/commands/doctor/vitals.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 5ee560a71b..061d38550a 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -62,7 +62,7 @@ export default class DoctorVitals extends Command { static topic = 'doctor' static flags = { - unmasked: flags.boolean({required: false}), + unmask: flags.boolean({description: 'unmasks fields heroku has deemed potentially sensitive', required: false}), 'copy-results': flags.boolean({description: 'copies results to clipboard', required: false}), json: flags.boolean({description: 'display as json', required: false}), } @@ -78,7 +78,7 @@ export default class DoctorVitals extends Command { const cliVersion = `v${this.config.version}` const nodeVersion = await getLocalNodeVersion() const networkConfig = { - httpsProxy: await getLocalProxySettings(flags.unmasked), + httpsProxy: await getLocalProxySettings(flags.unmask), } const installedPlugins = await getInstalledPLugins() const herokuStatus = await getHerokuStatus() From 75d18ace8f641aded39ee83df1b8697bcc51c91c Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 19:44:21 -0800 Subject: [PATCH 21/26] Add ask, diagnose, and recommend commands w/ temp font lib --- packages/cli/src/commands/doctor/ask.ts | 36 +++++ packages/cli/src/commands/doctor/diagnose.ts | 125 ++++++++++++++++++ packages/cli/src/commands/doctor/recommend.ts | 125 ++++++++++++++++++ .../font/BentonSans Regular Regular.otf | Bin 0 -> 25264 bytes packages/cli/src/lib/doctor/font/README.md | 1 + 5 files changed, 287 insertions(+) create mode 100644 packages/cli/src/commands/doctor/ask.ts create mode 100644 packages/cli/src/commands/doctor/diagnose.ts create mode 100644 packages/cli/src/commands/doctor/recommend.ts create mode 100644 packages/cli/src/lib/doctor/font/BentonSans Regular Regular.otf create mode 100644 packages/cli/src/lib/doctor/font/README.md diff --git a/packages/cli/src/commands/doctor/ask.ts b/packages/cli/src/commands/doctor/ask.ts new file mode 100644 index 0000000000..b4f947c609 --- /dev/null +++ b/packages/cli/src/commands/doctor/ask.ts @@ -0,0 +1,36 @@ +import color from '@heroku-cli/color' +import {Command, flags} from '@heroku-cli/command' +import * as Heroku from '@heroku-cli/schema' +import {Args, ux} from '@oclif/core' + +export default class DoctorAsk extends Command { + static description = 'recieve responses from HerokAI' + static topic = 'doctor' + + static flags = { + interactive: flags.boolean({description: 'use interactive mode with HerokAI', required: false}), + json: flags.boolean({description: 'display doctor:ask input/output as json', required: false}), + } + + static args = { + question: Args.string({description: 'question to ask HerokAI', required: true}), + } + + async run() { + const {args, flags} = await this.parse(DoctorAsk) + const {body: user} = await this.heroku.get('/account', {retryAuth: false}) + const userName = (user && user.name) ? ` ${user.name}` : '' + const herokAIResponse = `${color.heroku(`Hi${userName},`)} \n\nI'm just a concept right now. Remember?` + + const dialogue = { + question: args.question, + response: herokAIResponse, + } + + if (flags.json) { + ux.styledJSON(dialogue) + } else { + ux.log(herokAIResponse) + } + } +} diff --git a/packages/cli/src/commands/doctor/diagnose.ts b/packages/cli/src/commands/doctor/diagnose.ts new file mode 100644 index 0000000000..061d38550a --- /dev/null +++ b/packages/cli/src/commands/doctor/diagnose.ts @@ -0,0 +1,125 @@ +import color from '@heroku-cli/color' +import {Command, flags} from '@heroku-cli/command' +import * as Heroku from '@heroku-cli/schema' +import {ux} from '@oclif/core' +import * as lodash from 'lodash' +const clipboard = require('copy-paste') +const {exec} = require('child_process') +const {promisify} = require('util') +const execAsync = promisify(exec) + +const getLocalNodeVersion = async () => { + const {stdout} = await execAsync('node -v') + return stdout +} + +const getInstallMethod = () => { + return 'brew' +} + +const getInstallLocation = async () => { + const {stdout} = await execAsync('which heroku') + const formattedOutput = stdout.replace(/\n/g, '') + return formattedOutput +} + +const getLocalProxySettings = async (unmasked = false) => { + const command = `httpsProxy=$(scutil --proxy | awk -F': ' '/HTTPSProxy/ {print $2}') + + # Check if HTTPSProxy has a value + if [ -n "$httpsProxy" ]; then + echo "$httpsProxy" + else + echo "no proxy set" + fi` + + const {stdout} = await execAsync(command) + const hasProxySet = !stdout.includes('no proxy set') + + if (unmasked) { + return stdout + } + + return hasProxySet ? 'xxxxx\n' : stdout +} + +const getInstalledPLugins = async () => { + const {stdout} = await execAsync('heroku plugins') + return stdout +} + +const getHerokuStatus = async () => { + const {stdout} = await execAsync('heroku status') + return stdout +} + +const copyToClipboard = async (value: any) => { + clipboard.copy(value) +} + +export default class DoctorVitals extends Command { + static description = 'list local user setup for debugging' + static topic = 'doctor' + + static flags = { + unmask: flags.boolean({description: 'unmasks fields heroku has deemed potentially sensitive', required: false}), + 'copy-results': flags.boolean({description: 'copies results to clipboard', required: false}), + json: flags.boolean({description: 'display as json', required: false}), + } + + async run() { + const {flags} = await this.parse(DoctorVitals) + const copyResults = flags['copy-results'] + const time = new Date() + const dateChecked = time.toISOString().split('T')[0] + const cliInstallMethod = getInstallMethod() + const cliInstallLocation = await getInstallLocation() + const os = this.config.platform + const cliVersion = `v${this.config.version}` + const nodeVersion = await getLocalNodeVersion() + const networkConfig = { + httpsProxy: await getLocalProxySettings(flags.unmask), + } + const installedPlugins = await getInstalledPLugins() + const herokuStatus = await getHerokuStatus() + + const isHerokuUp = true + let copiedResults = '' + + ux.styledHeader(`${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}`) + ux.log(`${color.cyan('CLI Install Method:')} ${cliInstallMethod}`) + ux.log(`${color.cyan('CLI Install Location:')} ${cliInstallLocation}`) + ux.log(`${color.cyan('OS:')} ${os}`) + ux.log(`${color.cyan('Heroku CLI Version:')} ${cliVersion}`) + ux.log(`${color.cyan('Node Version:')} ${nodeVersion}`) + + ux.log(`${color.cyan('Network Config')}`) + ux.log(`HTTPSProxy: ${networkConfig.httpsProxy}`) + + ux.log(`${color.cyan('Installed Plugins')}`) + ux.log(`${installedPlugins}`) + + ux.log(`${color.bold(color.heroku('Heroku Status'))}`) + ux.log(`${color.bold(color.heroku('----------------------------------------'))}`) + ux.log(isHerokuUp ? color.green(herokuStatus) : color.red(herokuStatus)) + + if (copyResults) { + // copy results to clipboard here + copiedResults += `Heroku CLI Doctor · User Local Setup on ${dateChecked}\n` + copiedResults += `CLI Install Method: ${cliInstallMethod}\n` + copiedResults += `CLI Install Location: ${cliInstallLocation}\n` + copiedResults += `OS: ${os}\n` + copiedResults += `Heroku CLI Version: ${cliVersion}\n` + copiedResults += `Node Version: ${nodeVersion}\n` + copiedResults += 'Network Config\n' + copiedResults += `HTTPSProxy: ${networkConfig.httpsProxy}\n` + copiedResults += 'Installed Plugins\n' + copiedResults += `${installedPlugins}\n` + copiedResults += 'Heroku Status\n' + copiedResults += '----------------------------------------\n' + copiedResults += herokuStatus + } + + await copyToClipboard(copiedResults) + } +} diff --git a/packages/cli/src/commands/doctor/recommend.ts b/packages/cli/src/commands/doctor/recommend.ts new file mode 100644 index 0000000000..061d38550a --- /dev/null +++ b/packages/cli/src/commands/doctor/recommend.ts @@ -0,0 +1,125 @@ +import color from '@heroku-cli/color' +import {Command, flags} from '@heroku-cli/command' +import * as Heroku from '@heroku-cli/schema' +import {ux} from '@oclif/core' +import * as lodash from 'lodash' +const clipboard = require('copy-paste') +const {exec} = require('child_process') +const {promisify} = require('util') +const execAsync = promisify(exec) + +const getLocalNodeVersion = async () => { + const {stdout} = await execAsync('node -v') + return stdout +} + +const getInstallMethod = () => { + return 'brew' +} + +const getInstallLocation = async () => { + const {stdout} = await execAsync('which heroku') + const formattedOutput = stdout.replace(/\n/g, '') + return formattedOutput +} + +const getLocalProxySettings = async (unmasked = false) => { + const command = `httpsProxy=$(scutil --proxy | awk -F': ' '/HTTPSProxy/ {print $2}') + + # Check if HTTPSProxy has a value + if [ -n "$httpsProxy" ]; then + echo "$httpsProxy" + else + echo "no proxy set" + fi` + + const {stdout} = await execAsync(command) + const hasProxySet = !stdout.includes('no proxy set') + + if (unmasked) { + return stdout + } + + return hasProxySet ? 'xxxxx\n' : stdout +} + +const getInstalledPLugins = async () => { + const {stdout} = await execAsync('heroku plugins') + return stdout +} + +const getHerokuStatus = async () => { + const {stdout} = await execAsync('heroku status') + return stdout +} + +const copyToClipboard = async (value: any) => { + clipboard.copy(value) +} + +export default class DoctorVitals extends Command { + static description = 'list local user setup for debugging' + static topic = 'doctor' + + static flags = { + unmask: flags.boolean({description: 'unmasks fields heroku has deemed potentially sensitive', required: false}), + 'copy-results': flags.boolean({description: 'copies results to clipboard', required: false}), + json: flags.boolean({description: 'display as json', required: false}), + } + + async run() { + const {flags} = await this.parse(DoctorVitals) + const copyResults = flags['copy-results'] + const time = new Date() + const dateChecked = time.toISOString().split('T')[0] + const cliInstallMethod = getInstallMethod() + const cliInstallLocation = await getInstallLocation() + const os = this.config.platform + const cliVersion = `v${this.config.version}` + const nodeVersion = await getLocalNodeVersion() + const networkConfig = { + httpsProxy: await getLocalProxySettings(flags.unmask), + } + const installedPlugins = await getInstalledPLugins() + const herokuStatus = await getHerokuStatus() + + const isHerokuUp = true + let copiedResults = '' + + ux.styledHeader(`${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}`) + ux.log(`${color.cyan('CLI Install Method:')} ${cliInstallMethod}`) + ux.log(`${color.cyan('CLI Install Location:')} ${cliInstallLocation}`) + ux.log(`${color.cyan('OS:')} ${os}`) + ux.log(`${color.cyan('Heroku CLI Version:')} ${cliVersion}`) + ux.log(`${color.cyan('Node Version:')} ${nodeVersion}`) + + ux.log(`${color.cyan('Network Config')}`) + ux.log(`HTTPSProxy: ${networkConfig.httpsProxy}`) + + ux.log(`${color.cyan('Installed Plugins')}`) + ux.log(`${installedPlugins}`) + + ux.log(`${color.bold(color.heroku('Heroku Status'))}`) + ux.log(`${color.bold(color.heroku('----------------------------------------'))}`) + ux.log(isHerokuUp ? color.green(herokuStatus) : color.red(herokuStatus)) + + if (copyResults) { + // copy results to clipboard here + copiedResults += `Heroku CLI Doctor · User Local Setup on ${dateChecked}\n` + copiedResults += `CLI Install Method: ${cliInstallMethod}\n` + copiedResults += `CLI Install Location: ${cliInstallLocation}\n` + copiedResults += `OS: ${os}\n` + copiedResults += `Heroku CLI Version: ${cliVersion}\n` + copiedResults += `Node Version: ${nodeVersion}\n` + copiedResults += 'Network Config\n' + copiedResults += `HTTPSProxy: ${networkConfig.httpsProxy}\n` + copiedResults += 'Installed Plugins\n' + copiedResults += `${installedPlugins}\n` + copiedResults += 'Heroku Status\n' + copiedResults += '----------------------------------------\n' + copiedResults += herokuStatus + } + + await copyToClipboard(copiedResults) + } +} diff --git a/packages/cli/src/lib/doctor/font/BentonSans Regular Regular.otf b/packages/cli/src/lib/doctor/font/BentonSans Regular Regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..68c339480ab70f70d19f8f040cb35b7c0a47ad99 GIT binary patch literal 25264 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 literal 0 HcmV?d00001 diff --git a/packages/cli/src/lib/doctor/font/README.md b/packages/cli/src/lib/doctor/font/README.md new file mode 100644 index 0000000000..8203fbd72a --- /dev/null +++ b/packages/cli/src/lib/doctor/font/README.md @@ -0,0 +1 @@ +This is just temporary proof of concept. Actual font should be imported via heroku's cdn. \ No newline at end of file From 8755225e4f8390d841e8658f34b703093f0f3376 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 20:02:54 -0800 Subject: [PATCH 22/26] WIP add json logic --- packages/cli/src/commands/doctor/ask.ts | 5 ++++- .../{BentonSans Regular Regular.otf => heroku.otf} | Bin 2 files changed, 4 insertions(+), 1 deletion(-) rename packages/cli/src/lib/doctor/font/{BentonSans Regular Regular.otf => heroku.otf} (100%) diff --git a/packages/cli/src/commands/doctor/ask.ts b/packages/cli/src/commands/doctor/ask.ts index b4f947c609..6860f64147 100644 --- a/packages/cli/src/commands/doctor/ask.ts +++ b/packages/cli/src/commands/doctor/ask.ts @@ -2,6 +2,8 @@ import color from '@heroku-cli/color' import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' import {Args, ux} from '@oclif/core' +// const Font = require('ascii-art-font') +// Font.fontPath = '../../../lib/doctor/font' export default class DoctorAsk extends Command { static description = 'recieve responses from HerokAI' @@ -21,10 +23,11 @@ export default class DoctorAsk extends Command { const {body: user} = await this.heroku.get('/account', {retryAuth: false}) const userName = (user && user.name) ? ` ${user.name}` : '' const herokAIResponse = `${color.heroku(`Hi${userName},`)} \n\nI'm just a concept right now. Remember?` + const herokAIJsonResponse = `Hi${userName}, I'm just a concept right now. Remember?` const dialogue = { question: args.question, - response: herokAIResponse, + response: herokAIJsonResponse, } if (flags.json) { diff --git a/packages/cli/src/lib/doctor/font/BentonSans Regular Regular.otf b/packages/cli/src/lib/doctor/font/heroku.otf similarity index 100% rename from packages/cli/src/lib/doctor/font/BentonSans Regular Regular.otf rename to packages/cli/src/lib/doctor/font/heroku.otf From dc1ecf61473b8c47534523acca73846db13cfde1 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 20:07:16 -0800 Subject: [PATCH 23/26] Update herokAI responses --- packages/cli/src/commands/doctor/ask.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/doctor/ask.ts b/packages/cli/src/commands/doctor/ask.ts index 6860f64147..83e91598c9 100644 --- a/packages/cli/src/commands/doctor/ask.ts +++ b/packages/cli/src/commands/doctor/ask.ts @@ -22,8 +22,8 @@ export default class DoctorAsk extends Command { const {args, flags} = await this.parse(DoctorAsk) const {body: user} = await this.heroku.get('/account', {retryAuth: false}) const userName = (user && user.name) ? ` ${user.name}` : '' - const herokAIResponse = `${color.heroku(`Hi${userName},`)} \n\nI'm just a concept right now. Remember?` - const herokAIJsonResponse = `Hi${userName}, I'm just a concept right now. Remember?` + const herokAIResponse = `${color.heroku(`${color.bold(`Hi${userName},`)} \n\nI'm just a concept right now. Remember? Maybe you can get some buy in during the demo?`)}` + const herokAIJsonResponse = `Hi${userName}, I'm just a concept right now. Remember? Maybe you can get some buy in during the demo?` const dialogue = { question: args.question, From c3f5548d0e0eb791911799aeb920eb2eaecf9761 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Thu, 30 Nov 2023 23:35:35 -0800 Subject: [PATCH 24/26] WIP add logic for first pass on diagnose command --- packages/cli/src/commands/doctor/diagnose.ts | 129 ++++--------------- 1 file changed, 23 insertions(+), 106 deletions(-) diff --git a/packages/cli/src/commands/doctor/diagnose.ts b/packages/cli/src/commands/doctor/diagnose.ts index 061d38550a..6d6e0ec72d 100644 --- a/packages/cli/src/commands/doctor/diagnose.ts +++ b/packages/cli/src/commands/doctor/diagnose.ts @@ -1,125 +1,42 @@ import color from '@heroku-cli/color' import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' -import {ux} from '@oclif/core' +import {Args, ux} from '@oclif/core' import * as lodash from 'lodash' const clipboard = require('copy-paste') const {exec} = require('child_process') const {promisify} = require('util') const execAsync = promisify(exec) -const getLocalNodeVersion = async () => { - const {stdout} = await execAsync('node -v') - return stdout -} - -const getInstallMethod = () => { - return 'brew' -} - -const getInstallLocation = async () => { - const {stdout} = await execAsync('which heroku') - const formattedOutput = stdout.replace(/\n/g, '') - return formattedOutput -} - -const getLocalProxySettings = async (unmasked = false) => { - const command = `httpsProxy=$(scutil --proxy | awk -F': ' '/HTTPSProxy/ {print $2}') - - # Check if HTTPSProxy has a value - if [ -n "$httpsProxy" ]; then - echo "$httpsProxy" - else - echo "no proxy set" - fi` - - const {stdout} = await execAsync(command) - const hasProxySet = !stdout.includes('no proxy set') +export default class DoctorDiagnose extends Command { + static description = 'check the heroku cli build for errors' + static topic = 'doctor' - if (unmasked) { - return stdout + static args = { + command: Args.string({description: 'command to check for errors', required: false}), } - return hasProxySet ? 'xxxxx\n' : stdout -} - -const getInstalledPLugins = async () => { - const {stdout} = await execAsync('heroku plugins') - return stdout -} - -const getHerokuStatus = async () => { - const {stdout} = await execAsync('heroku status') - return stdout -} - -const copyToClipboard = async (value: any) => { - clipboard.copy(value) -} - -export default class DoctorVitals extends Command { - static description = 'list local user setup for debugging' - static topic = 'doctor' - static flags = { - unmask: flags.boolean({description: 'unmasks fields heroku has deemed potentially sensitive', required: false}), - 'copy-results': flags.boolean({description: 'copies results to clipboard', required: false}), + all: flags.boolean({description: 'check all commands for errors', required: false, char: 'A'}), + build: flags.boolean({description: 'check entire heroku cli build for errors', required: false, char: 'b'}), + 'copy-results': flags.boolean({description: 'copies results to clipboard', required: false, char: 'p'}), json: flags.boolean({description: 'display as json', required: false}), } async run() { - const {flags} = await this.parse(DoctorVitals) - const copyResults = flags['copy-results'] - const time = new Date() - const dateChecked = time.toISOString().split('T')[0] - const cliInstallMethod = getInstallMethod() - const cliInstallLocation = await getInstallLocation() - const os = this.config.platform - const cliVersion = `v${this.config.version}` - const nodeVersion = await getLocalNodeVersion() - const networkConfig = { - httpsProxy: await getLocalProxySettings(flags.unmask), - } - const installedPlugins = await getInstalledPLugins() - const herokuStatus = await getHerokuStatus() - - const isHerokuUp = true - let copiedResults = '' - - ux.styledHeader(`${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}`) - ux.log(`${color.cyan('CLI Install Method:')} ${cliInstallMethod}`) - ux.log(`${color.cyan('CLI Install Location:')} ${cliInstallLocation}`) - ux.log(`${color.cyan('OS:')} ${os}`) - ux.log(`${color.cyan('Heroku CLI Version:')} ${cliVersion}`) - ux.log(`${color.cyan('Node Version:')} ${nodeVersion}`) - - ux.log(`${color.cyan('Network Config')}`) - ux.log(`HTTPSProxy: ${networkConfig.httpsProxy}`) - - ux.log(`${color.cyan('Installed Plugins')}`) - ux.log(`${installedPlugins}`) - - ux.log(`${color.bold(color.heroku('Heroku Status'))}`) - ux.log(`${color.bold(color.heroku('----------------------------------------'))}`) - ux.log(isHerokuUp ? color.green(herokuStatus) : color.red(herokuStatus)) - - if (copyResults) { - // copy results to clipboard here - copiedResults += `Heroku CLI Doctor · User Local Setup on ${dateChecked}\n` - copiedResults += `CLI Install Method: ${cliInstallMethod}\n` - copiedResults += `CLI Install Location: ${cliInstallLocation}\n` - copiedResults += `OS: ${os}\n` - copiedResults += `Heroku CLI Version: ${cliVersion}\n` - copiedResults += `Node Version: ${nodeVersion}\n` - copiedResults += 'Network Config\n' - copiedResults += `HTTPSProxy: ${networkConfig.httpsProxy}\n` - copiedResults += 'Installed Plugins\n' - copiedResults += `${installedPlugins}\n` - copiedResults += 'Heroku Status\n' - copiedResults += '----------------------------------------\n' - copiedResults += herokuStatus - } - - await copyToClipboard(copiedResults) + const {args, flags} = await this.parse(DoctorDiagnose) + const errorMessage = 'H23' + const stackMessage = 'some/crazy/looking/stack/message' + + ux.action.start(`${color.heroku(`Running diagnostics on ${color.cyan(`${args.command}`)}`)}`) + ux.action.stop() + ux.action.start(`${color.heroku(`Writing up report on ${color.cyan(`${args.command}`)}`)}`) + ux.action.stop() + + ux.log('\n') + ux.log(`${color.bold(`${color.heroku('Report')}`)}`) + ux.log(`${color.bold(`${color.heroku('-------------------------------------------')}`)}`) + ux.log(`${color.cyan('Error:')} ${errorMessage}`) + ux.log(`${color.cyan('Stack:')} ${stackMessage}`) } } From f7219f9c125fa6387704fc309cdd6573366eae29 Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Fri, 1 Dec 2023 00:09:39 -0800 Subject: [PATCH 25/26] WIP add logic for first pass on recommend command --- packages/cli/src/commands/doctor/recommend.ts | 131 ++++-------------- 1 file changed, 24 insertions(+), 107 deletions(-) diff --git a/packages/cli/src/commands/doctor/recommend.ts b/packages/cli/src/commands/doctor/recommend.ts index 061d38550a..f63c3c0c8b 100644 --- a/packages/cli/src/commands/doctor/recommend.ts +++ b/packages/cli/src/commands/doctor/recommend.ts @@ -1,125 +1,42 @@ import color from '@heroku-cli/color' import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' -import {ux} from '@oclif/core' +import {Args, ux} from '@oclif/core' import * as lodash from 'lodash' const clipboard = require('copy-paste') const {exec} = require('child_process') const {promisify} = require('util') const execAsync = promisify(exec) -const getLocalNodeVersion = async () => { - const {stdout} = await execAsync('node -v') - return stdout -} - -const getInstallMethod = () => { - return 'brew' -} - -const getInstallLocation = async () => { - const {stdout} = await execAsync('which heroku') - const formattedOutput = stdout.replace(/\n/g, '') - return formattedOutput -} - -const getLocalProxySettings = async (unmasked = false) => { - const command = `httpsProxy=$(scutil --proxy | awk -F': ' '/HTTPSProxy/ {print $2}') - - # Check if HTTPSProxy has a value - if [ -n "$httpsProxy" ]; then - echo "$httpsProxy" - else - echo "no proxy set" - fi` - - const {stdout} = await execAsync(command) - const hasProxySet = !stdout.includes('no proxy set') - - if (unmasked) { - return stdout - } - - return hasProxySet ? 'xxxxx\n' : stdout -} - -const getInstalledPLugins = async () => { - const {stdout} = await execAsync('heroku plugins') - return stdout -} - -const getHerokuStatus = async () => { - const {stdout} = await execAsync('heroku status') - return stdout -} - -const copyToClipboard = async (value: any) => { - clipboard.copy(value) -} - -export default class DoctorVitals extends Command { - static description = 'list local user setup for debugging' +export default class DoctorRecommend extends Command { + static description = 'recieve the latest tips, general playbooks, and resources when encountering cli issues' static topic = 'doctor' + static examples = [ + '$ heroku doctor:recommend ', + '$ heroku doctor:recommend --type command "Command will not show output"', + '$ heroku doctor:recommend --type install "Cli is erroring during install"', + '$ heroku doctor:recommend --type error "I get an error when running..."', + '$ heroku doctor:recommend --type network "I can not push my latest release"', + '$ heroku doctor:recommend --type permissions "I cannot get access to..."', + ] + + static args = { + statement: Args.string({description: 'statement of problem user is enountering', required: true}), + } static flags = { - unmask: flags.boolean({description: 'unmasks fields heroku has deemed potentially sensitive', required: false}), + type: flags.string({description: 'type of help required', required: false}), 'copy-results': flags.boolean({description: 'copies results to clipboard', required: false}), - json: flags.boolean({description: 'display as json', required: false}), } async run() { - const {flags} = await this.parse(DoctorVitals) - const copyResults = flags['copy-results'] - const time = new Date() - const dateChecked = time.toISOString().split('T')[0] - const cliInstallMethod = getInstallMethod() - const cliInstallLocation = await getInstallLocation() - const os = this.config.platform - const cliVersion = `v${this.config.version}` - const nodeVersion = await getLocalNodeVersion() - const networkConfig = { - httpsProxy: await getLocalProxySettings(flags.unmask), - } - const installedPlugins = await getInstalledPLugins() - const herokuStatus = await getHerokuStatus() - - const isHerokuUp = true - let copiedResults = '' - - ux.styledHeader(`${color.heroku('Heroku CLI Doctor')} · ${color.cyan(`User Local Setup on ${dateChecked}`)}`) - ux.log(`${color.cyan('CLI Install Method:')} ${cliInstallMethod}`) - ux.log(`${color.cyan('CLI Install Location:')} ${cliInstallLocation}`) - ux.log(`${color.cyan('OS:')} ${os}`) - ux.log(`${color.cyan('Heroku CLI Version:')} ${cliVersion}`) - ux.log(`${color.cyan('Node Version:')} ${nodeVersion}`) - - ux.log(`${color.cyan('Network Config')}`) - ux.log(`HTTPSProxy: ${networkConfig.httpsProxy}`) - - ux.log(`${color.cyan('Installed Plugins')}`) - ux.log(`${installedPlugins}`) - - ux.log(`${color.bold(color.heroku('Heroku Status'))}`) - ux.log(`${color.bold(color.heroku('----------------------------------------'))}`) - ux.log(isHerokuUp ? color.green(herokuStatus) : color.red(herokuStatus)) - - if (copyResults) { - // copy results to clipboard here - copiedResults += `Heroku CLI Doctor · User Local Setup on ${dateChecked}\n` - copiedResults += `CLI Install Method: ${cliInstallMethod}\n` - copiedResults += `CLI Install Location: ${cliInstallLocation}\n` - copiedResults += `OS: ${os}\n` - copiedResults += `Heroku CLI Version: ${cliVersion}\n` - copiedResults += `Node Version: ${nodeVersion}\n` - copiedResults += 'Network Config\n' - copiedResults += `HTTPSProxy: ${networkConfig.httpsProxy}\n` - copiedResults += 'Installed Plugins\n' - copiedResults += `${installedPlugins}\n` - copiedResults += 'Heroku Status\n' - copiedResults += '----------------------------------------\n' - copiedResults += herokuStatus - } - - await copyToClipboard(copiedResults) + const {flags} = await this.parse(DoctorRecommend) + ux.log(`${color.bold(`${color.heroku('Recommendations')}`)}`) + ux.log(`${color.bold(`${color.heroku('-------------------------------------------')}`)}`) + ux.log(`- Visit ${color.cyan('"https://devcenter.heroku.com/articles/heroku-cli"')} for more install information`) + ux.log('- Try reinstalling the heroku cli') + ux.log(`- Try running ${color.cyan('"$ heroku doctor:diagnose"')}`) + ux.log(`- Try running ${color.cyan('"$ heroku doctor:ask"')}`) + ux.log('- Check which version of the heroku cli your are running') } } From 7519cc6143e27aa78a6539091904df75a22577ca Mon Sep 17 00:00:00 2001 From: Zane Whitfield Date: Fri, 1 Dec 2023 07:59:17 -0800 Subject: [PATCH 26/26] Update copy-results output --- packages/cli/src/commands/doctor/vitals.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/cli/src/commands/doctor/vitals.ts b/packages/cli/src/commands/doctor/vitals.ts index 061d38550a..0bfceaf426 100644 --- a/packages/cli/src/commands/doctor/vitals.ts +++ b/packages/cli/src/commands/doctor/vitals.ts @@ -118,6 +118,8 @@ export default class DoctorVitals extends Command { copiedResults += 'Heroku Status\n' copiedResults += '----------------------------------------\n' copiedResults += herokuStatus + + ux.log(`\n${color.bold(`${color.heroku('Results copied to clipboard!')}`)}`) } await copyToClipboard(copiedResults)