Skip to content

Commit 0f5da7b

Browse files
migrates pg:push
1 parent 1027223 commit 0f5da7b

3 files changed

Lines changed: 81 additions & 112 deletions

File tree

packages/cli/src/commands/pg/pull.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {Command, flags} from '@heroku-cli/command'
33
import {Args, ux} from '@oclif/core'
44
import tsheredoc from 'tsheredoc'
55
import {utils, pg} from '@heroku/heroku-cli-util'
6-
// import {SpawnOptions, spawn} from 'node:child_process'
76
import childProcess from 'node:child_process'
87
import {nls} from '../../nls.js'
98
import {
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
1-
/*
2-
import color from '@heroku-cli/color'
1+
import {color} from '@heroku-cli/color'
32
import {Command, flags} from '@heroku-cli/command'
43
import {Args, ux} from '@oclif/core'
5-
import heredoc from 'tsheredoc'
6-
import {utils, ConnectionDetails} from '@heroku/heroku-cli-util'
4+
import tsheredoc from 'tsheredoc'
5+
import {utils, pg} from '@heroku/heroku-cli-util'
76
import {
87
connArgs,
98
maybeTunnel,
109
parseExclusions,
1110
prepare,
1211
spawnPipe,
1312
verifyExtensionsMatch,
14-
} from '../../lib/pg/push_pull'
15-
import {SpawnOptions, spawn} from 'node:child_process'
16-
import {nls} from '../../nls'
17-
const env = process.env
13+
} from '../../lib/pg/push_pull.js'
14+
import childProcess from 'node:child_process'
15+
import {nls} from '../../nls.js'
16+
17+
const heredoc = tsheredoc.default
18+
const {env} = process
1819

1920
export default class Push extends Command {
20-
static topic = 'pg'
21+
static args = {
22+
source: Args.string({description: 'PostgreSQL connection string for the source database', required: true}),
23+
target: Args.string({description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`, required: true}),
24+
}
25+
2126
static description = heredoc`
2227
push local or remote into Heroku database
2328
Push from SOURCE into TARGET. TARGET must be empty.
@@ -37,67 +42,48 @@ export default class Push extends Command {
3742
`]
3843

3944
static flags = {
40-
'exclude-table-data': flags.string({description: 'tables for which data should be excluded (use \';\' to split multiple names)', hasValue: true}),
4145
app: flags.app({required: true}),
46+
'exclude-table-data': flags.string({description: 'tables for which data should be excluded (use \';\' to split multiple names)', hasValue: true}),
4247
remote: flags.remote(),
4348
}
4449

45-
static args = {
46-
source: Args.string({required: true, description: 'PostgreSQL connection string for the source database'}),
47-
target: Args.string({required: true, description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}`}),
48-
}
49-
50-
public async run(): Promise<void> {
51-
const {flags, args} = await this.parse(Push)
52-
const {app, 'exclude-table-data': excludeTableData} = flags
53-
54-
const exclusions = parseExclusions(excludeTableData)
55-
const source = utils.pg.DatabaseResolver.parsePostgresConnectionString(args.source)
56-
const dbResolver = new utils.pg.DatabaseResolver(this.heroku)
57-
const target = await dbResolver.getDatabase(app, args.target)
58-
59-
ux.log(`Pushing ${color.cyan(args.source)} to ${color.addon(target.attachment!.addon.name)}`)
60-
await this.push(source, target, exclusions)
61-
ux.log('Pushing complete.')
62-
}
50+
static topic = 'pg'
6351

6452
protected async push(
65-
sourceIn: ConnectionDetails,
66-
targetIn: ConnectionDetails,
53+
sourceIn: pg.ConnectionDetails,
54+
targetIn: pg.ConnectionDetails,
6755
exclusions: string[]) {
6856
await prepare(targetIn)
6957

7058
const source = await maybeTunnel(sourceIn)
7159
const target = await maybeTunnel(targetIn)
72-
const exclude = exclusions.map(function (e) {
73-
return '--exclude-table-data=' + e
74-
}).join(' ')
60+
const exclude = exclusions.map(e => ('--exclude-table-data=' + e)).join(' ')
7561

7662
const dumpFlags = ['--verbose', '-F', 'c', '-Z', '0', '-N', '_heroku', ...connArgs(source, true)]
7763

7864
if (exclude !== '') dumpFlags.push(exclude)
7965

80-
const dumpOptions: SpawnOptions & {env: NodeJS.ProcessEnv} = {
66+
const dumpOptions: { env: NodeJS.ProcessEnv } & childProcess.SpawnOptions = {
8167
env: {
8268
PGSSLMODE: 'prefer',
8369
...env,
84-
} as {[key: string]: string},
85-
stdio: ['pipe', 'pipe', 2],
70+
} as { [key: string]: string },
8671
shell: true,
72+
stdio: ['pipe', 'pipe', 2],
8773
}
8874
if (source.password) dumpOptions.env.PGPASSWORD = source.password
8975

9076
const restoreFlags = ['--verbose', '-F', 'c', '--no-acl', '--no-owner', ...connArgs(target)]
9177

92-
const restoreOptions: SpawnOptions & {env: NodeJS.ProcessEnv} = {
78+
const restoreOptions: { env: NodeJS.ProcessEnv } & childProcess.SpawnOptions = {
9379
env: {...env},
94-
stdio: ['pipe', 'pipe', 2],
9580
shell: true,
81+
stdio: ['pipe', 'pipe', 2],
9682
}
9783
if (target.password) restoreOptions.env.PGPASSWORD = target.password
9884

99-
const pgDump = spawn('pg_dump', dumpFlags, dumpOptions)
100-
const pgRestore = spawn('pg_restore', restoreFlags, restoreOptions)
85+
const pgDump = childProcess.spawn('pg_dump', dumpFlags, dumpOptions)
86+
const pgRestore = childProcess.spawn('pg_restore', restoreFlags, restoreOptions)
10187

10288
await spawnPipe(pgDump, pgRestore)
10389

@@ -106,5 +92,18 @@ export default class Push extends Command {
10692

10793
await verifyExtensionsMatch(sourceIn, targetIn)
10894
}
95+
96+
public async run(): Promise<void> {
97+
const {args, flags} = await this.parse(Push)
98+
const {app, 'exclude-table-data': excludeTableData} = flags
99+
100+
const exclusions = parseExclusions(excludeTableData)
101+
const source = utils.pg.DatabaseResolver.parsePostgresConnectionString(args.source)
102+
const dbResolver = new utils.pg.DatabaseResolver(this.heroku)
103+
const target = await dbResolver.getDatabase(app, args.target)
104+
105+
ux.stdout(`Pushing ${color.cyan(args.source)} to ${color.addon(target.attachment!.addon.name)}`)
106+
await this.push(source, target, exclusions)
107+
ux.stdout('Pushing complete.')
108+
}
109109
}
110-
*/

packages/cli/test/unit/commands/pg/push.unit.test.ts

Lines changed: 41 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
/*
21
import {stderr, stdout} from 'stdout-stderr'
3-
import runCommand, {GenericCmd} from '../../../helpers/runCommand.js'
2+
import Cmd from '../../../../src/commands/pg/push.js'
3+
import runCommand from '../../../helpers/runCommand.js'
44
import {expect} from 'chai'
5-
import * as proxyquire from 'proxyquire'
6-
import heredoc from 'tsheredoc'
7-
import {ConnectionDetails, utils} from '@heroku/heroku-cli-util'
5+
import tsheredoc from 'tsheredoc'
6+
import {pg, utils} from '@heroku/heroku-cli-util'
87
import sinon = require('sinon')
9-
import * as childProcess from 'node:child_process'
8+
import childProcess from 'node:child_process'
9+
10+
const heredoc = tsheredoc.default
1011

1112
describe('pg:push', function () {
1213
const skipOnWindows = process.platform === 'win32' ? it.skip : it
13-
let db: ConnectionDetails
14-
let push_pull: unknown
14+
let db: pg.ConnectionDetails
1515
const emptyResponse = '00'
16-
let Cmd: GenericCmd
1716
let spawnStub: sinon.SinonStub
1817
let env: NodeJS.ProcessEnv
1918
let sshTunnelStub: sinon.SinonStub
@@ -26,50 +25,24 @@ describe('pg:push', function () {
2625
env = process.env
2726
process.env = {}
2827
db = {
29-
user: 'jeff',
30-
password: 'pass',
31-
database: 'mydb',
32-
port: '5432',
33-
host: 'herokai.com',
3428
attachment: {
3529
addon: {
3630
name: 'postgres-1',
3731
},
38-
config_vars: ['DATABASE_URL'],
3932
app: {name: 'myapp'},
33+
config_vars: ['DATABASE_URL'],
4034
},
41-
} as ConnectionDetails
42-
sshTunnelStub = sinon.stub().resolves()
43-
const mockUtils = {
44-
pg: {
45-
DatabaseResolver: class {
46-
getDatabase = sinon.stub().callsFake(() => db)
47-
static parsePostgresConnectionString(url: string) {
48-
return utils.pg.DatabaseResolver.parsePostgresConnectionString(url)
49-
}
50-
},
51-
PsqlService: class {
52-
execQuery = sinon.stub().resolves(emptyResponse)
53-
},
54-
psql: {
55-
getPsqlConfigs: sinon.stub().callsFake((db: ConnectionDetails) => {
56-
return utils.pg.psql.getPsqlConfigs(db)
57-
}),
58-
sshTunnel: sshTunnelStub,
59-
},
60-
},
61-
}
62-
push_pull = proxyquire('../../../../src/lib/pg/push_pull', {
63-
'@heroku/heroku-cli-util': {
64-
utils: mockUtils,
65-
},
66-
})
67-
Cmd = proxyquire('../../../../src/commands/pg/push', {
68-
'@heroku/heroku-cli-util': {
69-
utils: mockUtils,
70-
},
71-
'../../lib/pg/push_pull': push_pull,
72-
}).default
35+
database: 'mydb',
36+
host: 'herokai.com',
37+
password: 'pass',
38+
port: '5432',
39+
user: 'jeff',
40+
} as pg.ConnectionDetails
41+
42+
sinon.stub(utils.pg.DatabaseResolver.prototype, 'getDatabase').resolves(db)
43+
sinon.stub(utils.pg.PsqlService.prototype, 'execQuery').resolves(emptyResponse)
44+
sinon.stub(utils.pg.psql, 'sshTunnel').resolves()
45+
7346
sinon.stub(Math, 'random').callsFake(() => 0)
7447
spawnStub = sinon.stub(childProcess, 'spawn')
7548
})
@@ -84,16 +57,16 @@ describe('pg:push', function () {
8457
const restoreFlags = ['--verbose', '-F', 'c', '--no-acl', '--no-owner', '-U', 'jeff', '-h', 'herokai.com', '-p', '5432', '-d', 'mydb']
8558

8659
spawnStub.withArgs('pg_dump', dumpFlags, sinon.match.any).returns({
60+
on: exitHandler,
8761
stdout: {
88-
pipe: () => {},
62+
pipe() {},
8963
},
90-
on: exitHandler,
9164
})
9265
spawnStub.withArgs('pg_restore', restoreFlags, sinon.match.any).returns({
66+
on: exitHandler,
9367
stdin: {
94-
end: () => {},
68+
end() {},
9569
},
96-
on: exitHandler,
9770
})
9871

9972
await runCommand(Cmd, [
@@ -116,16 +89,16 @@ describe('pg:push', function () {
11689
const restoreFlags = ['--verbose', '-F', 'c', '--no-acl', '--no-owner', '-U', 'jeff', '-h', 'herokai.com', '-p', '5432', '-d', 'mydb']
11790

11891
spawnStub.withArgs('pg_dump', dumpFlags, sinon.match.any).returns({
92+
on: exitHandler,
11993
stdout: {
120-
pipe: () => {},
94+
pipe() {},
12195
},
122-
on: exitHandler,
12396
})
12497
spawnStub.withArgs('pg_restore', restoreFlags, sinon.match.any).returns({
98+
on: exitHandler,
12599
stdin: {
126-
end: () => {},
100+
end() {},
127101
},
128-
on: exitHandler,
129102
})
130103

131104
await runCommand(Cmd, [
@@ -150,16 +123,16 @@ describe('pg:push', function () {
150123
const restoreFlags = ['--verbose', '-F', 'c', '--no-acl', '--no-owner', '-U', 'jeff', '-h', 'herokai.com', '-p', '5432', '-d', 'mydb']
151124

152125
spawnStub.withArgs('pg_dump', dumpFlags, sinon.match.any).returns({
126+
on: exitHandler,
153127
stdout: {
154-
pipe: () => {},
128+
pipe() {},
155129
},
156-
on: exitHandler,
157130
})
158131
spawnStub.withArgs('pg_restore', restoreFlags, sinon.match.any).returns({
132+
on: exitHandler,
159133
stdin: {
160-
end: () => {},
134+
end() {},
161135
},
162-
on: exitHandler,
163136
})
164137

165138
await runCommand(Cmd, [
@@ -185,16 +158,16 @@ describe('pg:push', function () {
185158
const restoreFlags = ['--verbose', '-F', 'c', '--no-acl', '--no-owner', '-U', 'jeff', '-h', 'herokai.com', '-p', '5432', '-d', 'mydb']
186159

187160
spawnStub.withArgs('pg_dump', dumpFlags, sinon.match.any).returns({
161+
on: exitHandler,
188162
stdout: {
189-
pipe: () => {},
163+
pipe() {},
190164
},
191-
on: exitHandler,
192165
})
193166
spawnStub.withArgs('pg_restore', restoreFlags, sinon.match.any).returns({
167+
on: exitHandler,
194168
stdin: {
195-
end: () => {},
169+
end() {},
196170
},
197-
on: exitHandler,
198171
})
199172

200173
await runCommand(Cmd, [
@@ -217,16 +190,16 @@ describe('pg:push', function () {
217190
const restoreFlags = ['--verbose', '-F', 'c', '--no-acl', '--no-owner', '-U', 'jeff', '-h', 'herokai.com', '-p', '5432', '-d', 'mydb']
218191

219192
spawnStub.withArgs('pg_dump', dumpFlags, sinon.match.any).returns({
220-
stdout: {
221-
pipe: () => {},
193+
on(key: string, func: CallableFunction) {
194+
return func(1)
222195
},
223-
on: (key: string, func: CallableFunction) => {
224-
func(1)
196+
stdout: {
197+
pipe() {},
225198
},
226199
})
227200
spawnStub.withArgs('pg_restore', restoreFlags, sinon.match.any).returns({
228201
stdin: {
229-
end: () => {},
202+
end() {},
230203
},
231204
})
232205

@@ -247,5 +220,3 @@ describe('pg:push', function () {
247220
expect(stderr.output).to.eq('')
248221
})
249222
})
250-
251-
*/

0 commit comments

Comments
 (0)