Skip to content
257 changes: 145 additions & 112 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@heroku-cli/schema": "^1.0.25",
"@heroku/buildpack-registry": "^1.0.1",
"@heroku/eventsource": "^1.0.7",
"@heroku/heroku-cli-util": "10.4.0-beta.1",
"@heroku/heroku-cli-util": "10.4.0",
"@heroku/http-call": "^5.5.0",
"@heroku/mcp-server": "1.0.7-alpha.1",
"@heroku/plugin-ai": "^1.0.1",
Expand All @@ -38,6 +38,7 @@
"@sentry/opentelemetry": "^10.27.0",
"@types/js-yaml": "^3.12.5",
"ansi-escapes": "3.2.0",
"ansis": "^4",
"bytes": "^3.1.2",
"cli-progress": "^3.12.0",
"commander": "^2.15.1",
Expand Down Expand Up @@ -70,7 +71,6 @@
"ssh2": "^1.16.0",
"stdout-stderr": "^0.1.13",
"strftime": "^0.10.0",
"strip-ansi": "^6",
"term-img": "^4.1.0",
"tmp": "^0.2.5",
"true-myth": "4.1.1",
Expand Down Expand Up @@ -115,7 +115,6 @@
"@types/uuid": "^8.3.0",
"@types/write-json-file": "^3.2.1",
"@types/ws": "^6.0.1",
"ansis": "^4",
"bats": "^1.1.0",
"chai": "^4.4.1",
"chai-as-promised": "^7.1.1",
Expand All @@ -131,7 +130,6 @@
"sinon": "^19.0.2",
"source-map-support": "^0.5.21",
"std-mocks": "^2.0.0",
"strip-ansi": "6.0.1",
"ts-node": "^10.9.2",
"typescript": "4.8.4"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/access/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function buildTableColumns(showPermissions: boolean) {
get: ({email}: any): string => color.user(email),
},
role: {
get: ({role}: any) => color.green(role),
get: ({role}: any) => color.info(role),
},
}

