Skip to content

Commit 3297c5e

Browse files
authored
feat: adds 'data:pg:upgrade:run/wait' commands (W-21304392) (#3551)
* Add 'data:pg:upgrade:run' command * Add 'upgrading' status and 'wait' sub-command * Updating messaging * Addressing CX feedback
1 parent 380ef7b commit 3297c5e

8 files changed

Lines changed: 570 additions & 164 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import {flags as Flags} from '@heroku-cli/command'
2+
import {color, hux, utils} from '@heroku/heroku-cli-util'
3+
import {Args, ux} from '@oclif/core'
4+
import tsheredoc from 'tsheredoc'
5+
6+
import BaseCommand from '../../../../lib/data/baseCommand.js'
7+
import {InfoResponse, UpgradeResponse} from '../../../../lib/data/types.js'
8+
9+
const heredoc = tsheredoc.default
10+
11+
export default class DataPgUpgradeRun extends BaseCommand {
12+
static args = {
13+
database: Args.string({
14+
description: 'database name, database attachment name, or related config var on an app',
15+
required: true,
16+
}),
17+
}
18+
19+
static description = 'upgrade the Postgres version on a Postgres Advanced database'
20+
21+
static examples = [
22+
heredoc`
23+
# Upgrade a Postgres Advanced database to version 17
24+
${color.code('<%= config.bin %> <%= command.id %> DATABASE --version 17 --app my-app')}
25+
`,
26+
]
27+
28+
static flags = {
29+
app: Flags.app({required: true}),
30+
confirm: Flags.string({char: 'c', description: 'pass in the app name to skip confirmation prompts'}),
31+
remote: Flags.remote(),
32+
version: Flags.string({char: 'v', description: 'Postgres version to upgrade to'}),
33+
}
34+
35+
public async run(): Promise<void> {
36+
const {args, flags} = await this.parse(DataPgUpgradeRun)
37+
const {app, confirm, version} = flags
38+
const {database} = args
39+
40+
const dbResolver = new utils.pg.DatabaseResolver(this.heroku)
41+
const {addon} = await dbResolver.getAttachment(app, database)
42+
43+
if (!utils.pg.isAdvancedDatabase(addon)) {
44+
ux.error(
45+
'You can only use this command on Advanced-tier databases.\n'
46+
+ `Use ${color.code(`heroku pg:upgrade:run ${addon.name} --app ${app}`)} instead.`,
47+
)
48+
}
49+
50+
const {body: databaseInfo} = await this.dataApi.get<InfoResponse>(`/data/postgres/v1/${addon.id}/info`)
51+
const {version: currentVersion} = databaseInfo
52+
const newVersion = version ?? 'the latest supported Postgres version'
53+
await hux.confirmCommand({
54+
comparison: app,
55+
confirmation: confirm,
56+
warningMessage: heredoc(`
57+
This command immediately upgrades your ${color.datastore(addon.name)} database from ${currentVersion} to ${newVersion}.
58+
Your database will be unavailable until the upgrade is complete.`),
59+
})
60+
61+
try {
62+
ux.action.start(`Upgrading your ${color.datastore(addon.name)} database from ${currentVersion} to ${newVersion}`)
63+
await this.dataApi.post<UpgradeResponse>(
64+
`/data/postgres/v1/${addon.id}/upgrade/run`,
65+
{body: {version}},
66+
)
67+
ux.action.stop()
68+
ux.stderr(`Upgrade started. Use ${color.code(`heroku data:pg:upgrade:wait ${addon.name} -a ${app}`)} to monitor progress.`)
69+
} catch (error: unknown) {
70+
ux.action.stop(color.red('!'))
71+
throw error
72+
}
73+
}
74+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as color from '@heroku/heroku-cli-util/color'
2+
import tsheredoc from 'tsheredoc'
3+
4+
import DataPgWait from '../wait.js'
5+
6+
const heredoc = tsheredoc.default
7+
8+
export default class DataPgUpgradeWait extends DataPgWait {
9+
static description = 'shows status of an upgrade until it\'s complete'
10+
11+
static examples = [
12+
heredoc(`
13+
# Wait for upgrade to complete
14+
${color.code('<%= config.bin %> <%= command.id %> DATABASE --app myapp')}
15+
`),
16+
heredoc(`
17+
# Wait with custom polling interval (to avoid rate limiting)
18+
${color.code('<%= config.bin %> <%= command.id %> DATABASE --app myapp --wait-interval 10')}
19+
`),
20+
]
21+
22+
protected classicWaitCommand: string = 'pg:upgrade:wait'
23+
}

src/commands/data/pg/wait.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import {color, pg, utils} from '@heroku/heroku-cli-util'
2-
import {HTTPError} from '@heroku/http-call'
1+
import type {pg} from '@heroku/heroku-cli-util'
2+
33
import {flags as Flags} from '@heroku-cli/command'
4+
import * as color from '@heroku/heroku-cli-util/color'
5+
import {DatabaseResolver, isAdvancedDatabase} from '@heroku/heroku-cli-util/utils'
6+
import {HTTPError} from '@heroku/http-call'
47
import {Args, ux} from '@oclif/core'
58
import tsheredoc from 'tsheredoc'
69

@@ -23,7 +26,7 @@ export default class DataPgWait extends BaseCommand {
2326
static examples = [
2427
heredoc(`
2528
# Wait for database to be available
26-
${color.command('heroku data:pg:wait DATABASE --app myapp')}
29+
${color.code('<%= config.bin %> <%= command.id %> DATABASE --app myapp')}
2730
`),
2831
]
2932

@@ -40,6 +43,8 @@ export default class DataPgWait extends BaseCommand {
4043
}),
4144
}
4245

46+
protected classicWaitCommand: string = 'pg:wait'
47+
4348
public async notify(...args: Parameters<typeof notify>): Promise<void> {
4449
return notify(...args)
4550
}
@@ -48,14 +53,14 @@ export default class DataPgWait extends BaseCommand {
4853
const {args, flags} = await this.parse(DataPgWait)
4954
const {database} = args
5055
const {app, 'no-notify': noNotify, 'wait-interval': waitInterval} = flags
51-
const databaseResolver = new utils.pg.DatabaseResolver(this.heroku)
56+
const databaseResolver = new DatabaseResolver(this.heroku)
5257
const db = await databaseResolver.getAttachment(app, database)
5358
const {addon} = db
5459

55-
if (!utils.pg.isAdvancedDatabase(addon)) {
60+
if (!isAdvancedDatabase(addon)) {
5661
ux.error(heredoc`
5762
You can only use this command on Advanced-tier databases.
58-
Run ${color.code(`heroku pg:wait ${addon.name} -a ${app}`)} instead.`)
63+
Use ${color.code(`heroku ${this.classicWaitCommand} ${addon.name} -a ${app}`)} instead.`)
5964
}
6065

6166
await this.waitFor(addon, waitInterval || 5, noNotify)

0 commit comments

Comments
 (0)