UNPKG

3.52 kBJavaScriptView Raw
1const cli = require('heroku-cli-util')
2const Dyno = require('heroku-run').Dyno
3const co = require('co')
4const api = require('../../lib/heroku-api')
5const git = require('../../lib/git')
6const source = require('../../lib/source')
7const TestRun = require('../../lib/test-run')
8const Utils = require('../../lib/utils')
9
10// Default command. Run setup, source profile.d scripts and open a bash session
11const SETUP_COMMAND = 'ci setup && eval $(ci env)'
12
13function* run (context, heroku) {
14 const pipeline = yield Utils.getPipeline(context, heroku)
15
16 const pipelineRepository = yield api.pipelineRepository(heroku, pipeline.id)
17 const organization = pipelineRepository.organization &&
18 pipelineRepository.organization.name
19
20 const commit = yield git.readCommit('HEAD')
21 const sourceBlobUrl = yield cli.action('Preparing source', co(function* () {
22 return yield source.createSourceBlob(commit.ref, context, heroku)
23 }))
24
25 // Create test run and wait for it to transition to `debugging`
26 const testRun = yield cli.action('Creating test run', co(function* () {
27 const run = yield api.createTestRun(heroku, {
28 commit_branch: commit.branch,
29 commit_message: commit.message,
30 commit_sha: commit.ref,
31 debug: true,
32 clear_cache: !!context.flags['no-cache'],
33 organization,
34 pipeline: pipeline.id,
35 source_blob_url: sourceBlobUrl
36 })
37
38 return yield TestRun.waitForStates(['debugging', 'errored'], run, { heroku })
39 }))
40
41 if (testRun.status === 'errored') {
42 cli.exit(1, `Test run creation failed while ${testRun.error_state} with message "${testRun.message}"`)
43 }
44
45 const appSetup = yield api.appSetup(heroku, testRun.app_setup.id)
46 const noSetup = context.flags['no-setup']
47
48 cli.log(`${noSetup ? 'Attaching' : 'Running setup and attaching'} to test dyno...`)
49
50 if (noSetup) {
51 cli.warn('Skipping test setup phase.')
52 cli.warn(`Run \`${SETUP_COMMAND}\``)
53 cli.warn('to execute a build and configure the environment')
54 }
55
56 const testNodes = yield api.testNodes(heroku, testRun.id)
57
58 const dyno = new Dyno({
59 heroku,
60 app: appSetup.app.id,
61 showStatus: false
62 })
63
64 dyno.dyno = { attach_url: Utils.dig(testNodes, 0, 'dyno', 'attach_url') }
65
66 function sendSetup (data, connection) {
67 if (data.toString().includes('$')) {
68 dyno.write(SETUP_COMMAND + '\n')
69 dyno.removeListener('data', sendSetup)
70 }
71 }
72
73 if (!noSetup) {
74 dyno.on('data', sendSetup)
75 }
76
77 try {
78 yield dyno.attach()
79 } catch (err) {
80 if (err.exitCode) cli.exit(err.exitCode, err)
81 else throw err
82 }
83
84 yield cli.action(
85 'Cleaning up',
86 api.updateTestRun(heroku, testRun.id, {
87 status: 'cancelled',
88 message: 'debug run cancelled by Heroku CLI'
89 })
90 )
91}
92
93module.exports = {
94 topic: 'ci',
95 command: 'debug',
96 wantsApp: true,
97 needsAuth: true,
98 description: 'opens an interactive test debugging session with the contents of the current directory',
99 help: `Example:
100
101 $ heroku ci:debug
102 Preparing source... done
103 Creating test run... done
104 Running setup and attaching to test dyno...
105
106~ $
107`,
108 flags: [
109 {
110 name: 'no-setup',
111 hasValue: false,
112 description: 'start test dyno without running test-setup'
113 },
114 {
115 name: 'pipeline',
116 char: 'p',
117 hasValue: true,
118 description: 'pipeline'
119 },
120 {
121 name: 'no-cache',
122 hasValue: false,
123 description: 'start test run with an empty cache'
124 }
125 ],
126 run: cli.command(co.wrap(run))
127}