Expand Down
8 changes: 4 additions & 4 deletions packages/cli/src/commands/apps/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ async function createApp(context: Interfaces.ParserOutput, heroku: APIClient, na

let status = name ? 'done' : `done, ${color.app(app.name || '')}`
if (flags.region) {
status += `, region is ${color.yellow(app.region?.name || '')}`
status += `, region is ${color.info(app.region?.name || '')}`
}

if (stack) {
status += `, stack is ${color.yellow(app.stack?.name || '')}`
status += `, stack is ${color.info(app.stack?.name || '')}`
}

ux.action.stop(status)
Expand Down Expand Up @@ -99,7 +99,7 @@ function printAppSummary(context: Interfaces.ParserOutput, app: Heroku.App, remo
if (context.flags.json) {
hux.styledJSON(app)
} else {
ux.stdout(`${color.cyan(app.web_url || '')} | ${color.green(remoteUrl)}`)
ux.stdout(`${color.info(app.web_url || '')} | ${color.info(remoteUrl)}`)
}
}

Expand All @@ -112,7 +112,7 @@ async function runFromFlags(context: Interfaces.ParserOutput, heroku: APIClient,
const name = flags.app || args.app || process.env.HEROKU_APP

async function addBuildpack(app: Heroku.App, buildpack: string) {
ux.action.start(`Setting buildpack to ${color.cyan(buildpack)}`)
ux.action.start(`Setting buildpack to ${color.info(buildpack)}`)
await heroku.put(`/apps/${app.name}/buildpack-installations`, {
body: {updates: [{buildpack}]},
headers: {Range: ''},
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/apps/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const colorize = (level: string, s: string) => {
}

case 'info': {
return color.cyan(s)
return color.info(s)
}

default: {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/apps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function annotateAppName(app: App) {
function regionizeAppName(app: App) {
const name = annotateAppName(app)
if (app.region && app.region.name !== 'us') {
return `${name} (${color.green(app.region.name)})`
return `${name} (${color.info(app.region.name)})`
}

return name
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/apps/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function print(info: Heroku.App, addons: Heroku.AddOn[], collaborators: Heroku.C

data['Auto Cert Mgmt'] = info.app.acm
data['Git URL'] = info.app.git_url
data['Web URL'] = info.app.web_url
data['Web URL'] = color.info(info.app.web_url)
data['Repo Size'] = filesize(info.app.repo_size, {round: 0, standard: 'jedec'})
if (getGeneration(info.app) !== 'fir') data['Slug Size'] = filesize(info.app.slug_size, {round: 0, standard: 'jedec'})
data.Owner = color.user(info.app.owner.email)
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/apps/rename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default class AppsRename extends Command {
ux.action.stop()

const gitUrl = git.gitUrl(app.name)
ux.stdout(`${app.web_url} | ${gitUrl}`)
ux.stdout(`${color.info(app.web_url!)} | ${color.info(gitUrl)}`)

if (!app.web_url!.includes('https')) {
ux.stdout('Please note that it may take a few minutes for Heroku to provision a SSL certificate for your application.')
Expand All @@ -60,7 +60,7 @@ export default class AppsRename extends Command {
const {name} = remote
await git.rmRemote(name)
await git.createRemote(name, url.replace(oldApp, newApp))
ux.stdout(`Git remote ${name} updated`)
ux.stdout(`Git remote ${color.name(name)} updated`)
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/authorizations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default class AuthorizationsIndex extends Command {
ux.stdout('No OAuth authorizations.')
} else {
hux.table(authorizations, {
Description: {get: (v: any) => color.green(v.description)},
Description: {get: (v: any) => color.name(v.description)},
ID: {get: (v: any) => v.id},
Scope: {get: (v: any) => v.scope.join(',')},
}, {sort: {Description: 'asc'}})
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/clients/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export default class ClientsCreate extends Command {
if (flags.json) {
hux.styledJSON(client)
} else {
ux.stdout(`HEROKU_OAUTH_ID=${client.id}`)
ux.stdout(`HEROKU_OAUTH_SECRET=${client.secret}`)
ux.stdout(`${color.label('HEROKU_OAUTH_ID')}=${color.name(client.id!)}`)
ux.stdout(`${color.label('HEROKU_OAUTH_SECRET')}=${color.info(client.secret!)}`)
}
}
}
6 changes: 3 additions & 3 deletions packages/cli/src/commands/config/set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ RACK_ENV: staging`)]
argv.forEach((v: string) => {
const idx = v.indexOf('=')
if (idx === -1) {
ux.error(`${color.cyan(v)} is invalid. Must be in the format ${color.cyan('FOO=bar')}.`, {exit: 1})
ux.error(`${color.name(v)} is invalid. Must be in the format ${color.code('FOO=bar')}.`, {exit: 1})
}

vars[v.slice(0, idx)] = v.slice(idx + 1)
})

const varsCopy = argv.map((v: string) => color.green(v.split('=')[0])).join(', ')
const varsCopy = argv.map((v: string) => color.name(v.split('=')[0])).join(', ')
ux.action.start(`Setting ${varsCopy} and restarting ${color.app(flags.app)}`)

let {body: config} = await this.heroku.patch<Heroku.ConfigVars>(`/apps/${flags.app}/config-vars`, {
Expand All @@ -65,7 +65,7 @@ RACK_ENV: staging`)]
config = Object.fromEntries(
Object.entries(config)
.filter(([k]) => vars[k])
.map(([k, v]) => [color.green(k), v]),
.map(([k, v]) => [color.name(k), v]),
)
hux.styledObject(config)
await this.config.runHook('recache', {app: flags.app, type: 'config'})
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/domains/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ www.example.com CNAME www.example.herokudns.com`]
hux.styledJSON(domains)
} else {
hux.styledHeader(`${color.app(flags.app)} Heroku Domain`)
ux.stdout(herokuDomain && herokuDomain.hostname)
ux.stdout(herokuDomain && color.info(herokuDomain.hostname!))
if (customDomains && customDomains.length > 0) {
ux.stdout()

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/drains/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default class Drains extends Command {
if (drainsWithoutAddons.length > 0) {
hux.styledHeader('Drains')
drainsWithoutAddons.forEach((drain: Heroku.LogDrain) => {
styledDrain(drain.url || '', color.green(drain.token || ''), drain)
styledDrain(drain.url || '', color.name(drain.token || ''), drain)
})
}

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/keys/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {ux} from '@oclif/core'

function formatKey(key: string) {
const [name, pub, email] = key.trim().split(/\s/)
return `${name} ${pub.slice(0, 10)}...${pub.slice(-10)} ${color.green(email)}`
return `${name} ${pub.slice(0, 10)}...${pub.slice(-10)} ${color.user(email)}`
}

export default class Keys extends Command {
Expand Down
9 changes: 6 additions & 3 deletions packages/cli/src/commands/members/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@ const buildTableColumns = (teamInvites: MemberWithStatus[]) => {
get: ({email}: any): string => color.user(email),
},
role: {
get: ({role}: any): string => color.green(role),
get: ({role}: any): string => color.info(role),
},
}

if (teamInvites.length > 0) {
return {
...baseColumns,
status: {
get: ({status}: any): string => color.green(status),
get({status}: any): string {
if (status === 'pending') return color.warning(status)
return color.success(status)
},
},
}
}
Expand Down Expand Up @@ -84,7 +87,7 @@ export default class MembersIndex extends Command {
} else if (members.length === 0) {
let msg = `No members in ${color.team(team || '')}`
if (role)
msg += ` with role ${color.green(role)}`
msg += ` with role ${color.info(role)}`
ux.stdout(msg)
} else {
const tableColumns = buildTableColumns(teamInvites)
Expand Down
10 changes: 5 additions & 5 deletions packages/cli/src/commands/pg/backups/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default class Index extends Command {
const pgbackups = backupsFactory(app, this.heroku)
hux.styledHeader('Backups')
if (backups.length === 0) {
ux.stdout(`No backups. Capture one with ${color.cyan.bold('heroku pg:backups:capture')}`)
ux.stdout(`No backups. Capture one with ${color.code('heroku pg:backups:capture')}`)
} else {
/* eslint-disable perfectionist/sort-objects */
hux.table<BackupTransfer>(backups, {
Expand Down Expand Up @@ -70,7 +70,7 @@ export default class Index extends Command {
const copies = transfers.filter(t => t.from_type === 'pg_dump' && t.to_type === 'pg_restore').slice(0, 10)
hux.styledHeader('Copies')
if (copies.length === 0) {
ux.stdout(`No copies found. Use ${color.cyan.bold('heroku pg:copy')} to copy a database to another`)
ux.stdout(`No copies found. Use ${color.code('heroku pg:copy')} to copy a database to another`)
} else {
/* eslint-disable perfectionist/sort-objects */
hux.table(copies, {
Expand Down Expand Up @@ -106,11 +106,11 @@ export default class Index extends Command {
const pgbackups = backupsFactory(app, this.heroku)
hux.styledHeader('Restores')
if (restores.length === 0) {
ux.stdout(`No restores found. Use ${color.cyan.bold('heroku pg:backups:restore')} to restore a backup`)
ux.stdout(`No restores found. Use ${color.code('heroku pg:backups:restore')} to restore a backup`)
} else {
hux.table(restores, {
ID: {
get: (transfer: BackupTransfer) => color.cyan(pgbackups.name(transfer)),
get: (transfer: BackupTransfer) => color.name(pgbackups.name(transfer)),
},
'Started at': {
get: (transfer: BackupTransfer) => transfer.created_at,
Expand All @@ -122,7 +122,7 @@ export default class Index extends Command {
get: (transfer: BackupTransfer) => pgbackups.filesize(transfer.processed_bytes),
},
Database: {
get: (transfer: BackupTransfer) => color.green(transfer.to_name) || 'UNKNOWN',
get: (transfer: BackupTransfer) => color.datastore(transfer.to_name) || 'UNKNOWN',
},
})
}
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/pg/backups/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default class Info extends Command {

displayBackup = (backup: BackupTransfer, app: string) => {
const pgbackups = pgBackupsApi(app, this.heroku)
hux.styledHeader(`Backup ${color.cyan(pgbackups.name(backup))}`)
hux.styledHeader(`Backup ${color.name(pgbackups.name(backup))}`)
/* eslint-disable perfectionist/sort-objects */
hux.styledObject({
Database: color.datastore(backup.from_name),
Expand Down Expand Up @@ -82,7 +82,7 @@ export default class Info extends Command {
const backups = transfers.filter(t => t.from_type === 'pg_dump' && t.to_type === 'gof3r')
const lastBackup = backups.pop()
if (!lastBackup)
throw new Error(`No backups. Capture one with ${color.cyan.bold('heroku pg:backups:capture')}`)
throw new Error(`No backups. Capture one with ${color.code('heroku pg:backups:capture')}`)
backupID = lastBackup.num
}

Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/pg/backups/schedules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ export default class Schedules extends Command {
const db = await dbResolver.getArbitraryLegacyDB(app)
const {body: schedules} = await this.heroku.get<TransferSchedule[]>(`/client/v11/databases/${db.id}/transfer-schedules`, {hostname: utils.pg.host()})
if (schedules.length === 0) {
ux.warn(`No backup schedules found on ${color.app(app)}\nUse ${color.cyan.bold('heroku pg:backups:schedule')} to set one up`)
ux.warn(`No backup schedules found on ${color.app(app)}\nUse ${color.code('heroku pg:backups:schedule')} to set one up`)
} else {
hux.styledHeader('Backup Schedules')
for (const s of schedules) {
ux.stdout(`${color.green(s.name)}: daily at ${s.hour}:00 ${s.timezone}\n`)
ux.stdout(`${color.name(s.name)}: daily at ${s.hour}:00 ${s.timezone}\n`)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/pg/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function displayDB(db: DBObject, app: string) {
hux.styledHeader(db.addon.attachment_names.map((c: string) => color.attachment(c + '_URL'))
.join(', '))
} else {
hux.styledHeader(db.configVars?.map(c => color.green(c))
hux.styledHeader(db.configVars?.map(c => color.name(c))
.join(', ') || '')
}

Expand Down
16 changes: 8 additions & 8 deletions packages/cli/src/commands/ps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,15 @@ async function printAccountQuota(heroku: APIClient, app: AppProcessTier, account
ux.stdout(`Eco dyno hours quota remaining this month: ${hours}h ${minutes}m (${percentage}%)`)
ux.stdout(`Eco dyno usage for this app: ${appHours}h ${appMinutes}m (${appPercentage}%)`)
ux.stdout('For more information on Eco dyno hours, see:')
ux.stdout('https://devcenter.heroku.com/articles/eco-dyno-hours')
ux.stdout(color.info('https://devcenter.heroku.com/articles/eco-dyno-hours'))
ux.stdout()
}

if (app.process_tier === 'free') {
ux.stdout(`Free dyno hours quota remaining this month: ${hours}h ${minutes}m (${percentage}%)`)
ux.stdout(`Free dyno usage for this app: ${appHours}h ${appMinutes}m (${appPercentage}%)`)
ux.stdout('For more information on dyno sleeping and how to upgrade, see:')
ux.stdout('https://devcenter.heroku.com/articles/dyno-sleeping')
ux.stdout(color.info('https://devcenter.heroku.com/articles/dyno-sleeping'))
ux.stdout()
}
}
Expand All @@ -137,14 +137,14 @@ function decorateOneOffDyno(dyno: DynoExtended) : string {
const since = ago(new Date(dyno.updated_at))
// eslint-disable-next-line unicorn/explicit-length-check
const size = dyno.size || '1X'
const state = dyno.state === 'up' ? color.green(dyno.state) : color.yellow(dyno.state)
const state = dyno.state === 'up' ? color.success(dyno.state) : color.warning(dyno.state)

return `${dyno.name} (${color.cyan(size)}): ${state} ${color.dim(since)}: ${dyno.command}`
return `${dyno.name} (${color.info(size)}): ${state} ${color.dim(since)}: ${dyno.command}`
}

function decorateCommandDyno(dyno: DynoExtended) : string {
const since = ago(new Date(dyno.updated_at))
const state = dyno.state === 'up' ? color.green(dyno.state) : color.yellow(dyno.state)
const state = dyno.state === 'up' ? color.success(dyno.state) : color.warning(dyno.state)

return `${dyno.name}: ${state} ${color.dim(since)}`
}
Expand All @@ -158,7 +158,7 @@ function printDynos(dynos: DynoExtended[]) : void {

// Print one-off dynos
if (oneOffs.length > 0) {
hux.styledHeader(`${color.green('run')}: one-off processes (${color.yellow(oneOffs.length.toString())})`)
hux.styledHeader(`${color.label('run')}: one-off processes (${oneOffs.length})`)
oneOffs.forEach(dyno => ux.stdout(decorateOneOffDyno(dyno)))
ux.stdout()
}
Expand All @@ -168,7 +168,7 @@ function printDynos(dynos: DynoExtended[]) : void {
const commandDynos = dynos.filter(d => d.command === command).sort(byProcessNumber)
const {size = '1X', type} = commandDynos[0]

hux.styledHeader(`${color.green(type)} (${color.cyan(size)}): ${command} (${color.yellow(commandDynos.length.toString())})`)
hux.styledHeader(`${color.label(type)} (${color.info(size)}): ${command} (${commandDynos.length})`)
for (const dyno of commandDynos)
ux.stdout(decorateCommandDyno(dyno))
ux.stdout()
Expand Down Expand Up @@ -233,7 +233,7 @@ export default class Index extends Command {
selectedDynos = selectedDynos.filter(dyno => types.find((t: string) => dyno.type === t))
types.forEach(t => {
if (!selectedDynos.some(d => d.type === t)) {
throw new Error(`No ${color.cyan(t)} dynos on ${color.app(app)}`)
throw new Error(`No ${color.info(t)} dynos on ${color.app(app)}`)
}
})
}
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/releases/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import ansis from 'ansis'
import {color, hux} from '@heroku/heroku-cli-util'
import {Command, flags} from '@heroku-cli/command'
import * as Heroku from '@heroku-cli/schema'
import {ux} from '@oclif/core'
import _ from 'lodash'
import stripAnsi from 'strip-ansi'

import * as statusHelper from '../../lib/releases/status_helper.js'
import * as time from '../../lib/time.js'
Expand Down Expand Up @@ -52,7 +52,7 @@ const getDescriptionTruncation = function (releases: Heroku.Release[], columns:
if (key !== optimizeKey) {
optimizationWidthMap[key] = Math.max(
optimizationWidthMap[key],
stripAnsi(String(formattedValue)).length,
ansis.strip(String(formattedValue)).length,
)
}
}
Expand Down
Loading
Loading