Skip to content

Commit fd37841

Browse files
committed
fix(nx-heroku): improve logging and buildpacks setup
1 parent 157d68c commit fd37841

File tree

4 files changed

+43
-32
lines changed

4 files changed

+43
-32
lines changed

packages/nx-heroku/src/executors/common/logger.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { logger } from '@nrwl/devkit';
2-
import type { ExecException } from 'child_process';
32
import Container, { Constructable } from 'typedi';
43

4+
import { isExecException } from './utils';
5+
56
export interface LoggerInterface {
67
debug: boolean;
78
log(message: string): void;
@@ -10,10 +11,6 @@ export interface LoggerInterface {
1011
error(message: string | Error | unknown): void;
1112
}
1213

13-
function isExecException(error: unknown): error is ExecException {
14-
return (error as ExecException).code !== undefined;
15-
}
16-
1714
export class ConsoleLogger implements LoggerInterface {
1815
private _debug = false;
1916

packages/nx-heroku/src/executors/common/utils.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { exec as cbBasedExec } from 'child_process';
1+
import { exec as cbBasedExec, ExecException } from 'child_process';
22
import { expand } from 'dotenv-expand';
33
import { cloneDeep, isString, merge, pickBy } from 'lodash';
44
import { promisify } from 'util';
@@ -41,3 +41,7 @@ export function expandOptions<O extends object>(options: O): O {
4141
const { parsed } = expand({ parsed: pickBy(parsedNested, isString) });
4242
return merge(cloneDeep(options), parsedNested, parsed);
4343
}
44+
45+
export function isExecException(error: unknown): error is ExecException {
46+
return (error as ExecException).code !== undefined;
47+
}

packages/nx-heroku/src/executors/deploy/services/heroku-app.service.ts

+33-25
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,25 @@ import { getGitLocalBranchName, getGitRemoteBranch } from '../../common/git';
2121
import {
2222
addAddons,
2323
addAppToPipeline,
24+
addBuildPack,
2425
addDrain,
2526
addMember,
2627
addWebhook,
28+
clearBuildPacks,
2729
createApp,
2830
createAppRemote,
2931
createPipeline,
3032
getPipelineName,
33+
hasPlugin,
3134
HerokuError,
35+
installPlugin,
3236
mergeConfigVars,
3337
pipelineExists,
38+
serializeConfigVars,
3439
} from '../../common/heroku';
3540
import { HerokuBaseService } from '../../common/heroku/base.service';
3641
import { Logger, LoggerInterface } from '../../common/logger';
37-
import { exec, sleep } from '../../common/utils';
42+
import { exec, isExecException, sleep } from '../../common/utils';
3843
import { DeployExecutorSchema, ExtendedDeployExecutorSchema } from '../schema';
3944
import { DEPLOY_EXECUTOR_SCHEMA } from './tokens';
4045

@@ -113,7 +118,7 @@ class HerokuApp {
113118
} catch (error) {
114119
const ex = error as ExecException;
115120
// there is (probably) nothing to commit
116-
this.logger.warn(ex.message);
121+
this.logger.warn(ex.message.trim());
117122
this.logger.warn(ex.code?.toString());
118123
}
119124
}
@@ -151,25 +156,18 @@ class HerokuApp {
151156
}
152157
}
153158

