heroku 8.1.9 → 8.2.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/autocomplete-scripts/README.md +7 -0
- package/autocomplete-scripts/bash/heroku.bash +59 -0
- package/autocomplete-scripts/brew/bash +4 -0
- package/autocomplete-scripts/brew/zsh/_heroku +59 -0
- package/autocomplete-scripts/zsh/_heroku +53 -0
- package/bin/bats-test-runner.js +7 -0
- package/bin/dev +17 -0
- package/bin/dev.cmd +3 -0
- package/bin/run +36 -1
- package/lib/analytics.d.ts +2 -2
- package/lib/analytics.js +3 -4
- package/lib/commands/auth/2fa/disable.d.ts +7 -0
- package/lib/commands/auth/2fa/disable.js +16 -0
- package/lib/commands/auth/2fa/index.d.ts +6 -0
- package/lib/commands/auth/2fa/index.js +18 -0
- package/lib/commands/auth/login.d.ts +8 -0
- package/lib/commands/auth/login.js +25 -0
- package/lib/commands/auth/logout.d.ts +6 -0
- package/lib/commands/auth/logout.js +14 -0
- package/lib/commands/auth/token.d.ts +7 -0
- package/lib/commands/auth/token.js +31 -0
- package/lib/commands/auth/whoami.d.ts +7 -0
- package/lib/commands/auth/whoami.js +26 -0
- package/lib/commands/authorizations/create.d.ts +13 -0
- package/lib/commands/authorizations/create.js +41 -0
- package/lib/commands/authorizations/index.d.ts +9 -0
- package/lib/commands/authorizations/index.js +35 -0
- package/lib/commands/authorizations/info.d.ts +11 -0
- package/lib/commands/authorizations/info.js +25 -0
- package/lib/commands/authorizations/revoke.d.ts +10 -0
- package/lib/commands/authorizations/revoke.js +22 -0
- package/lib/commands/authorizations/rotate.d.ts +8 -0
- package/lib/commands/authorizations/rotate.js +19 -0
- package/lib/commands/authorizations/update.d.ts +13 -0
- package/lib/commands/authorizations/update.js +36 -0
- package/lib/commands/autocomplete/create.d.ts +29 -0
- package/lib/commands/autocomplete/create.js +213 -0
- package/lib/commands/autocomplete/doctor.d.ts +12 -0
- package/lib/commands/autocomplete/doctor.js +97 -0
- package/lib/commands/autocomplete/index.d.ts +12 -0
- package/lib/commands/autocomplete/index.js +67 -0
- package/lib/commands/autocomplete/options.d.ts +25 -0
- package/lib/commands/autocomplete/options.js +210 -0
- package/lib/commands/autocomplete/script.d.ts +10 -0
- package/lib/commands/autocomplete/script.js +23 -0
- package/lib/commands/buildpacks/add.d.ts +13 -0
- package/lib/commands/buildpacks/add.js +43 -0
- package/lib/commands/buildpacks/clear.d.ts +9 -0
- package/lib/commands/buildpacks/clear.js +17 -0
- package/lib/commands/buildpacks/index.d.ts +9 -0
- package/lib/commands/buildpacks/index.js +25 -0
- package/lib/commands/buildpacks/info.d.ts +8 -0
- package/lib/commands/buildpacks/info.js +41 -0
- package/lib/commands/buildpacks/remove.d.ts +13 -0
- package/lib/commands/buildpacks/remove.js +55 -0
- package/lib/commands/buildpacks/search.d.ts +13 -0
- package/lib/commands/buildpacks/search.js +69 -0
- package/lib/commands/buildpacks/set.d.ts +13 -0
- package/lib/commands/buildpacks/set.js +42 -0
- package/lib/commands/buildpacks/versions.d.ts +8 -0
- package/lib/commands/buildpacks/versions.js +56 -0
- package/lib/commands/certs/auto/wait.d.ts +9 -0
- package/lib/commands/certs/auto/wait.js +14 -0
- package/lib/commands/ci/index.d.ts +12 -0
- package/lib/commands/ci/index.js +25 -0
- package/lib/commands/ci/info.d.ts +14 -0
- package/lib/commands/ci/info.js +29 -0
- package/lib/commands/ci/last.d.ts +11 -0
- package/lib/commands/ci/last.js +31 -0
- package/lib/commands/ci/rerun.d.ts +13 -0
- package/lib/commands/ci/rerun.js +53 -0
- package/lib/commands/ci/run.d.ts +10 -0
- package/lib/commands/ci/run.js +42 -0
- package/lib/commands/clients/create.d.ts +14 -0
- package/lib/commands/clients/create.js +37 -0
- package/lib/commands/clients/destroy.d.ts +8 -0
- package/lib/commands/clients/destroy.js +18 -0
- package/lib/commands/clients/index.d.ts +8 -0
- package/lib/commands/clients/index.js +32 -0
- package/lib/commands/clients/info.d.ts +13 -0
- package/lib/commands/clients/info.js +33 -0
- package/lib/commands/clients/rotate.d.ts +12 -0
- package/lib/commands/clients/rotate.js +33 -0
- package/lib/commands/clients/update.d.ts +13 -0
- package/lib/commands/clients/update.js +38 -0
- package/lib/commands/config/edit.d.ts +23 -0
- package/lib/commands/config/edit.js +132 -0
- package/lib/commands/config/get.d.ts +16 -0
- package/lib/commands/config/get.js +35 -0
- package/lib/commands/config/index.d.ts +11 -0
- package/lib/commands/config/index.js +33 -0
- package/lib/commands/config/unset.d.ts +12 -0
- package/lib/commands/config/unset.js +51 -0
- package/lib/commands/console.d.ts +11 -0
- package/lib/commands/console.js +30 -0
- package/lib/commands/domains/add.d.ts +19 -0
- package/lib/commands/domains/add.js +117 -0
- package/lib/commands/domains/clear.d.ts +11 -0
- package/lib/commands/domains/clear.js +26 -0
- package/lib/commands/domains/index.d.ts +40 -0
- package/lib/commands/domains/index.js +79 -0
- package/lib/commands/domains/info.d.ts +14 -0
- package/lib/commands/domains/info.js +25 -0
- package/lib/commands/domains/remove.d.ts +14 -0
- package/lib/commands/domains/remove.js +26 -0
- package/lib/commands/domains/update.d.ts +15 -0
- package/lib/commands/domains/update.js +40 -0
- package/lib/commands/domains/wait.d.ts +13 -0
- package/lib/commands/domains/wait.js +32 -0
- package/lib/commands/git/clone.d.ts +13 -0
- package/lib/commands/git/clone.js +29 -0
- package/lib/commands/git/credentials.d.ts +9 -0
- package/lib/commands/git/credentials.js +32 -0
- package/lib/commands/git/remote.d.ts +11 -0
- package/lib/commands/git/remote.js +44 -0
- package/lib/commands/labs/disable.d.ts +11 -0
- package/lib/commands/labs/disable.js +65 -0
- package/lib/commands/local/index.d.ts +17 -0
- package/lib/commands/local/index.js +75 -0
- package/lib/commands/local/run.d.ts +11 -0
- package/lib/commands/local/run.js +38 -0
- package/lib/commands/local/version.d.ts +5 -0
- package/lib/commands/local/version.js +13 -0
- package/lib/commands/logs.d.ts +16 -0
- package/lib/commands/logs.js +45 -0
- package/lib/commands/pipelines/add.d.ts +14 -0
- package/lib/commands/pipelines/add.js +61 -0
- package/lib/commands/pipelines/connect.d.ts +12 -0
- package/lib/commands/pipelines/connect.js +52 -0
- package/lib/commands/pipelines/create.d.ts +15 -0
- package/lib/commands/pipelines/create.js +91 -0
- package/lib/commands/pipelines/destroy.d.ts +9 -0
- package/lib/commands/pipelines/destroy.js +27 -0
- package/lib/commands/pipelines/diff.d.ts +19 -0
- package/lib/commands/pipelines/diff.js +152 -0
- package/lib/commands/pipelines/index.d.ts +9 -0
- package/lib/commands/pipelines/index.js +27 -0
- package/lib/commands/pipelines/info.d.ts +13 -0
- package/lib/commands/pipelines/info.js +43 -0
- package/lib/commands/pipelines/open.d.ts +9 -0
- package/lib/commands/pipelines/open.js +19 -0
- package/lib/commands/pipelines/promote.d.ts +12 -0
- package/lib/commands/pipelines/promote.js +216 -0
- package/lib/commands/pipelines/remove.d.ts +10 -0
- package/lib/commands/pipelines/remove.js +23 -0
- package/lib/commands/pipelines/rename.d.ts +10 -0
- package/lib/commands/pipelines/rename.js +33 -0
- package/lib/commands/pipelines/setup.d.ts +14 -0
- package/lib/commands/pipelines/setup.js +91 -0
- package/lib/commands/pipelines/transfer.d.ts +13 -0
- package/lib/commands/pipelines/transfer.js +65 -0
- package/lib/commands/pipelines/update.d.ts +11 -0
- package/lib/commands/pipelines/update.js +32 -0
- package/lib/commands/ps/autoscale/disable.d.ts +9 -0
- package/lib/commands/ps/autoscale/disable.js +35 -0
- package/lib/commands/ps/autoscale/enable.d.ts +13 -0
- package/lib/commands/ps/autoscale/enable.js +65 -0
- package/lib/commands/ps/wait.d.ts +12 -0
- package/lib/commands/ps/wait.js +78 -0
- package/lib/commands/rake.d.ts +14 -0
- package/lib/commands/rake.js +46 -0
- package/lib/commands/regions.d.ts +11 -0
- package/lib/commands/regions.js +45 -0
- package/lib/commands/reviewapps/disable.d.ts +17 -0
- package/lib/commands/reviewapps/disable.js +102 -0
- package/lib/commands/reviewapps/enable.d.ts +14 -0
- package/lib/commands/reviewapps/enable.js +89 -0
- package/lib/commands/run/detached.d.ts +15 -0
- package/lib/commands/run/detached.js +56 -0
- package/lib/commands/run/index.d.ts +18 -0
- package/lib/commands/run/index.js +64 -0
- package/lib/commands/run/inside.d.ts +15 -0
- package/lib/commands/run/inside.js +55 -0
- package/lib/commands/sessions/destroy.d.ts +8 -0
- package/lib/commands/sessions/destroy.js +18 -0
- package/lib/commands/sessions/index.d.ts +8 -0
- package/lib/commands/sessions/index.js +31 -0
- package/lib/commands/status.d.ts +8 -0
- package/lib/commands/status.js +46 -0
- package/lib/commands/webhooks/add.d.ts +16 -0
- package/lib/commands/webhooks/add.js +47 -0
- package/lib/commands/webhooks/deliveries/index.d.ts +12 -0
- package/lib/commands/webhooks/deliveries/index.js +80 -0
- package/lib/commands/webhooks/deliveries/info.d.ts +14 -0
- package/lib/commands/webhooks/deliveries/info.js +42 -0
- package/lib/commands/webhooks/events/index.d.ts +11 -0
- package/lib/commands/webhooks/events/index.js +46 -0
- package/lib/commands/webhooks/events/info.d.ts +14 -0
- package/lib/commands/webhooks/events/info.js +31 -0
- package/lib/commands/webhooks/index.d.ts +11 -0
- package/lib/commands/webhooks/index.js +45 -0
- package/lib/commands/webhooks/info.d.ts +14 -0
- package/lib/commands/webhooks/info.js +31 -0
- package/lib/commands/webhooks/remove.d.ts +14 -0
- package/lib/commands/webhooks/remove.js +29 -0
- package/lib/commands/webhooks/update.d.ts +19 -0
- package/lib/commands/webhooks/update.js +41 -0
- package/lib/file.js +2 -3
- package/lib/global_telemetry.d.ts +53 -0
- package/lib/global_telemetry.js +82 -0
- package/lib/hooks/command_not_found/performance_analytics.d.ts +3 -0
- package/lib/hooks/command_not_found/performance_analytics.js +7 -0
- package/lib/hooks/init/terms-of-service.js +3 -4
- package/lib/hooks/init/version.d.ts +2 -1
- package/lib/hooks/init/version.js +1 -2
- package/lib/hooks/postrun/performance_analytics.d.ts +3 -0
- package/lib/hooks/postrun/performance_analytics.js +11 -0
- package/lib/hooks/prerun/analytics.d.ts +2 -1
- package/lib/hooks/prerun/analytics.js +4 -4
- package/lib/hooks/recache.d.ts +3 -0
- package/lib/hooks/recache.js +56 -0
- package/lib/hooks/update/brew.d.ts +2 -1
- package/lib/hooks/update/brew.js +3 -5
- package/lib/hooks/update/completions.d.ts +2 -1
- package/lib/hooks/update/completions.js +2 -3
- package/lib/hooks/update/plugin-migrate.d.ts +2 -1
- package/lib/hooks/update/plugin-migrate.js +3 -5
- package/lib/hooks/update/tidy.d.ts +2 -1
- package/lib/hooks/update/tidy.js +3 -5
- package/lib/lib/authorizations/authorizations.d.ts +2 -0
- package/lib/lib/authorizations/authorizations.js +39 -0
- package/lib/lib/autocomplete/base.d.ts +11 -0
- package/lib/lib/autocomplete/base.js +44 -0
- package/lib/lib/autocomplete/cache.d.ts +2 -0
- package/lib/lib/autocomplete/cache.js +28 -0
- package/lib/lib/autocomplete/completions.d.ts +37 -0
- package/lib/lib/autocomplete/completions.js +261 -0
- package/lib/lib/buildpacks/buildpacks.d.ts +32 -0
- package/lib/lib/buildpacks/buildpacks.js +161 -0
- package/lib/lib/buildpacks/push.d.ts +0 -0
- package/lib/lib/buildpacks/push.js +4 -0
- package/lib/lib/ci/git.d.ts +8 -0
- package/lib/lib/ci/git.js +71 -0
- package/lib/lib/ci/interfaces/kolkrabbi.d.ts +311 -0
- package/lib/lib/ci/interfaces/kolkrabbi.js +7 -0
- package/lib/lib/ci/pipelines.d.ts +3 -0
- package/lib/lib/ci/pipelines.js +57 -0
- package/lib/lib/ci/source.d.ts +2 -0
- package/lib/lib/ci/source.js +42 -0
- package/lib/lib/ci/test-run.d.ts +5 -0
- package/lib/lib/ci/test-run.js +232 -0
- package/lib/lib/clients/clients.d.ts +1 -0
- package/lib/lib/clients/clients.js +25 -0
- package/lib/lib/config/quote.d.ts +2 -0
- package/lib/lib/config/quote.js +33 -0
- package/lib/lib/config/util.d.ts +3 -0
- package/lib/lib/config/util.js +12 -0
- package/lib/lib/domains/wait-for-domain.d.ts +3 -0
- package/lib/lib/domains/wait-for-domain.js +19 -0
- package/lib/lib/git/git.d.ts +8 -0
- package/lib/lib/git/git.js +56 -0
- package/lib/lib/local/fork-foreman.d.ts +1 -0
- package/lib/lib/local/fork-foreman.js +33 -0
- package/lib/lib/local/load-foreman-procfile.d.ts +0 -0
- package/lib/lib/local/load-foreman-procfile.js +2 -0
- package/lib/lib/local/run-foreman.d.ts +0 -0
- package/lib/lib/local/run-foreman.js +2 -0
- package/lib/lib/pipelines/api.d.ts +25 -0
- package/lib/lib/pipelines/api.js +130 -0
- package/lib/lib/pipelines/disambiguate.d.ts +3 -0
- package/lib/lib/pipelines/disambiguate.js +49 -0
- package/lib/lib/pipelines/github-api.d.ts +8 -0
- package/lib/lib/pipelines/github-api.js +18 -0
- package/lib/lib/pipelines/infer.d.ts +1 -0
- package/lib/lib/pipelines/infer.js +11 -0
- package/lib/lib/pipelines/key-by.d.ts +1 -0
- package/lib/lib/pipelines/key-by.js +11 -0
- package/lib/lib/pipelines/kolkrabbi-api.d.ts +14 -0
- package/lib/lib/pipelines/kolkrabbi-api.js +62 -0
- package/lib/lib/pipelines/ownership.d.ts +4 -0
- package/lib/lib/pipelines/ownership.js +40 -0
- package/lib/lib/pipelines/render-pipeline.d.ts +6 -0
- package/lib/lib/pipelines/render-pipeline.js +52 -0
- package/lib/lib/pipelines/setup/create-apps.d.ts +1 -0
- package/lib/lib/pipelines/setup/create-apps.js +44 -0
- package/lib/lib/pipelines/setup/get-ci-settings.d.ts +4 -0
- package/lib/lib/pipelines/setup/get-ci-settings.js +19 -0
- package/lib/lib/pipelines/setup/get-github-token.d.ts +1 -0
- package/lib/lib/pipelines/setup/get-github-token.js +10 -0
- package/lib/lib/pipelines/setup/get-name-and-repo.d.ts +1 -0
- package/lib/lib/pipelines/setup/get-name-and-repo.js +47 -0
- package/lib/lib/pipelines/setup/get-repo.d.ts +1 -0
- package/lib/lib/pipelines/setup/get-repo.js +8 -0
- package/lib/lib/pipelines/setup/get-settings.d.ts +9 -0
- package/lib/lib/pipelines/setup/get-settings.js +39 -0
- package/lib/lib/pipelines/setup/poll-app-setups.d.ts +1 -0
- package/lib/lib/pipelines/setup/poll-app-setups.js +25 -0
- package/lib/lib/pipelines/setup/setup-pipeline.d.ts +1 -0
- package/lib/lib/pipelines/setup/setup-pipeline.js +15 -0
- package/lib/lib/pipelines/setup/validate.d.ts +9 -0
- package/lib/lib/pipelines/setup/validate.js +31 -0
- package/lib/lib/pipelines/stages.d.ts +7 -0
- package/lib/lib/pipelines/stages.js +22 -0
- package/lib/lib/run/colorize.d.ts +2 -0
- package/lib/lib/run/colorize.js +300 -0
- package/lib/lib/run/dyno.d.ts +72 -0
- package/lib/lib/run/dyno.js +397 -0
- package/lib/lib/run/helpers.d.ts +2 -0
- package/lib/lib/run/helpers.js +34 -0
- package/lib/lib/run/line-transform.d.ts +4 -0
- package/lib/lib/run/line-transform.js +26 -0
- package/lib/lib/run/log-displayer.d.ts +10 -0
- package/lib/lib/run/log-displayer.js +79 -0
- package/lib/lib/sessions/sessions.d.ts +7 -0
- package/lib/lib/sessions/sessions.js +2 -0
- package/lib/lib/status/util.d.ts +1 -0
- package/lib/lib/status/util.js +14 -0
- package/lib/lib/webhooks/base.d.ts +13 -0
- package/lib/lib/webhooks/base.js +28 -0
- package/lib/user-config.js +2 -3
- package/oclif.manifest.json +3376 -2
- package/package.json +79 -42
- package/lib/hooks/update/b.d.ts +0 -2
- package/lib/hooks/update/b.js +0 -48
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sleep = void 0;
|
|
4
|
+
const color_1 = require("@heroku-cli/color");
|
|
5
|
+
const command_1 = require("@heroku-cli/command");
|
|
6
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const assert = require("assert");
|
|
8
|
+
const node_fetch_1 = require("node-fetch");
|
|
9
|
+
const Stream = require("stream");
|
|
10
|
+
const util = require("util");
|
|
11
|
+
const api_1 = require("../../lib/pipelines/api");
|
|
12
|
+
const key_by_1 = require("../../lib/pipelines/key-by");
|
|
13
|
+
const sleep = (time) => {
|
|
14
|
+
return new Promise(resolve => setTimeout(resolve, time));
|
|
15
|
+
};
|
|
16
|
+
exports.sleep = sleep;
|
|
17
|
+
function assertNotPromotingToSelf(source, target) {
|
|
18
|
+
assert.notStrictEqual(source, target, `Cannot promote from an app to itself: ${target}. Specify a different target app.`);
|
|
19
|
+
}
|
|
20
|
+
function findAppInPipeline(apps, target) {
|
|
21
|
+
const found = apps.find(app => (app.name === target) || (app.id === target));
|
|
22
|
+
assert(found, `Cannot find app ${color_1.default.app(target)}`);
|
|
23
|
+
return found;
|
|
24
|
+
}
|
|
25
|
+
const PROMOTION_ORDER = ['development', 'staging', 'production'];
|
|
26
|
+
const wait = (ms = 100) => new Promise(resolve => setTimeout(resolve, ms));
|
|
27
|
+
function isComplete(promotionTarget) {
|
|
28
|
+
return promotionTarget.status !== 'pending';
|
|
29
|
+
}
|
|
30
|
+
function isSucceeded(promotionTarget) {
|
|
31
|
+
return promotionTarget.status === 'succeeded';
|
|
32
|
+
}
|
|
33
|
+
function isFailed(promotionTarget) {
|
|
34
|
+
return promotionTarget.status === 'failed';
|
|
35
|
+
}
|
|
36
|
+
function pollPromotionStatus(heroku, id, needsReleaseCommand) {
|
|
37
|
+
return heroku.get(`/pipeline-promotions/${id}/promotion-targets`).then(function ({ body: targets }) {
|
|
38
|
+
if (targets.every(isComplete)) { // eslint-disable-line unicorn/no-array-callback-reference
|
|
39
|
+
return targets;
|
|
40
|
+
}
|
|
41
|
+
//
|
|
42
|
+
// With only one target, we can return as soon as the release is created.
|
|
43
|
+
// The command will then read the release phase output
|
|
44
|
+
//
|
|
45
|
+
// `needsReleaseCommand` allows us to keep polling, as it can take a few
|
|
46
|
+
// seconds to get the release to succeeded after the release command
|
|
47
|
+
// finished.
|
|
48
|
+
//
|
|
49
|
+
if (needsReleaseCommand && targets.length === 1 && targets[0].release !== null) {
|
|
50
|
+
return targets;
|
|
51
|
+
}
|
|
52
|
+
return wait(1000).then(pollPromotionStatus.bind(null, heroku, id, needsReleaseCommand));
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async function getCoupling(heroku, app) {
|
|
56
|
+
core_1.ux.log('Fetching app info...');
|
|
57
|
+
const { body: coupling } = await heroku.get(`/apps/${app}/pipeline-couplings`);
|
|
58
|
+
return coupling;
|
|
59
|
+
}
|
|
60
|
+
async function promote(heroku, label, id, sourceAppId, targetApps, secondFactor) {
|
|
61
|
+
const options = {
|
|
62
|
+
headers: {},
|
|
63
|
+
body: {
|
|
64
|
+
pipeline: { id },
|
|
65
|
+
source: { app: { id: sourceAppId } },
|
|
66
|
+
targets: targetApps.map(app => ({ app: { id: app.id } })),
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
if (secondFactor) {
|
|
70
|
+
options.headers = { 'Heroku-Two-Factor-Code': secondFactor };
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
core_1.ux.log(`${label}...`);
|
|
74
|
+
const { body: promotions } = await heroku.post('/pipeline-promotions', options);
|
|
75
|
+
return promotions;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (!error.body || error.body.id !== 'two_factor') {
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
const secondFactor = await heroku.twoFactorPrompt();
|
|
82
|
+
return promote(heroku, label, id, sourceAppId, targetApps, secondFactor);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function assertValidPromotion(app, source, target) {
|
|
86
|
+
if (!target || PROMOTION_ORDER.indexOf(source) < 0) { // eslint-disable-line unicorn/prefer-includes
|
|
87
|
+
throw new Error(`Cannot promote ${app} from '${source}' stage`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function assertApps(app, targetApps, targetStage) {
|
|
91
|
+
if (targetApps.length === 0) {
|
|
92
|
+
throw new Error(`Cannot promote from ${color_1.default.app(app)} as there are no downstream apps in ${targetStage} stage`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function getRelease(heroku, app, releaseId) {
|
|
96
|
+
core_1.ux.log('Fetching release info...');
|
|
97
|
+
const { body: release } = await heroku.get(`/apps/${app}/releases/${releaseId}`);
|
|
98
|
+
return release;
|
|
99
|
+
}
|
|
100
|
+
async function streamReleaseCommand(heroku, targets, promotion) {
|
|
101
|
+
if (targets.length !== 1 || targets.every(isComplete)) { // eslint-disable-line unicorn/no-array-callback-reference
|
|
102
|
+
return pollPromotionStatus(heroku, promotion.id, false);
|
|
103
|
+
}
|
|
104
|
+
const target = targets[0];
|
|
105
|
+
const release = await getRelease(heroku, target.app.id, target.release.id);
|
|
106
|
+
if (!release.output_stream_url) {
|
|
107
|
+
return pollPromotionStatus(heroku, promotion.id, false);
|
|
108
|
+
}
|
|
109
|
+
core_1.ux.log('Running release command...');
|
|
110
|
+
async function streamReleaseOutput(releaseStreamUrl) {
|
|
111
|
+
const finished = util.promisify(Stream.finished);
|
|
112
|
+
const fetchResponse = await (0, node_fetch_1.default)(releaseStreamUrl);
|
|
113
|
+
if (fetchResponse.status >= 400) {
|
|
114
|
+
throw new Error('stream release output not available');
|
|
115
|
+
}
|
|
116
|
+
fetchResponse.body.pipe(process.stdout);
|
|
117
|
+
await finished(fetchResponse.body);
|
|
118
|
+
}
|
|
119
|
+
async function retry(maxAttempts, fn) {
|
|
120
|
+
let currentAttempt = 0;
|
|
121
|
+
while (true) {
|
|
122
|
+
try {
|
|
123
|
+
await fn();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
if (++currentAttempt === maxAttempts) {
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
await (0, exports.sleep)(1000);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
await retry(100, () => {
|
|
135
|
+
return streamReleaseOutput(release.output_stream_url);
|
|
136
|
+
});
|
|
137
|
+
return pollPromotionStatus(heroku, promotion.id, false);
|
|
138
|
+
}
|
|
139
|
+
class Promote extends command_1.Command {
|
|
140
|
+
async run() {
|
|
141
|
+
const { flags } = await this.parse(Promote);
|
|
142
|
+
const appNameOrId = flags.app;
|
|
143
|
+
const coupling = await getCoupling(this.heroku, appNameOrId);
|
|
144
|
+
core_1.ux.log(`Fetching apps from ${color_1.default.pipeline(coupling.pipeline.name)}...`);
|
|
145
|
+
const allApps = await (0, api_1.listPipelineApps)(this.heroku, coupling.pipeline.id);
|
|
146
|
+
const sourceStage = coupling.stage;
|
|
147
|
+
let promotionActionName = '';
|
|
148
|
+
let targetApps = [];
|
|
149
|
+
if (flags.to) {
|
|
150
|
+
// The user specified a specific set of apps they want to target
|
|
151
|
+
// We don't have to infer the apps or the stage they want to promote to
|
|
152
|
+
// Strip out any empty app names due to something like a trailing comma
|
|
153
|
+
const targetAppNames = flags.to.split(',').filter(appName => appName.length > 0);
|
|
154
|
+
// Now let's make sure that we can find every target app they specified
|
|
155
|
+
// The only requirement is that the app be in this pipeline. They can be at any stage.
|
|
156
|
+
targetApps = targetAppNames.reduce((acc, targetAppNameOrId) => {
|
|
157
|
+
assertNotPromotingToSelf(appNameOrId, targetAppNameOrId);
|
|
158
|
+
const app = findAppInPipeline(allApps, targetAppNameOrId);
|
|
159
|
+
if (app) {
|
|
160
|
+
acc.push(app);
|
|
161
|
+
}
|
|
162
|
+
return acc;
|
|
163
|
+
}, []);
|
|
164
|
+
promotionActionName = `Starting promotion to apps: ${targetAppNames.toString()}`;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const targetStage = PROMOTION_ORDER[PROMOTION_ORDER.indexOf(sourceStage) + 1];
|
|
168
|
+
assertValidPromotion(appNameOrId, sourceStage, targetStage);
|
|
169
|
+
targetApps = allApps.filter(app => app.coupling.stage === targetStage);
|
|
170
|
+
assertApps(appNameOrId, targetApps, targetStage);
|
|
171
|
+
promotionActionName = `Starting promotion to ${targetStage}`;
|
|
172
|
+
}
|
|
173
|
+
const promotion = await promote(this.heroku, promotionActionName, coupling.pipeline.id, coupling.app.id, targetApps);
|
|
174
|
+
const pollLoop = pollPromotionStatus(this.heroku, promotion.id, true);
|
|
175
|
+
core_1.ux.log('Waiting for promotion to complete...');
|
|
176
|
+
let promotionTargets = await pollLoop;
|
|
177
|
+
try {
|
|
178
|
+
promotionTargets = await streamReleaseCommand(this.heroku, promotionTargets, promotion);
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
core_1.ux.error(error);
|
|
182
|
+
}
|
|
183
|
+
const appsByID = (0, key_by_1.default)(allApps, 'id');
|
|
184
|
+
const styledTargets = promotionTargets.reduce(function (memo, target) {
|
|
185
|
+
const app = appsByID[target.app.id];
|
|
186
|
+
const details = [target.status];
|
|
187
|
+
if (isFailed(target)) {
|
|
188
|
+
details.push(target.error_message);
|
|
189
|
+
}
|
|
190
|
+
memo[app.name] = details;
|
|
191
|
+
return memo;
|
|
192
|
+
}, {});
|
|
193
|
+
if (promotionTargets.every(isSucceeded)) { // eslint-disable-line unicorn/no-array-callback-reference
|
|
194
|
+
core_1.ux.log('\nPromotion successful');
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
core_1.ux.warn('\nPromotion to some apps failed');
|
|
198
|
+
}
|
|
199
|
+
core_1.ux.styledObject(styledTargets);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
exports.default = Promote;
|
|
203
|
+
Promote.description = 'promote the latest release of this app to its downstream app(s)';
|
|
204
|
+
Promote.examples = [
|
|
205
|
+
'$ heroku pipelines:promote -a my-app-staging',
|
|
206
|
+
];
|
|
207
|
+
Promote.flags = {
|
|
208
|
+
app: command_1.flags.app({
|
|
209
|
+
required: true,
|
|
210
|
+
}),
|
|
211
|
+
remote: command_1.flags.remote(),
|
|
212
|
+
to: command_1.flags.string({
|
|
213
|
+
char: 't',
|
|
214
|
+
description: 'comma separated list of apps to promote to',
|
|
215
|
+
}),
|
|
216
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class PipelinesRemove extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
app: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
7
|
+
remote: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const color_1 = require("@heroku-cli/color");
|
|
4
|
+
const command_1 = require("@heroku-cli/command");
|
|
5
|
+
const core_1 = require("@oclif/core");
|
|
6
|
+
const api_1 = require("../../lib/pipelines/api");
|
|
7
|
+
class PipelinesRemove extends command_1.Command {
|
|
8
|
+
async run() {
|
|
9
|
+
const { flags: { app } } = await this.parse(PipelinesRemove);
|
|
10
|
+
core_1.ux.action.start(`Removing ${color_1.default.app(app)}`);
|
|
11
|
+
await (0, api_1.removeCoupling)(this.heroku, app);
|
|
12
|
+
core_1.ux.action.stop();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.default = PipelinesRemove;
|
|
16
|
+
PipelinesRemove.description = 'remove this app from its pipeline';
|
|
17
|
+
PipelinesRemove.examples = [
|
|
18
|
+
'$ heroku pipelines:remove -a my-app',
|
|
19
|
+
];
|
|
20
|
+
PipelinesRemove.flags = {
|
|
21
|
+
app: command_1.flags.app({ required: true }),
|
|
22
|
+
remote: command_1.flags.remote(),
|
|
23
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class PipelinesRename extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
pipeline: import("@oclif/core/lib/interfaces/parser").Arg<string, Record<string, unknown>>;
|
|
7
|
+
name: import("@oclif/core/lib/interfaces/parser").Arg<string, Record<string, unknown>>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const color_1 = require("@heroku-cli/color");
|
|
4
|
+
const command_1 = require("@heroku-cli/command");
|
|
5
|
+
const core_1 = require("@oclif/core");
|
|
6
|
+
const api_1 = require("../../lib/pipelines/api");
|
|
7
|
+
const disambiguate_1 = require("../../lib/pipelines/disambiguate");
|
|
8
|
+
class PipelinesRename extends command_1.Command {
|
|
9
|
+
async run() {
|
|
10
|
+
const { args } = await this.parse(PipelinesRename);
|
|
11
|
+
const pipeline = await (0, disambiguate_1.default)(this.heroku, args.pipeline);
|
|
12
|
+
core_1.ux.action.start(`Renaming ${color_1.default.pipeline(pipeline.name)} pipeline to ${color_1.default.pipeline(args.name)}`);
|
|
13
|
+
await (0, api_1.updatePipeline)(this.heroku, pipeline.id, {
|
|
14
|
+
name: args.name,
|
|
15
|
+
});
|
|
16
|
+
core_1.ux.action.stop();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.default = PipelinesRename;
|
|
20
|
+
PipelinesRename.description = 'rename a pipeline';
|
|
21
|
+
PipelinesRename.examples = [
|
|
22
|
+
'$ heroku pipelines:rename my-pipeline new-pipeline-name',
|
|
23
|
+
];
|
|
24
|
+
PipelinesRename.args = {
|
|
25
|
+
pipeline: core_1.Args.string({
|
|
26
|
+
description: 'current name of pipeline',
|
|
27
|
+
required: true,
|
|
28
|
+
}),
|
|
29
|
+
name: core_1.Args.string({
|
|
30
|
+
description: 'new name of pipeline',
|
|
31
|
+
required: true,
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class Setup extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
team: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
7
|
+
yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
};
|
|
9
|
+
static args: {
|
|
10
|
+
name: import("@oclif/core/lib/interfaces/parser").Arg<string | undefined, Record<string, unknown>>;
|
|
11
|
+
repo: import("@oclif/core/lib/interfaces/parser").Arg<string | undefined, Record<string, unknown>>;
|
|
12
|
+
};
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const color_1 = require("@heroku-cli/color");
|
|
4
|
+
const command_1 = require("@heroku-cli/command");
|
|
5
|
+
const core_1 = require("@oclif/core");
|
|
6
|
+
const open = require("open");
|
|
7
|
+
const debug_1 = require("debug");
|
|
8
|
+
const api_1 = require("../../lib/pipelines/api");
|
|
9
|
+
const github_api_1 = require("../../lib/pipelines/github-api");
|
|
10
|
+
const kolkrabbi_api_1 = require("../../lib/pipelines/kolkrabbi-api");
|
|
11
|
+
const create_apps_1 = require("../../lib/pipelines/setup/create-apps");
|
|
12
|
+
const get_ci_settings_1 = require("../../lib/pipelines/setup/get-ci-settings");
|
|
13
|
+
const get_github_token_1 = require("../../lib/pipelines/setup/get-github-token");
|
|
14
|
+
const get_name_and_repo_1 = require("../../lib/pipelines/setup/get-name-and-repo");
|
|
15
|
+
const get_repo_1 = require("../../lib/pipelines/setup/get-repo");
|
|
16
|
+
const get_settings_1 = require("../../lib/pipelines/setup/get-settings");
|
|
17
|
+
const poll_app_setups_1 = require("../../lib/pipelines/setup/poll-app-setups");
|
|
18
|
+
const setup_pipeline_1 = require("../../lib/pipelines/setup/setup-pipeline");
|
|
19
|
+
const validate_1 = require("../../lib/pipelines/setup/validate");
|
|
20
|
+
// eslint-disable-next-line new-cap
|
|
21
|
+
const debug = (0, debug_1.default)('pipelines:setup');
|
|
22
|
+
class Setup extends command_1.Command {
|
|
23
|
+
async run() {
|
|
24
|
+
const { args, flags } = await this.parse(Setup);
|
|
25
|
+
const errors = (0, validate_1.nameAndRepo)(args);
|
|
26
|
+
if (errors.length > 0) {
|
|
27
|
+
this.error(errors.join(', '));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const kolkrabbi = new kolkrabbi_api_1.default(this.config.userAgent, () => this.heroku.auth);
|
|
31
|
+
const github = new github_api_1.default(this.config.userAgent, await (0, get_github_token_1.default)(kolkrabbi));
|
|
32
|
+
const team = flags.team;
|
|
33
|
+
const { name: pipelineName, repo: repoName } = await (0, get_name_and_repo_1.default)(args);
|
|
34
|
+
const stagingAppName = pipelineName + validate_1.STAGING_APP_INDICATOR;
|
|
35
|
+
const repo = await (0, get_repo_1.default)(github, repoName);
|
|
36
|
+
const settings = await (0, get_settings_1.default)(flags.yes, repo.default_branch);
|
|
37
|
+
const ciSettings = await (0, get_ci_settings_1.default)(flags.yes, team);
|
|
38
|
+
const ownerType = team ? 'team' : 'user';
|
|
39
|
+
// If team or org is not specified, we assign ownership to the user creating
|
|
40
|
+
const { body: { id: ownerID }, } = team ? await (0, api_1.getTeam)(this.heroku, team) : await (0, api_1.getAccountInfo)(this.heroku);
|
|
41
|
+
const owner = { id: ownerID, type: ownerType };
|
|
42
|
+
core_1.ux.action.start('Creating pipeline');
|
|
43
|
+
const { body: pipeline } = await (0, api_1.createPipeline)(this.heroku, pipelineName, owner);
|
|
44
|
+
core_1.ux.action.stop();
|
|
45
|
+
core_1.ux.action.start('Linking to repo');
|
|
46
|
+
await kolkrabbi.createPipelineRepository(pipeline.id, repo.id);
|
|
47
|
+
core_1.ux.action.stop();
|
|
48
|
+
const archiveURL = await kolkrabbi.getArchiveURL(repoName, repo.default_branch);
|
|
49
|
+
const appSetupsResult = await (0, create_apps_1.default)(this.heroku, archiveURL, pipeline, pipelineName, stagingAppName, team);
|
|
50
|
+
const appSetups = appSetupsResult.map((result) => result.body);
|
|
51
|
+
core_1.ux.action.start(`Creating production and staging apps (${color_1.default.app(pipelineName)} and ${color_1.default.app(stagingAppName)})`);
|
|
52
|
+
await (0, poll_app_setups_1.default)(this.heroku, appSetups);
|
|
53
|
+
core_1.ux.action.stop();
|
|
54
|
+
const stagingApp = appSetups.find((appSetup) => appSetup.app.name === stagingAppName).app;
|
|
55
|
+
const setup = (0, setup_pipeline_1.default)(kolkrabbi, stagingApp.id, settings, pipeline.id, ciSettings);
|
|
56
|
+
core_1.ux.action.start('Configuring pipeline');
|
|
57
|
+
try {
|
|
58
|
+
await setup;
|
|
59
|
+
await open(`https://dashboard.heroku.com/pipelines/${pipeline.id}`);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
debug(error);
|
|
63
|
+
core_1.ux.error(error);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
core_1.ux.action.stop();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.default = Setup;
|
|
71
|
+
Setup.description = 'bootstrap a new pipeline with common settings and create a production and staging app (requires a fully formed app.json in the repo)';
|
|
72
|
+
Setup.examples = ['$ heroku pipelines:setup my-pipeline githuborg/reponame -t my-team'];
|
|
73
|
+
Setup.flags = {
|
|
74
|
+
team: command_1.flags.team({
|
|
75
|
+
description: 'the team to assign pipeline ownership to (defaults to current user)',
|
|
76
|
+
}),
|
|
77
|
+
yes: command_1.flags.boolean({
|
|
78
|
+
char: 'y',
|
|
79
|
+
description: 'accept all default settings without prompting',
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
82
|
+
Setup.args = {
|
|
83
|
+
name: core_1.Args.string({
|
|
84
|
+
description: 'name of pipeline',
|
|
85
|
+
required: false,
|
|
86
|
+
}),
|
|
87
|
+
repo: core_1.Args.string({
|
|
88
|
+
description: 'a GitHub repository to connect the pipeline to',
|
|
89
|
+
required: false,
|
|
90
|
+
}),
|
|
91
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class PipelinesTransfer extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
owner: import("@oclif/core/lib/interfaces/parser").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
pipeline: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
10
|
+
confirm: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const color_1 = require("@heroku-cli/color");
|
|
4
|
+
const command_1 = require("@heroku-cli/command");
|
|
5
|
+
const core_1 = require("@oclif/core");
|
|
6
|
+
const api_1 = require("../../lib/pipelines/api");
|
|
7
|
+
const disambiguate_1 = require("../../lib/pipelines/disambiguate");
|
|
8
|
+
const render_pipeline_1 = require("../../lib/pipelines/render-pipeline");
|
|
9
|
+
async function getTeamOwner(heroku, name) {
|
|
10
|
+
const { body: team } = await (0, api_1.getTeam)(heroku, name);
|
|
11
|
+
return { id: team.id, type: 'team' };
|
|
12
|
+
}
|
|
13
|
+
async function getAccountOwner(heroku, name) {
|
|
14
|
+
const { body: account } = await (0, api_1.getAccountInfo)(heroku, name);
|
|
15
|
+
return { id: account.id, type: 'user' };
|
|
16
|
+
}
|
|
17
|
+
function getOwner(heroku, name) {
|
|
18
|
+
return getTeamOwner(heroku, name)
|
|
19
|
+
.catch(() => {
|
|
20
|
+
return getAccountOwner(heroku, name);
|
|
21
|
+
})
|
|
22
|
+
.catch(() => {
|
|
23
|
+
throw new Error(`Cannot find a team or account for "${name}"`);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
class PipelinesTransfer extends command_1.Command {
|
|
27
|
+
async run() {
|
|
28
|
+
const { args, flags } = await this.parse(PipelinesTransfer);
|
|
29
|
+
const pipeline = await (0, disambiguate_1.default)(this.heroku, flags.pipeline);
|
|
30
|
+
const newOwner = await getOwner(this.heroku, args.owner);
|
|
31
|
+
const apps = await (0, api_1.listPipelineApps)(this.heroku, pipeline.id);
|
|
32
|
+
const displayType = newOwner.type === 'user' ? 'account' : newOwner.type;
|
|
33
|
+
let confirmName = flags.confirm;
|
|
34
|
+
if (!confirmName) {
|
|
35
|
+
await (0, render_pipeline_1.default)(this.heroku, pipeline, apps);
|
|
36
|
+
core_1.ux.log('');
|
|
37
|
+
core_1.ux.warn(`This will transfer ${color_1.default.pipeline(pipeline.name)} and all of the listed apps to the ${args.owner} ${displayType}`);
|
|
38
|
+
core_1.ux.warn(`to proceed, type ${color_1.default.red(pipeline.name)} or re-run this command with ${color_1.default.red('--confirm')} ${pipeline.name}`);
|
|
39
|
+
confirmName = await core_1.ux.prompt('', {});
|
|
40
|
+
}
|
|
41
|
+
if (confirmName !== pipeline.name) {
|
|
42
|
+
core_1.ux.warn(`Confirmation did not match ${color_1.default.red(pipeline.name)}. Aborted.`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
core_1.ux.action.start(`Transferring ${color_1.default.pipeline(pipeline.name)} pipeline to the ${args.owner} ${displayType}`);
|
|
46
|
+
await (0, api_1.createPipelineTransfer)(this.heroku, { pipeline: { id: pipeline.id }, new_owner: newOwner });
|
|
47
|
+
core_1.ux.action.stop();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.default = PipelinesTransfer;
|
|
51
|
+
PipelinesTransfer.description = 'transfer ownership of a pipeline';
|
|
52
|
+
PipelinesTransfer.examples = [
|
|
53
|
+
'$ heroku pipelines:transfer admin@example.com -p my-pipeline',
|
|
54
|
+
'$ heroku pipelines:transfer admin-team -p my-pipeline',
|
|
55
|
+
];
|
|
56
|
+
PipelinesTransfer.args = {
|
|
57
|
+
owner: core_1.Args.string({
|
|
58
|
+
description: 'the owner to transfer the pipeline to',
|
|
59
|
+
required: true,
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
PipelinesTransfer.flags = {
|
|
63
|
+
pipeline: command_1.flags.pipeline({ required: true }),
|
|
64
|
+
confirm: command_1.flags.string({ char: 'c' }),
|
|
65
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class PipelinesUpdate extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
app: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
7
|
+
remote: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
|
+
stage: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const color_1 = require("@heroku-cli/color");
|
|
4
|
+
const command_1 = require("@heroku-cli/command");
|
|
5
|
+
const completions_1 = require("@heroku-cli/command/lib/completions");
|
|
6
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const api_1 = require("../../lib/pipelines/api");
|
|
8
|
+
class PipelinesUpdate extends command_1.Command {
|
|
9
|
+
async run() {
|
|
10
|
+
const { flags } = await this.parse(PipelinesUpdate);
|
|
11
|
+
const app = flags.app;
|
|
12
|
+
const stage = flags.stage;
|
|
13
|
+
core_1.ux.action.start(`Changing ${color_1.default.app(app)} to ${stage}`);
|
|
14
|
+
await (0, api_1.updateCoupling)(this.heroku, app, stage);
|
|
15
|
+
core_1.ux.action.stop();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.default = PipelinesUpdate;
|
|
19
|
+
PipelinesUpdate.description = 'update the app\'s stage in a pipeline';
|
|
20
|
+
PipelinesUpdate.examples = [
|
|
21
|
+
'$ heroku pipelines:update -s staging -a my-app',
|
|
22
|
+
];
|
|
23
|
+
PipelinesUpdate.flags = {
|
|
24
|
+
app: command_1.flags.app({ required: true }),
|
|
25
|
+
remote: command_1.flags.remote(),
|
|
26
|
+
stage: command_1.flags.string({
|
|
27
|
+
char: 's',
|
|
28
|
+
description: 'new stage of app',
|
|
29
|
+
completion: completions_1.StageCompletion,
|
|
30
|
+
required: true,
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class Disable extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
app: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
6
|
+
remote: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const command_1 = require("@heroku-cli/command");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const METRICS_HOST = 'api.metrics.heroku.com';
|
|
6
|
+
class Disable extends command_1.Command {
|
|
7
|
+
async run() {
|
|
8
|
+
const { flags } = await this.parse(Disable);
|
|
9
|
+
core_1.ux.action.start('Disabling dyno autoscaling');
|
|
10
|
+
const appResponse = await this.heroku.get(`/apps/${flags.app}`);
|
|
11
|
+
const app = appResponse.body;
|
|
12
|
+
const monitorsResponse = await this.heroku.get(`/apps/${app.id}/formation/web/monitors`, {
|
|
13
|
+
hostname: METRICS_HOST,
|
|
14
|
+
});
|
|
15
|
+
const monitors = monitorsResponse.body;
|
|
16
|
+
const scaleMonitor = monitors.find((m) => m.action_type === 'scale');
|
|
17
|
+
if (!scaleMonitor)
|
|
18
|
+
throw new Error(`${flags.app} does not have autoscale enabled`);
|
|
19
|
+
await this.heroku.patch(`/apps/${app.id}/formation/web/monitors/${scaleMonitor.id}`, {
|
|
20
|
+
hostname: METRICS_HOST,
|
|
21
|
+
body: {
|
|
22
|
+
is_active: false,
|
|
23
|
+
period: 1,
|
|
24
|
+
op: 'GREATER_OR_EQUAL',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
core_1.ux.action.stop();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.default = Disable;
|
|
31
|
+
Disable.description = 'disable web dyno autoscaling';
|
|
32
|
+
Disable.flags = {
|
|
33
|
+
app: command_1.flags.app({ required: true }),
|
|
34
|
+
remote: command_1.flags.remote(),
|
|
35
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class Enable extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
app: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
6
|
+
remote: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
7
|
+
min: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
|
+
max: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
9
|
+
p95: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
10
|
+
notifications: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const command_1 = require("@heroku-cli/command");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const METRICS_HOST = 'api.metrics.heroku.com';
|
|
6
|
+
const isPerfOrPrivateTier = (size) => {
|
|
7
|
+
const applicableTiers = ['performance', 'private', 'shield'];
|
|
8
|
+
return applicableTiers.some(tier => size.toLowerCase().includes(tier));
|
|
9
|
+
};
|
|
10
|
+
class Enable extends command_1.Command {
|
|
11
|
+
async run() {
|
|
12
|
+
const { flags } = await this.parse(Enable);
|
|
13
|
+
core_1.ux.action.start('Enabling dyno autoscaling');
|
|
14
|
+
const [appResponse, formationResponse] = await Promise.all([
|
|
15
|
+
this.heroku.get(`/apps/${flags.app}`),
|
|
16
|
+
this.heroku.get(`/apps/${flags.app}/formation`),
|
|
17
|
+
]);
|
|
18
|
+
const app = appResponse.body;
|
|
19
|
+
const formations = formationResponse.body;
|
|
20
|
+
const webFormation = formations.find((f) => f.type === 'web');
|
|
21
|
+
if (!webFormation)
|
|
22
|
+
throw new Error(`${flags.app} does not have any web dynos to scale`);
|
|
23
|
+
const { size } = webFormation;
|
|
24
|
+
if (!isPerfOrPrivateTier(size || '')) {
|
|
25
|
+
throw new Error('Autoscaling is only available with Performance or Private dynos');
|
|
26
|
+
}
|
|
27
|
+
const { body } = await this.heroku.get(`/apps/${app.id}/formation/web/monitors`, {
|
|
28
|
+
hostname: METRICS_HOST,
|
|
29
|
+
});
|
|
30
|
+
const scaleMonitor = (body || []).find((m) => m.action_type === 'scale');
|
|
31
|
+
let updatedValues = {
|
|
32
|
+
is_active: true,
|
|
33
|
+
action_type: 'scale',
|
|
34
|
+
notification_period: 1440,
|
|
35
|
+
op: 'GREATER_OR_EQUAL',
|
|
36
|
+
period: 1,
|
|
37
|
+
notification_channels: flags.notifications ? ['app'] : [],
|
|
38
|
+
};
|
|
39
|
+
if (scaleMonitor) {
|
|
40
|
+
updatedValues = Object.assign(Object.assign({}, updatedValues), { min_quantity: flags.min || scaleMonitor.min_quantity, max_quantity: flags.max || scaleMonitor.max_quantity, value: flags.p95 ? flags.p95 : scaleMonitor.value });
|
|
41
|
+
await this.heroku.patch(`/apps/${app.id}/formation/web/monitors/${scaleMonitor.id}`, {
|
|
42
|
+
body: updatedValues,
|
|
43
|
+
hostname: METRICS_HOST,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
updatedValues = Object.assign(Object.assign({}, updatedValues), { name: 'LATENCY_SCALE', min_quantity: flags.min, max_quantity: flags.max, value: flags.p95 ? flags.p95 : 1000 });
|
|
48
|
+
await this.heroku.post(`/apps/${app.id}/formation/web/monitors`, {
|
|
49
|
+
hostname: METRICS_HOST,
|
|
50
|
+
body: updatedValues,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
core_1.ux.action.stop();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.default = Enable;
|
|
57
|
+
Enable.description = 'enable web dyno autoscaling';
|
|
58
|
+
Enable.flags = {
|
|
59
|
+
app: command_1.flags.app({ required: true }),
|
|
60
|
+
remote: command_1.flags.remote(),
|
|
61
|
+
min: command_1.flags.integer({ required: true, description: 'minimum number of dynos' }),
|
|
62
|
+
max: command_1.flags.integer({ required: true, description: 'maximum number of dynos' }),
|
|
63
|
+
p95: command_1.flags.integer({ description: 'desired p95 response time' }),
|
|
64
|
+
notifications: command_1.flags.boolean({ description: 'receive email notifications when the max dyno limit is reached' }),
|
|
65
|
+
};
|