Skip to content
This repository was archived by the owner on Oct 16, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 33 additions & 16 deletions commands/pipelines/diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,24 @@ function* diff(targetApp, downstreamApp, githubToken, herokuUserAgent) {
}
}

function* findDownstreamApps(heroku, coupling) {
const allApps = yield cli.action(`Fetching apps from pipeline`,
heroku.request({
method: 'GET',
path: `/pipelines/${coupling.pipeline.id}/apps`,
headers: { Accept: PIPELINES_HEADER }
}));

const sourceStage = coupling.stage;
const downstreamStage = PROMOTION_ORDER[PROMOTION_ORDER.indexOf(sourceStage) + 1];
if (downstreamStage === null || PROMOTION_ORDER.indexOf(sourceStage) < 0) {
throw new Error(`Unable to infer the downstream stage for ${sourceStage}`);
}
return allApps.filter(function (app) {
return app.coupling.stage === downstreamStage;
});
}

module.exports = {
topic: 'pipelines',
command: 'diff',
Expand All @@ -128,26 +146,28 @@ module.exports = {
}
const targetAppId = coupling.app.id;

const allApps = yield cli.action(`Fetching apps from pipeline`,
heroku.request({
method: 'GET',
path: `/pipelines/${coupling.pipeline.id}/apps`,
headers: { 'Accept': PIPELINES_HEADER }
}));

const sourceStage = coupling.stage;
const downstreamStage = PROMOTION_ORDER[PROMOTION_ORDER.indexOf(sourceStage) + 1];
if (downstreamStage === null || PROMOTION_ORDER.indexOf(sourceStage) < 0) {
// Find downstream apps
let downstreamApps;
try {
downstreamApps = yield findDownstreamApps(heroku, coupling);
} catch (err) {
return cli.error(`Unable to diff ${targetAppName}`);
}
const downstreamApps = allApps.filter(function(app) {
return app.coupling.stage === downstreamStage;
});

if (downstreamApps.length < 1) {
return cli.error(`Cannot diff ${targetAppName} as there are no downstream apps configured`);
}

// Fetch GitHub token for the user
let githubAccount;
try {
githubAccount = yield kolkrabbiRequest(`/account/github/token`, heroku.options.token);
} catch (err) {
cli.hush(err);
cli.error(`You need to enable the GitHub integration for your Heroku account`);
return cli.error(`See https://devcenter.heroku.com/articles/github-integration for help`);
}

// Fetch GitHub repo/latest release hash for [target, downstream[0], .., downstream[n]] apps
const wrappedGetAppInfo = co.wrap(getAppInfo);
const appInfoPromises = [wrappedGetAppInfo(heroku, targetAppName, targetAppId)];
Expand All @@ -165,9 +185,6 @@ module.exports = {
return cli.error(`No release was found for ${targetAppName}, unable to diff`);
}

// Fetch GitHub token for the user
const githubAccount = yield kolkrabbiRequest(`/account/github/token`, heroku.options.token);

// Diff [{target, downstream[0]}, {target, downstream[1]}, .., {target, downstream[n]}]
const downstreamAppsInfo = appInfo.slice(1);
for (let downstreamAppInfo of downstreamAppsInfo) {
Expand Down
30 changes: 24 additions & 6 deletions test/commands/pipelines/diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ describe('pipelines:diff', function () {
.reply(200, [targetApp, downstreamApp1, downstreamApp2]);
}

function mockGithubAccount() {
nock(kolkrabbiApi)
.get(`/account/github/token`)
.reply(200, { github: { token: 'github-token' } });
}

beforeEach(function () {
cli.mockConsole();
});
Expand All @@ -75,7 +81,7 @@ describe('pipelines:diff', function () {
nock.cleanAll();
});

describe('for app without a pipeline', function () {
describe('for apps without a pipeline', function () {
it('should return an error', function () {
const req = nock(api)
.get(`/apps/${targetApp.name}/pipeline-couplings`)
Expand All @@ -89,9 +95,11 @@ describe('pipelines:diff', function () {
});
});

describe('for app with a pipeline but no downstream apps', function () {
it('should return an error', function () {
describe('for apps with a pipeline', function () {
beforeEach(function () {
mockPipelineCoupling();
});
it('should return an error for pipelines with no downstream apps', function () {
nock(api)
.get(`/pipelines/${pipeline.id}/apps`)
.reply(200, [targetApp]);
Expand All @@ -101,12 +109,23 @@ describe('pipelines:diff', function () {
expect(cli.stderr).to.contain('no downstream apps');
});
});
it('should return an error if no GitHub account is associated', function () {
mockApps();
nock(kolkrabbiApi)
.get(`/account/github/token`)
.reply(404);
return cmd.run({ app: targetApp.name })
.then(function () {
expect(cli.stderr).to.contain('You need to enable the GitHub integration');
});
});
});

describe('for invalid apps with a pipeline', function () {
beforeEach(function () {
mockPipelineCoupling();
mockApps();
mockGithubAccount();
});

it('should return an error if the app is not connected to GitHub', function () {
Expand Down Expand Up @@ -144,6 +163,7 @@ describe('pipelines:diff', function () {
beforeEach(function () {
mockPipelineCoupling();
mockApps();
mockGithubAccount();

// Mock the GitHub apps for targetApp and downstreamApp1:
nock(kolkrabbiApi)
Expand All @@ -152,9 +172,7 @@ describe('pipelines:diff', function () {
.get(`/apps/${downstreamApp1.id}/github`)
.reply(200, downstreamApp1Github)
.get(`/apps/${downstreamApp2.id}/github`)
.reply(200, downstreamApp2Github)
.get(`/account/github/token`)
.reply(200, { github: { token: 'github-token' } });
.reply(200, downstreamApp2Github);

// Mock latest release/slug endpoints for two apps:
nock(api)
Expand Down