154-
private async hasPlugin(plugin: string): Promise<boolean> {
155-
const { stdout: list } = await exec('heroku plugins', { encoding: 'utf8' });
156-
return list.includes(plugin);
157-
}
158-
159159
private async addBuildPacks(): Promise<void> {
160160
const { appName, buildPacks } = this.options;
161161
if (buildPacks?.length) {
162-
if (!this.hasPlugin('buildpack-registry')) {
163-
await exec('heroku plugins:install buildpack-registry');
162+
if (!(await hasPlugin('buildpack-registry'))) {
163+
await installPlugin('buildpack-registry');
164164
}
165-
await exec(`heroku buildpacks:clear --app ${appName}`);
166-
const promises = buildPacks.map((buildPack, i) => {
167-
const index = i + 1;
168-
return exec(
169-
`heroku buildpacks:add ${buildPack} --app ${appName} --index ${index}`
170-
);
171-
});
172-
await Promise.all(promises);
165+
this.logger.info(`Clearing and adding buildpacks...`);
166+
await clearBuildPacks({ appName });
167+
for (const [i, buildPack] of buildPacks.entries()) {
168+
await addBuildPack({ appName, buildPack, index: i + 1 });
169+
}
170+
this.logger.info(`Buildpacks ${buildPacks} added.`);
173171
}
174172
}
175173

@@ -192,15 +190,23 @@ class HerokuApp {
192190
org,
193191
remoteName,
194192
});
195-
this.logger.info(`Created app ${appName} on git remote ${remoteName}`);
193+
this.logger.info(`Created app ${appName} on git remote ${remoteName}.`);
196194
return true;
197195
}
198196
throw error;
199197
}
200-
this.logger.info(`Added git remote ${remoteName}`);
198+
this.logger.info(`Added git remote ${remoteName}.`);
201199
return false;
202200
}
203201

202+
private async mergeConfigVars(): Promise<void> {
203+
const updatedConfigVars = await mergeConfigVars(this.options);
204+
if (updatedConfigVars) {
205+
this.logger.info(
206+
`Merged config vars : ${serializeConfigVars(updatedConfigVars)}.`
207+
);
208+
}
209+
}
204210
private async addToPipeline(): Promise<void> {
205211
const {
206212
appName,
@@ -220,7 +226,7 @@ class HerokuApp {
220226
});
221227
} else {
222228
this.logger.warn(
223-
`Pipeline ${pipelineName} not found, it will be created at the next step`
229+
`Pipeline ${pipelineName} not found, it will be created at the next step.`
224230
);
225231
await createPipeline({ appName, environment, org, pipelineName });
226232
if (repositoryName) {
@@ -230,8 +236,8 @@ class HerokuApp {
230236
}
231237
}
232238
} catch (error) {
233-
if (error.status !== 2) {
234-
this.logger.warn(error.message);
239+
if (isExecException(error) && error.code !== 2) {
240+
this.logger.warn(error.message.trim());
235241
}
236242
}
237243
}
@@ -292,8 +298,8 @@ class HerokuApp {
292298
remoteBranch: string | undefined
293299
): Promise<boolean> {
294300
this.logger.info(`Reset repo ${remoteBranch}`);
295-
if (!(await this.hasPlugin('heroku-repo'))) {
296-
await exec('heroku plugins:install heroku-repo');
301+
if (!(await hasPlugin('heroku-repo'))) {
302+
await installPlugin('heroku-repo');
297303
}
298304
await exec(`heroku repo:reset -a ${appName}`);
299305
return true;
@@ -316,9 +322,11 @@ class HerokuApp {
316322
}
317323

318324
const push = spawn('git', args, { signal });
325+
//? if data contains `Everything up-to-date`, should we still restart the app
319326
push.stdout
320327
.setEncoding('utf-8')
321328
.on('data', (data) => this.logger.info(data));
329+
322330
push.stderr
323331
.setEncoding('utf-8')
324332
.on('data', (data) => this.logger.info(data));
@@ -432,7 +440,7 @@ class HerokuApp {
432440
await this.createStatic();
433441
await this.createAptfile();
434442
await this.addRemote();
435-
await mergeConfigVars(this.options);
443+
await this.mergeConfigVars();
436444
await this.addBuildPacks();
437445
// TODO: add warning if stack update is available https://devcenter.heroku.com/articles/upgrading-to-the-latest-stack
438446
await this.addToPipeline();

packages/nx-heroku/src/executors/deploy/services/heroku-deploy.service.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ export class HerokuDeployService extends HerokuBaseService<DeployExecutorSchema>
5858
encoding: 'utf-8',
5959
});
6060
if (status) {
61-
this.logger.warn(`Some local changes are not committed ${status}`);
61+
this.logger.warn(
62+
`Some local changes are not committed :\n ${status.trim()}`
63+
);
6264
}
6365

6466
// Check if Repo clone is shallow

0 commit comments

Comments
 (0)