heroku 9.0.0-alpha.0 → 9.0.0-alpha.1
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/README.md +4 -1
- package/lib/commands/apps/create.d.ts +28 -0
- package/lib/commands/apps/create.js +194 -0
- package/lib/commands/apps/destroy.d.ts +14 -0
- package/lib/commands/apps/destroy.js +46 -0
- package/lib/commands/apps/errors.d.ts +12 -0
- package/lib/commands/apps/errors.js +119 -0
- package/lib/commands/apps/favorites/add.d.ts +9 -0
- package/lib/commands/apps/favorites/add.js +37 -0
- package/lib/commands/apps/favorites/index.d.ts +9 -0
- package/lib/commands/apps/favorites/index.js +25 -0
- package/lib/commands/apps/favorites/remove.d.ts +9 -0
- package/lib/commands/apps/favorites/remove.js +27 -0
- package/lib/commands/apps/index.d.ts +16 -0
- package/lib/commands/apps/index.js +119 -0
- package/lib/commands/apps/info.d.ts +18 -0
- package/lib/commands/apps/info.js +163 -0
- package/lib/commands/apps/open.d.ts +14 -0
- package/lib/commands/apps/open.js +29 -0
- package/lib/commands/apps/rename.d.ts +15 -0
- package/lib/commands/apps/rename.js +50 -0
- package/lib/commands/apps/stacks/index.d.ts +10 -0
- package/lib/commands/apps/stacks/index.js +43 -0
- package/lib/commands/apps/stacks/set.d.ts +14 -0
- package/lib/commands/apps/stacks/set.js +41 -0
- package/lib/commands/auth/logout.js +1 -0
- package/lib/commands/ci/config/index.d.ts +12 -0
- package/lib/commands/ci/config/index.js +43 -0
- package/lib/commands/ci/config/set.d.ts +2 -2
- package/lib/commands/ci/config/set.js +2 -6
- package/lib/commands/ci/config/unset.d.ts +13 -0
- package/lib/commands/ci/config/unset.js +35 -0
- package/lib/commands/ci/migrate-manifest.d.ts +7 -0
- package/lib/commands/ci/migrate-manifest.js +74 -0
- package/lib/commands/config/set.d.ts +11 -0
- package/lib/commands/config/set.js +59 -0
- package/lib/commands/domains/index.d.ts +1 -1
- package/lib/commands/drains/add.d.ts +11 -0
- package/lib/commands/drains/add.js +22 -0
- package/lib/commands/drains/index.d.ts +10 -0
- package/lib/commands/drains/index.js +49 -0
- package/lib/commands/drains/remove.d.ts +12 -0
- package/lib/commands/drains/remove.js +21 -0
- package/lib/lib/api.d.ts +1 -1
- package/lib/lib/apps/confirm-app.d.ts +1 -0
- package/lib/lib/apps/confirm-app.js +23 -0
- package/lib/lib/apps/error_info.d.ts +7 -0
- package/lib/lib/apps/error_info.js +185 -0
- package/lib/lib/buildpacks/buildpacks.js +3 -4
- package/lib/lib/ci/git.d.ts +7 -1
- package/lib/lib/ci/git.js +44 -1
- package/lib/lib/ci/validate.d.ts +2 -0
- package/lib/lib/ci/validate.js +10 -0
- package/lib/lib/git/git.d.ts +3 -0
- package/lib/lib/git/git.js +19 -0
- package/lib/lib/git/push.d.ts +1 -0
- package/lib/lib/git/push.js +6 -0
- package/lib/lib/types/favorites.d.ts +7 -0
- package/lib/lib/types/favorites.js +2 -0
- package/oclif.manifest.json +708 -1
- package/package.json +15 -11
- package/lib/lib/buildpacks/push.d.ts +0 -0
- package/lib/lib/buildpacks/push.js +0 -4
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ For other issues, [submit a support ticket](https://help.heroku.com/).
|
|
|
41
41
|
* [`heroku autocomplete`](docs/autocomplete.md) - display autocomplete installation instructions
|
|
42
42
|
* [`heroku buildpacks`](docs/buildpacks.md) - scripts used to compile apps
|
|
43
43
|
* [`heroku certs`](docs/certs.md) - a topic for the ssl plugin
|
|
44
|
-
* [`heroku ci`](docs/ci.md) -
|
|
44
|
+
* [`heroku ci`](docs/ci.md) - test runner for Heroku Pipelines
|
|
45
45
|
* [`heroku clients`](docs/clients.md) - OAuth clients on the platform
|
|
46
46
|
* [`heroku config`](docs/config.md) - environment variables of apps
|
|
47
47
|
* [`heroku container`](docs/container.md) - Use containers to build and deploy Heroku apps
|
|
@@ -56,13 +56,16 @@ For other issues, [submit a support ticket](https://help.heroku.com/).
|
|
|
56
56
|
* [`heroku logs`](docs/logs.md) - display recent log output
|
|
57
57
|
* [`heroku maintenance`](docs/maintenance.md) - enable/disable access to app
|
|
58
58
|
* [`heroku members`](docs/members.md) - manage organization members
|
|
59
|
+
* [`heroku notifications`](docs/notifications.md) - display notifications
|
|
59
60
|
* [`heroku orgs`](docs/orgs.md) - manage organizations
|
|
60
61
|
* [`heroku pg`](docs/pg.md) - manage postgresql databases
|
|
61
62
|
* [`heroku pipelines`](docs/pipelines.md) - manage pipelines
|
|
62
63
|
* [`heroku plugins`](docs/plugins.md) - List installed plugins.
|
|
63
64
|
* [`heroku ps`](docs/ps.md) - Client tools for Heroku Exec
|
|
65
|
+
* [`heroku psql`](docs/psql.md) - open a psql shell to the database
|
|
64
66
|
* [`heroku redis`](docs/redis.md) - manage heroku redis instances
|
|
65
67
|
* [`heroku regions`](docs/regions.md) - list available regions for deployment
|
|
68
|
+
* [`heroku releases`](docs/releases.md) - display the releases for an app
|
|
66
69
|
* [`heroku reviewapps`](docs/reviewapps.md) - manage reviewapps in pipelines
|
|
67
70
|
* [`heroku run`](docs/run.md) - run a one-off process inside a Heroku dyno
|
|
68
71
|
* [`heroku sessions`](docs/sessions.md) - OAuth sessions
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
import { Interfaces } from '@oclif/core';
|
|
3
|
+
export default class Create extends Command {
|
|
4
|
+
static description: string;
|
|
5
|
+
static aliases: string[];
|
|
6
|
+
static examples: string[];
|
|
7
|
+
static args: {
|
|
8
|
+
apps: import("@oclif/core/lib/interfaces/parser").Arg<string | undefined, Record<string, unknown>>;
|
|
9
|
+
};
|
|
10
|
+
static flags: {
|
|
11
|
+
app: Interfaces.OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
12
|
+
addons: Interfaces.OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
13
|
+
buildpack: Interfaces.OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
14
|
+
manifest: Interfaces.BooleanFlag<boolean>;
|
|
15
|
+
'no-remote': Interfaces.BooleanFlag<boolean>;
|
|
16
|
+
remote: Interfaces.OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
17
|
+
stack: Interfaces.OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
18
|
+
space: Interfaces.OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
19
|
+
region: Interfaces.OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
20
|
+
'internal-routing': Interfaces.BooleanFlag<boolean>;
|
|
21
|
+
features: Interfaces.OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
22
|
+
kernel: Interfaces.OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
23
|
+
locked: Interfaces.BooleanFlag<boolean>;
|
|
24
|
+
json: Interfaces.BooleanFlag<boolean>;
|
|
25
|
+
team: Interfaces.OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
26
|
+
};
|
|
27
|
+
run(): Promise<void>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const js_yaml_1 = require("js-yaml");
|
|
4
|
+
const fs_extra_1 = require("fs-extra");
|
|
5
|
+
const command_1 = require("@heroku-cli/command");
|
|
6
|
+
const completions_1 = require("@heroku-cli/command/lib/completions");
|
|
7
|
+
const core_1 = require("@oclif/core");
|
|
8
|
+
const color_1 = require("@heroku-cli/color");
|
|
9
|
+
const lodash_1 = require("lodash");
|
|
10
|
+
const git_1 = require("../../lib/git/git");
|
|
11
|
+
const git = new git_1.default();
|
|
12
|
+
function createText(name, space) {
|
|
13
|
+
let text = `Creating ${name ? color_1.default.app(name) : 'app'}`;
|
|
14
|
+
if (space) {
|
|
15
|
+
text += ` in space ${space}`;
|
|
16
|
+
}
|
|
17
|
+
return text;
|
|
18
|
+
}
|
|
19
|
+
async function createApp(context, heroku, name, stack) {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
const { flags } = context;
|
|
22
|
+
const params = {
|
|
23
|
+
name,
|
|
24
|
+
team: flags.team,
|
|
25
|
+
region: flags.region,
|
|
26
|
+
space: flags.space,
|
|
27
|
+
stack,
|
|
28
|
+
internal_routing: flags['internal-routing'],
|
|
29
|
+
feature_flags: flags.features,
|
|
30
|
+
kernel: flags.kernel,
|
|
31
|
+
locked: flags.locked,
|
|
32
|
+
};
|
|
33
|
+
const requestPath = (params.space || params.team) ? '/teams/apps' : '/apps';
|
|
34
|
+
const { body: app } = await heroku.post(requestPath, {
|
|
35
|
+
body: params,
|
|
36
|
+
});
|
|
37
|
+
let status = name ? 'done' : `done, ${color_1.default.app(app.name || '')}`;
|
|
38
|
+
if (flags.region) {
|
|
39
|
+
status += `, region is ${color_1.default.yellow(((_a = app.region) === null || _a === void 0 ? void 0 : _a.name) || '')}`;
|
|
40
|
+
}
|
|
41
|
+
if (stack) {
|
|
42
|
+
status += `, stack is ${color_1.default.yellow(((_b = app.stack) === null || _b === void 0 ? void 0 : _b.name) || '')}`;
|
|
43
|
+
}
|
|
44
|
+
core_1.ux.action.stop(status);
|
|
45
|
+
return app;
|
|
46
|
+
}
|
|
47
|
+
async function addAddons(heroku, app, addons) {
|
|
48
|
+
for (const addon of addons) {
|
|
49
|
+
const body = {
|
|
50
|
+
plan: addon.plan,
|
|
51
|
+
attachment: addon.as ? { name: addon.as } : undefined,
|
|
52
|
+
};
|
|
53
|
+
core_1.ux.action.start(`Adding ${color_1.default.green(addon.plan)}`);
|
|
54
|
+
await heroku.post(`/apps/${app.name}/addons`, { body });
|
|
55
|
+
core_1.ux.action.stop();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function addConfigVars(heroku, app, configVars) {
|
|
59
|
+
if (Object.keys(configVars).length > 0) {
|
|
60
|
+
core_1.ux.action.start('Setting config vars');
|
|
61
|
+
await heroku.patch(`/apps/${app.name}/config-vars`, {
|
|
62
|
+
body: configVars,
|
|
63
|
+
});
|
|
64
|
+
core_1.ux.action.stop();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function addonsFromPlans(plans) {
|
|
68
|
+
return plans.map(plan => ({
|
|
69
|
+
plan: plan.trim(),
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
async function configureGitRemote(context, app) {
|
|
73
|
+
const remoteUrl = git.httpGitUrl(app.name || '');
|
|
74
|
+
if (!context.flags['no-remote'] && git.inGitRepo()) {
|
|
75
|
+
await git.createRemote(context.flags.remote || 'heroku', remoteUrl);
|
|
76
|
+
}
|
|
77
|
+
return remoteUrl;
|
|
78
|
+
}
|
|
79
|
+
function printAppSummary(context, app, remoteUrl) {
|
|
80
|
+
if (context.flags.json) {
|
|
81
|
+
core_1.ux.styledJSON(app);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
core_1.ux.log(`${color_1.default.cyan(app.web_url || '')} | ${color_1.default.green(remoteUrl)}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function runFromFlags(context, heroku, config) {
|
|
88
|
+
const { flags, args } = context;
|
|
89
|
+
if (flags['internal-routing'] && !flags.space) {
|
|
90
|
+
throw new Error('Space name required.\nInternal Web Apps are only available for Private Spaces.\nUSAGE: heroku apps:create --space my-space --internal-routing');
|
|
91
|
+
}
|
|
92
|
+
const name = flags.app || args.app || process.env.HEROKU_APP;
|
|
93
|
+
async function addBuildpack(app, buildpack) {
|
|
94
|
+
core_1.ux.action.start(`Setting buildpack to ${color_1.default.cyan(buildpack)}`);
|
|
95
|
+
await heroku.put(`/apps/${app.name}/buildpack-installations`, {
|
|
96
|
+
headers: { Range: '' },
|
|
97
|
+
body: { updates: [{ buildpack: buildpack }] },
|
|
98
|
+
});
|
|
99
|
+
core_1.ux.action.stop();
|
|
100
|
+
}
|
|
101
|
+
core_1.ux.action.start(createText(name, flags.space));
|
|
102
|
+
const app = await createApp(context, heroku, name, flags.stack);
|
|
103
|
+
core_1.ux.action.stop();
|
|
104
|
+
if (flags.addons) {
|
|
105
|
+
const plans = flags.addons.split(',');
|
|
106
|
+
const addons = addonsFromPlans(plans);
|
|
107
|
+
await addAddons(heroku, app, addons);
|
|
108
|
+
}
|
|
109
|
+
if (flags.buildpack) {
|
|
110
|
+
await addBuildpack(app, flags.buildpack);
|
|
111
|
+
}
|
|
112
|
+
const remoteUrl = await configureGitRemote(context, app);
|
|
113
|
+
await config.runHook('recache', { type: 'app', app: app.name });
|
|
114
|
+
printAppSummary(context, app, remoteUrl);
|
|
115
|
+
}
|
|
116
|
+
async function readManifest() {
|
|
117
|
+
const buffer = await (0, fs_extra_1.readFile)('heroku.yml');
|
|
118
|
+
return (0, js_yaml_1.load)(buffer.toString(), { filename: 'heroku.yml' });
|
|
119
|
+
}
|
|
120
|
+
async function runFromManifest(context, heroku) {
|
|
121
|
+
const { flags, args } = context;
|
|
122
|
+
const name = flags.app || args.app || process.env.HEROKU_APP;
|
|
123
|
+
core_1.ux.action.start('Reading heroku.yml manifest');
|
|
124
|
+
const manifest = await readManifest();
|
|
125
|
+
core_1.ux.action.stop();
|
|
126
|
+
core_1.ux.action.start(createText(name, flags.space));
|
|
127
|
+
const app = await createApp(context, heroku, name, 'container');
|
|
128
|
+
core_1.ux.action.stop();
|
|
129
|
+
// _.get used here to avoid type guards when working with `unknown`
|
|
130
|
+
const setup = (0, lodash_1.get)(manifest, 'setup', {});
|
|
131
|
+
const addons = setup.addons || [];
|
|
132
|
+
const configVars = setup.config || {};
|
|
133
|
+
await addAddons(heroku, app, addons);
|
|
134
|
+
await addConfigVars(heroku, app, configVars);
|
|
135
|
+
const remoteUrl = await configureGitRemote(context, app);
|
|
136
|
+
printAppSummary(context, app, remoteUrl);
|
|
137
|
+
}
|
|
138
|
+
class Create extends command_1.Command {
|
|
139
|
+
async run() {
|
|
140
|
+
const context = await this.parse(Create);
|
|
141
|
+
const { flags } = context;
|
|
142
|
+
if (this.config.channel === 'beta' && flags.manifest) {
|
|
143
|
+
return runFromManifest(context, this.heroku);
|
|
144
|
+
}
|
|
145
|
+
await runFromFlags(context, this.heroku, this.config);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.default = Create;
|
|
149
|
+
Create.description = 'creates a new app';
|
|
150
|
+
Create.aliases = ['create'];
|
|
151
|
+
Create.examples = [
|
|
152
|
+
`$ heroku apps:create
|
|
153
|
+
Creating app... done, stack is heroku-22
|
|
154
|
+
https://floating-dragon-42.heroku.com/ | https://git.heroku.com/floating-dragon-42.git
|
|
155
|
+
|
|
156
|
+
# or just
|
|
157
|
+
$ heroku create
|
|
158
|
+
|
|
159
|
+
# use a heroku.yml manifest file
|
|
160
|
+
$ heroku apps:create --manifest
|
|
161
|
+
|
|
162
|
+
# specify a buildpack
|
|
163
|
+
$ heroku apps:create --buildpack https://github.com/some/buildpack.git
|
|
164
|
+
|
|
165
|
+
# specify a name
|
|
166
|
+
$ heroku apps:create example
|
|
167
|
+
|
|
168
|
+
# create a staging app
|
|
169
|
+
$ heroku apps:create example-staging --remote staging
|
|
170
|
+
|
|
171
|
+
# create an app in the eu region
|
|
172
|
+
$ heroku apps:create --region eu`,
|
|
173
|
+
];
|
|
174
|
+
Create.args = {
|
|
175
|
+
apps: core_1.Args.string({ description: 'name of app to create', required: false }),
|
|
176
|
+
};
|
|
177
|
+
Create.flags = {
|
|
178
|
+
// `app` set to `flags.string` instead of `flags.app` to maintain original v5 functionality and avoid a default value from the git remote set when used without an app
|
|
179
|
+
app: command_1.flags.string({ hidden: true }),
|
|
180
|
+
addons: command_1.flags.string({ description: 'comma-delimited list of addons to install' }),
|
|
181
|
+
buildpack: command_1.flags.string({ char: 'b', description: 'buildpack url to use for this app', completion: completions_1.BuildpackCompletion }),
|
|
182
|
+
manifest: command_1.flags.boolean({ char: 'm', description: 'use heroku.yml settings for this app', hidden: true }),
|
|
183
|
+
'no-remote': command_1.flags.boolean({ char: 'n', description: 'do not create a git remote' }),
|
|
184
|
+
remote: command_1.flags.remote({ description: 'the git remote to create, default "heroku"', default: 'heroku' }),
|
|
185
|
+
stack: command_1.flags.string({ char: 's', description: 'the stack to create the app on', completion: completions_1.StackCompletion }),
|
|
186
|
+
space: command_1.flags.string({ description: 'the private space to create the app in', completion: completions_1.SpaceCompletion }),
|
|
187
|
+
region: command_1.flags.string({ description: 'specify region for the app to run in', completion: completions_1.RegionCompletion }),
|
|
188
|
+
'internal-routing': command_1.flags.boolean({ hidden: true, description: 'private space-only. create as an Internal Web App that is only routable in the local network.' }),
|
|
189
|
+
features: command_1.flags.string({ hidden: true }),
|
|
190
|
+
kernel: command_1.flags.string({ hidden: true }),
|
|
191
|
+
locked: command_1.flags.boolean({ hidden: true }),
|
|
192
|
+
json: command_1.flags.boolean({ description: 'output in json format' }),
|
|
193
|
+
team: command_1.flags.team(),
|
|
194
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class Destroy extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static help: string;
|
|
5
|
+
static aliases: string[];
|
|
6
|
+
static flags: {
|
|
7
|
+
app: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
|
+
confirm: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
9
|
+
};
|
|
10
|
+
static args: {
|
|
11
|
+
app: import("@oclif/core/lib/interfaces/parser").Arg<string | undefined, Record<string, unknown>>;
|
|
12
|
+
};
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
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 lodash_1 = require("lodash");
|
|
7
|
+
const confirm_app_1 = require("../../lib/apps/confirm-app");
|
|
8
|
+
const git = require("../../lib/ci/git");
|
|
9
|
+
class Destroy extends command_1.Command {
|
|
10
|
+
async run() {
|
|
11
|
+
const { flags, args } = await this.parse(Destroy);
|
|
12
|
+
const app = args.app || flags.app;
|
|
13
|
+
if (!app)
|
|
14
|
+
throw new Error('No app specified.\nUSAGE: heroku apps:destroy APPNAME');
|
|
15
|
+
// this appears to report errors if app not found
|
|
16
|
+
await this.heroku.get(`/apps/${app}`);
|
|
17
|
+
await (0, confirm_app_1.default)(app, flags.confirm, `WARNING: This will delete ${color_1.default.app(app)} including all add-ons.`);
|
|
18
|
+
core_1.ux.action.start(`Destroying ${color_1.default.app(app)} (including all add-ons)`);
|
|
19
|
+
await this.heroku.delete(`/apps/${app}`);
|
|
20
|
+
if (git.inGitRepo()) {
|
|
21
|
+
// delete git remotes pointing to this app
|
|
22
|
+
await git.listRemotes()
|
|
23
|
+
.then(remotes => {
|
|
24
|
+
const transformed = remotes
|
|
25
|
+
.filter(r => git.gitUrl(app) === r[1] || git.sshGitUrl(app) === r[1])
|
|
26
|
+
.map(r => r[0]);
|
|
27
|
+
const uniqueRemotes = (0, lodash_1.uniq)(transformed);
|
|
28
|
+
uniqueRemotes.forEach(element => {
|
|
29
|
+
git.rmRemote(element);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
core_1.ux.action.stop();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.default = Destroy;
|
|
37
|
+
Destroy.description = 'permanently destroy an app';
|
|
38
|
+
Destroy.help = 'This will also destroy all add-ons on the app.';
|
|
39
|
+
Destroy.aliases = ['destroy', 'apps:delete'];
|
|
40
|
+
Destroy.flags = {
|
|
41
|
+
app: command_1.flags.app(),
|
|
42
|
+
confirm: command_1.flags.string({ char: 'c' }),
|
|
43
|
+
};
|
|
44
|
+
Destroy.args = {
|
|
45
|
+
app: core_1.Args.string({ hidden: true }),
|
|
46
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class Errors 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
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
hours: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
|
+
router: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
dyno: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
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 lodash_1 = require("lodash");
|
|
7
|
+
const error_info_1 = require("../../lib/apps/error_info");
|
|
8
|
+
const colorize = (level, s) => {
|
|
9
|
+
switch (level) {
|
|
10
|
+
case 'critical':
|
|
11
|
+
return color_1.default.red(s);
|
|
12
|
+
case 'warning':
|
|
13
|
+
return color_1.default.yellow(s);
|
|
14
|
+
case 'info':
|
|
15
|
+
return color_1.default.cyan(s);
|
|
16
|
+
default:
|
|
17
|
+
return s;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function buildErrorTable(errors, source) {
|
|
21
|
+
return Object.keys(errors).map(name => {
|
|
22
|
+
const count = errors[name];
|
|
23
|
+
const info = error_info_1.default.find(e => e.name === name);
|
|
24
|
+
if (info) {
|
|
25
|
+
return { name, count, source, level: info.level, title: info.title };
|
|
26
|
+
}
|
|
27
|
+
return { name, count, source, level: 'critical', title: 'unknown error' };
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const sumErrors = (errors) => {
|
|
31
|
+
const summed = {};
|
|
32
|
+
Object.keys(errors.data).forEach(key => {
|
|
33
|
+
summed[key] = (0, lodash_1.sum)(errors.data[key]);
|
|
34
|
+
});
|
|
35
|
+
return summed;
|
|
36
|
+
};
|
|
37
|
+
class Errors extends command_1.Command {
|
|
38
|
+
async run() {
|
|
39
|
+
const { flags } = await this.parse(Errors);
|
|
40
|
+
const hours = Number.parseInt(flags.hours);
|
|
41
|
+
const NOW = new Date().toISOString();
|
|
42
|
+
const YESTERDAY = new Date(Date.now() - (hours * 60 * 60 * 1000)).toISOString();
|
|
43
|
+
const DATE_QUERY = `start_time=${YESTERDAY}&end_time=${NOW}&step=1h`;
|
|
44
|
+
async function getAllDynoErrors(types) {
|
|
45
|
+
const values = await Promise.all(types.map(dynoErrors));
|
|
46
|
+
const memo = {};
|
|
47
|
+
types.forEach((key, index) => {
|
|
48
|
+
memo[key] = values[index];
|
|
49
|
+
});
|
|
50
|
+
return memo;
|
|
51
|
+
}
|
|
52
|
+
const routerErrors = () => {
|
|
53
|
+
return this.heroku.get(`/apps/${flags.app}/router-metrics/errors?${DATE_QUERY}&process_type=web`, {
|
|
54
|
+
hostname: 'api.metrics.herokai.com',
|
|
55
|
+
}).then(({ body }) => sumErrors(body));
|
|
56
|
+
};
|
|
57
|
+
const dynoErrors = (type) => {
|
|
58
|
+
return this.heroku.get(`/apps/${flags.app}/formation/${type}/metrics/errors?${DATE_QUERY}`, {
|
|
59
|
+
hostname: 'api.metrics.herokai.com',
|
|
60
|
+
}).catch(error => {
|
|
61
|
+
const { http } = error;
|
|
62
|
+
// eslint-disable-next-line prefer-regex-literals
|
|
63
|
+
const match = new RegExp('^invalid process_type provided', 'i');
|
|
64
|
+
if (http && http.statusCode === 400 && http.body && http.body.message && match.test(http.body.message)) {
|
|
65
|
+
return { body: { data: {} } };
|
|
66
|
+
}
|
|
67
|
+
throw error;
|
|
68
|
+
}).then(rsp => {
|
|
69
|
+
const { body } = rsp;
|
|
70
|
+
return sumErrors(body);
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
const { body: formation } = await this.heroku.get(`/apps/${flags.app}/formation`);
|
|
74
|
+
const types = formation.map((p) => p.type);
|
|
75
|
+
const showDyno = flags.dyno || !flags.router;
|
|
76
|
+
const showRouter = flags.router || !flags.dyno;
|
|
77
|
+
const noDynoEmpty = Promise.resolve({});
|
|
78
|
+
const noRouterEmpty = Promise.resolve({});
|
|
79
|
+
const [dyno, router] = await Promise.all([
|
|
80
|
+
showDyno ? getAllDynoErrors(types) : noDynoEmpty,
|
|
81
|
+
showRouter ? routerErrors() : noRouterEmpty,
|
|
82
|
+
]);
|
|
83
|
+
const errors = {
|
|
84
|
+
dyno,
|
|
85
|
+
router,
|
|
86
|
+
};
|
|
87
|
+
if (flags.json) {
|
|
88
|
+
core_1.ux.styledJSON(errors);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
let t = buildErrorTable(errors.router, 'router');
|
|
92
|
+
for (const type of Object.keys(errors.dyno)) {
|
|
93
|
+
t = t.concat(buildErrorTable(dyno[type], type));
|
|
94
|
+
}
|
|
95
|
+
if (t.length === 0) {
|
|
96
|
+
core_1.ux.log(`No errors on ${color_1.default.app(flags.app)} in the last ${hours} hours`);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
core_1.ux.styledHeader(`Errors on ${color_1.default.app(flags.app)} in the last ${hours} hours`);
|
|
100
|
+
core_1.ux.table(t, {
|
|
101
|
+
source: {},
|
|
102
|
+
name: { get: ({ name, level }) => colorize(level, name) },
|
|
103
|
+
level: { get: ({ level }) => colorize(level, level) },
|
|
104
|
+
title: { header: 'Desc' },
|
|
105
|
+
count: {},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.default = Errors;
|
|
112
|
+
Errors.description = 'view app errors';
|
|
113
|
+
Errors.flags = {
|
|
114
|
+
app: command_1.flags.app({ required: true }),
|
|
115
|
+
json: command_1.flags.boolean({ description: 'output in json format' }),
|
|
116
|
+
hours: command_1.flags.string({ description: 'number of hours to look back (default 24)', default: '24' }),
|
|
117
|
+
router: command_1.flags.boolean({ description: 'show only router errors' }),
|
|
118
|
+
dyno: command_1.flags.boolean({ description: 'show only dyno errors' }),
|
|
119
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class Add extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static topic: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
app: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const color_1 = require("@heroku-cli/color");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const command_1 = require("@heroku-cli/command");
|
|
6
|
+
class Add extends command_1.Command {
|
|
7
|
+
async run() {
|
|
8
|
+
const { flags } = await this.parse(Add);
|
|
9
|
+
const { app } = flags;
|
|
10
|
+
core_1.ux.action.start(`Adding ${color_1.default.app(app)} to favorites`);
|
|
11
|
+
const { body: favorites } = await this.heroku.get('/favorites?type=app', { hostname: 'particleboard.heroku.com' });
|
|
12
|
+
if (favorites.find(f => f.resource_name === app)) {
|
|
13
|
+
throw new Error(`${color_1.default.app(app)} is already a favorite app.`);
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
await this.heroku.post('/favorites', {
|
|
17
|
+
hostname: 'particleboard.heroku.com',
|
|
18
|
+
body: { type: 'app', resource_id: app },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error.statusCode === 404) {
|
|
23
|
+
core_1.ux.error('App not found');
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
core_1.ux.error(error.cause);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
core_1.ux.action.stop();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.default = Add;
|
|
33
|
+
Add.description = 'favorites an app';
|
|
34
|
+
Add.topic = 'apps';
|
|
35
|
+
Add.flags = {
|
|
36
|
+
app: command_1.flags.app({ required: true }),
|
|
37
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class Index extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static topic: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const command_1 = require("@heroku-cli/command");
|
|
5
|
+
class Index extends command_1.Command {
|
|
6
|
+
async run() {
|
|
7
|
+
const { flags } = await this.parse(Index);
|
|
8
|
+
const { body: favorites } = await this.heroku.get('/favorites?type=app', { hostname: 'particleboard.heroku.com' });
|
|
9
|
+
if (flags.json) {
|
|
10
|
+
core_1.ux.styledJSON(favorites);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
core_1.ux.styledHeader('Favorited Apps');
|
|
14
|
+
for (const f of favorites) {
|
|
15
|
+
core_1.ux.log(f.resource_name);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.default = Index;
|
|
21
|
+
Index.description = 'list favorited apps';
|
|
22
|
+
Index.topic = 'apps';
|
|
23
|
+
Index.flags = {
|
|
24
|
+
json: command_1.flags.boolean({ char: 'j', description: 'output in json format' }),
|
|
25
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class Remove extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static topic: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
app: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const color_1 = require("@heroku-cli/color");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const command_1 = require("@heroku-cli/command");
|
|
6
|
+
class Remove extends command_1.Command {
|
|
7
|
+
async run() {
|
|
8
|
+
const { flags } = await this.parse(Remove);
|
|
9
|
+
const { app } = flags;
|
|
10
|
+
core_1.ux.action.start(`Removing ${color_1.default.app(app)} from favorites`);
|
|
11
|
+
const { body: favorites } = await this.heroku.get('/favorites?type=app', { hostname: 'particleboard.heroku.com' });
|
|
12
|
+
const favorite = favorites.find(f => f.resource_name === app);
|
|
13
|
+
if (!favorite) {
|
|
14
|
+
throw new Error(`${color_1.default.app(app)} is not already a favorite app.`);
|
|
15
|
+
}
|
|
16
|
+
await this.heroku.delete(`/favorites/${favorite.id}`, {
|
|
17
|
+
hostname: 'particleboard.heroku.com',
|
|
18
|
+
});
|
|
19
|
+
core_1.ux.action.stop();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.default = Remove;
|
|
23
|
+
Remove.description = 'unfavorites an app';
|
|
24
|
+
Remove.topic = 'apps';
|
|
25
|
+
Remove.flags = {
|
|
26
|
+
app: command_1.flags.app({ required: true }),
|
|
27
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class AppsIndex extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static topic: string;
|
|
5
|
+
static aliases: string[];
|
|
6
|
+
static examples: string[];
|
|
7
|
+
static flags: {
|
|
8
|
+
all: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
space: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
11
|
+
personal: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
'internal-routing': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
team: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
}
|