heroku 10.2.0 → 10.3.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/README.md +1 -1
- package/lib/commands/buildpacks/index.js +12 -3
- package/lib/commands/pg/outliers.js +21 -20
- package/lib/commands/pipelines/create.js +5 -3
- package/lib/commands/redis/credentials.js +1 -1
- package/lib/commands/redis/stats-reset.js +1 -1
- package/lib/commands/redis/wait.js +1 -1
- package/lib/commands/spaces/create.js +1 -1
- package/lib/commands/telemetry/index.js +1 -1
- package/lib/lib/api.d.ts +2 -1
- package/lib/lib/api.js +2 -2
- package/lib/lib/redis/api.d.ts +1 -1
- package/lib/lib/redis/api.js +2 -2
- package/oclif.manifest.json +998 -997
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ Run all tests with `yarn test`.
|
|
|
96
96
|
|
|
97
97
|
## Debugging
|
|
98
98
|
|
|
99
|
-
Using WebStorm (from
|
|
99
|
+
Using WebStorm (from JetBrains / IntelliJ), you can run/debug an individual test case.
|
|
100
100
|
|
|
101
101
|
- Create a new run/debug configuration
|
|
102
102
|
- Select the 'Mocha' type
|
|
@@ -14,18 +14,27 @@ class Index extends command_1.Command {
|
|
|
14
14
|
Accept: 'application/vnd.heroku+json; version=3.sdk',
|
|
15
15
|
},
|
|
16
16
|
});
|
|
17
|
-
const
|
|
17
|
+
const isFirApp = (0, generation_1.getGeneration)(app) === 'fir';
|
|
18
|
+
const buildpacks = await buildpacksCommand.fetch(flags.app, isFirApp);
|
|
18
19
|
if (buildpacks.length === 0) {
|
|
19
20
|
this.log(`${color_1.default.app(flags.app)} has no Buildpacks.`);
|
|
20
21
|
}
|
|
21
22
|
else {
|
|
22
|
-
|
|
23
|
+
const pluralizedBuildpacks = buildpacks.length > 1 ? 'Buildpacks' : 'Buildpack';
|
|
24
|
+
let header = `${color_1.default.app(flags.app)}`;
|
|
25
|
+
if (isFirApp) {
|
|
26
|
+
header += ` Cloud Native ${pluralizedBuildpacks} (from the latest release's OCI image)`;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
header += ` Classic ${pluralizedBuildpacks} (from the Heroku Buildpack Registry)`;
|
|
30
|
+
}
|
|
31
|
+
core_1.ux.styledHeader(header);
|
|
23
32
|
buildpacksCommand.display(buildpacks, '');
|
|
24
33
|
}
|
|
25
34
|
}
|
|
26
35
|
}
|
|
27
36
|
exports.default = Index;
|
|
28
|
-
Index.description = '
|
|
37
|
+
Index.description = 'list the buildpacks on an app';
|
|
29
38
|
Index.flags = {
|
|
30
39
|
app: command_1.flags.app({ required: true }),
|
|
31
40
|
remote: command_1.flags.remote(),
|
|
@@ -51,36 +51,37 @@ class Outliers extends command_1.Command {
|
|
|
51
51
|
const truncatedQueryString = truncate ? (0, tsheredoc_1.default) `
|
|
52
52
|
CASE WHEN length(query) <= 40 THEN query ELSE substr(query, 0, 39) || '…' END
|
|
53
53
|
` : 'query';
|
|
54
|
+
let totalExecTimeField = '';
|
|
54
55
|
if (version && Number.parseInt(version, 10) >= 13) {
|
|
55
|
-
|
|
56
|
+
totalExecTimeField = 'total_exec_time';
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
totalExecTimeField = 'total_time';
|
|
60
|
+
}
|
|
61
|
+
let blkReadTimeField = '';
|
|
62
|
+
let blkWriteTimeField = '';
|
|
63
|
+
if (version && Number.parseInt(version, 10) >= 17) {
|
|
64
|
+
blkReadTimeField = 'shared_blk_read_time';
|
|
65
|
+
blkWriteTimeField = 'shared_blk_write_time';
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
blkReadTimeField = 'blk_read_time';
|
|
69
|
+
blkWriteTimeField = 'blk_write_time';
|
|
70
|
+
}
|
|
71
|
+
return (0, tsheredoc_1.default) `
|
|
56
72
|
SELECT
|
|
57
|
-
interval '1 millisecond' *
|
|
58
|
-
to_char((
|
|
73
|
+
interval '1 millisecond' * ${totalExecTimeField} AS total_exec_time,
|
|
74
|
+
to_char((${totalExecTimeField}/sum(${totalExecTimeField}) OVER()) * 100, 'FM90D0') || '%' AS prop_exec_time,
|
|
59
75
|
to_char(calls, 'FM999G999G999G990') AS ncalls,
|
|
60
|
-
interval '1 millisecond' * (
|
|
76
|
+
interval '1 millisecond' * (${blkReadTimeField} + ${blkWriteTimeField}) AS sync_io_time,
|
|
61
77
|
${truncatedQueryString} AS query
|
|
62
78
|
FROM pg_stat_statements
|
|
63
79
|
WHERE userid = (
|
|
64
80
|
SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1
|
|
65
81
|
)
|
|
66
|
-
ORDER BY
|
|
82
|
+
ORDER BY ${totalExecTimeField} DESC
|
|
67
83
|
LIMIT ${limit};
|
|
68
84
|
`;
|
|
69
|
-
}
|
|
70
|
-
return (0, tsheredoc_1.default) `
|
|
71
|
-
SELECT
|
|
72
|
-
interval '1 millisecond' * total_time AS total_exec_time,
|
|
73
|
-
to_char((total_time/sum(total_time) OVER()) * 100, 'FM90D0') || '%' AS prop_exec_time,
|
|
74
|
-
to_char(calls, 'FM999G999G999G990') AS ncalls,
|
|
75
|
-
interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time,
|
|
76
|
-
${truncatedQueryString} AS query
|
|
77
|
-
FROM pg_stat_statements
|
|
78
|
-
WHERE userid = (
|
|
79
|
-
SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1
|
|
80
|
-
)
|
|
81
|
-
ORDER BY total_time DESC
|
|
82
|
-
LIMIT ${limit};
|
|
83
|
-
`;
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
exports.default = Outliers;
|
|
@@ -8,6 +8,7 @@ const inquirer_1 = require("inquirer");
|
|
|
8
8
|
const api_1 = require("../../lib/api");
|
|
9
9
|
const infer_1 = require("../../lib/pipelines/infer");
|
|
10
10
|
const stages_1 = require("../../lib/pipelines/stages");
|
|
11
|
+
const generation_1 = require("../../lib/apps/generation");
|
|
11
12
|
class Create extends command_1.Command {
|
|
12
13
|
async run() {
|
|
13
14
|
const { args, flags } = await this.parse(Create);
|
|
@@ -43,8 +44,8 @@ class Create extends command_1.Command {
|
|
|
43
44
|
const teamName = flags.team;
|
|
44
45
|
const ownerType = teamName ? 'team' : 'user';
|
|
45
46
|
// If team or org is not specified, we assign ownership to the user creating
|
|
46
|
-
|
|
47
|
-
owner =
|
|
47
|
+
const response = teamName ? await (0, api_1.getTeam)(this.heroku, teamName) : await (0, api_1.getAccountInfo)(this.heroku);
|
|
48
|
+
owner = response.body;
|
|
48
49
|
const ownerID = owner.id;
|
|
49
50
|
owner = { id: ownerID, type: ownerType };
|
|
50
51
|
const answers = await (0, inquirer_1.prompt)(questions);
|
|
@@ -53,7 +54,8 @@ class Create extends command_1.Command {
|
|
|
53
54
|
if (answers.stage)
|
|
54
55
|
stage = answers.stage;
|
|
55
56
|
core_1.ux.action.start(`Creating ${name} pipeline`);
|
|
56
|
-
const
|
|
57
|
+
const generation = await (0, generation_1.getGenerationByAppId)(app, this.heroku);
|
|
58
|
+
const { body: pipeline } = await (0, api_1.createPipeline)(this.heroku, name, owner, generation);
|
|
57
59
|
core_1.ux.action.stop();
|
|
58
60
|
core_1.ux.action.start(`Adding ${color_1.default.app(app)} to ${color_1.default.pipeline(pipeline.name)} pipeline as ${stage}`);
|
|
59
61
|
await (0, api_1.createCoupling)(this.heroku, pipeline, app, stage);
|
|
@@ -11,7 +11,7 @@ class Credentials extends command_1.Command {
|
|
|
11
11
|
const addon = await (0, api_1.default)(app, database, false, this.heroku).getRedisAddon();
|
|
12
12
|
if (reset) {
|
|
13
13
|
core_1.ux.log(`Resetting credentials for ${addon.name}`);
|
|
14
|
-
await (0, api_1.default)(app, database, false, this.heroku).request(`/redis/v0/databases/${addon.name}/credentials_rotation`, 'POST');
|
|
14
|
+
await (0, api_1.default)(app, database, false, this.heroku).request(`/redis/v0/databases/${addon.name}/credentials_rotation`, 'POST', {});
|
|
15
15
|
}
|
|
16
16
|
else {
|
|
17
17
|
const { body: redis } = await (0, api_1.default)(app, database, false, this.heroku).request(`/redis/v0/databases/${addon.name}`);
|
|
@@ -19,7 +19,7 @@ class StatsReset extends command_1.Command {
|
|
|
19
19
|
await (0, confirmCommand_1.default)(app, confirm, warning);
|
|
20
20
|
core_1.ux.action.start(`Resetting stats on ${color_1.default.addon(addon.name || '')}`);
|
|
21
21
|
const { body: response } = await (0, api_1.default)(app, database, false, this.heroku)
|
|
22
|
-
.request(`/redis/v0/databases/${addon.id}/stats/reset`, 'POST');
|
|
22
|
+
.request(`/redis/v0/databases/${addon.id}/stats/reset`, 'POST', {});
|
|
23
23
|
core_1.ux.action.stop(response.message);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -22,7 +22,7 @@ class Wait extends command_1.Command {
|
|
|
22
22
|
let waiting = false;
|
|
23
23
|
while (true) {
|
|
24
24
|
try {
|
|
25
|
-
status = await api.request(`/redis/v0/databases/${addon.name}/wait
|
|
25
|
+
status = await api.request(`/redis/v0/databases/${addon.name}/wait`).then(response => response.body);
|
|
26
26
|
}
|
|
27
27
|
catch (error) {
|
|
28
28
|
const httpError = error;
|
|
@@ -56,7 +56,7 @@ class Create extends command_1.Command {
|
|
|
56
56
|
},
|
|
57
57
|
});
|
|
58
58
|
core_1.ux.action.stop();
|
|
59
|
-
core_1.ux.warn(`${color_1.default.bold('Spend Alert.')}
|
|
59
|
+
core_1.ux.warn(`${color_1.default.bold('Spend Alert.')} Each Heroku ${spaceType} Private Space costs ~${dollarAmountHourly}/hour (max ${dollarAmountMonthly}/month), pro-rated to the second.`);
|
|
60
60
|
core_1.ux.warn(`Use ${color_1.default.cmd('heroku spaces:wait')} to track allocation.`);
|
|
61
61
|
core_1.ux.styledHeader(space.name);
|
|
62
62
|
core_1.ux.styledObject({
|
|
@@ -44,6 +44,6 @@ Index.topic = 'telemetry';
|
|
|
44
44
|
Index.description = 'list telemetry drains';
|
|
45
45
|
Index.flags = {
|
|
46
46
|
space: command_1.flags.string({ char: 's', description: 'filter by space name', exactlyOne: ['app', 'space'] }),
|
|
47
|
-
app: command_1.flags.string({ description: 'filter by app name' }),
|
|
47
|
+
app: command_1.flags.string({ char: 'a', description: 'filter by app name' }),
|
|
48
48
|
};
|
|
49
49
|
Index.example = '$ heroku telemetry';
|
package/lib/lib/api.d.ts
CHANGED
|
@@ -5,12 +5,13 @@ export declare const V3_HEADER = "application/vnd.heroku+json; version=3";
|
|
|
5
5
|
export declare const SDK_HEADER = "application/vnd.heroku+json; version=3.sdk";
|
|
6
6
|
export declare const FILTERS_HEADER: string;
|
|
7
7
|
export declare const PIPELINES_HEADER: string;
|
|
8
|
+
export declare type Owner = Pick<Heroku.Account, 'id' | 'type'> | Pick<Heroku.Team, 'id' | 'type'>;
|
|
8
9
|
export declare function createAppSetup(heroku: APIClient, body: {
|
|
9
10
|
body: any;
|
|
10
11
|
}): Promise<import("@heroku/http-call").HTTP<Heroku.AppSetup>>;
|
|
11
12
|
export declare function postCoupling(heroku: APIClient, pipeline: any, app: any, stage: string): Promise<import("@heroku/http-call").HTTP<unknown>>;
|
|
12
13
|
export declare function createCoupling(heroku: APIClient, pipeline: any, app: string, stage: string): Promise<import("@heroku/http-call").HTTP<unknown>>;
|
|
13
|
-
export declare function createPipeline(heroku: APIClient, name:
|
|
14
|
+
export declare function createPipeline(heroku: APIClient, name: string, owner: Owner, generationName?: string): Promise<import("@heroku/http-call").HTTP<Heroku.Pipeline>>;
|
|
14
15
|
export declare function createPipelineTransfer(heroku: APIClient, pipeline: Heroku.Pipeline): Promise<import("@heroku/http-call").HTTP<unknown>>;
|
|
15
16
|
export declare function destroyPipeline(heroku: APIClient, name: any, pipelineId: any): Promise<import("@heroku/http-call").HTTP<unknown>>;
|
|
16
17
|
export declare function findPipelineByName(heroku: APIClient, idOrName: string): Promise<import("@heroku/http-call").HTTP<Heroku.Pipeline[]>>;
|
package/lib/lib/api.js
CHANGED
|
@@ -20,11 +20,11 @@ function createCoupling(heroku, pipeline, app, stage) {
|
|
|
20
20
|
return postCoupling(heroku, pipeline.id, app, stage);
|
|
21
21
|
}
|
|
22
22
|
exports.createCoupling = createCoupling;
|
|
23
|
-
function createPipeline(heroku, name, owner) {
|
|
23
|
+
function createPipeline(heroku, name, owner, generationName = 'cedar') {
|
|
24
24
|
return heroku.request('/pipelines', {
|
|
25
25
|
method: 'POST',
|
|
26
26
|
headers: { Accept: exports.PIPELINES_HEADER },
|
|
27
|
-
body: { name, owner },
|
|
27
|
+
body: { name, owner, generation: { name: generationName } },
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
exports.createPipeline = createPipeline;
|
package/lib/lib/redis/api.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export declare type RedisFormationWaitResponse = {
|
|
|
57
57
|
};
|
|
58
58
|
declare type HttpVerb = 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'PUT';
|
|
59
59
|
declare const _default: (app: string, database: string | undefined, json: boolean, heroku: APIClient) => {
|
|
60
|
-
request<T>(path: string, method?: HttpVerb, body?:
|
|
60
|
+
request<T>(path: string, method?: HttpVerb, body?: unknown): Promise<import("@heroku/http-call").HTTP<T>>;
|
|
61
61
|
makeAddonsFilter(filter: string | undefined): (addons: Required<Heroku.AddOn>[]) => Required<Heroku.AddOn>[];
|
|
62
62
|
getRedisAddon(addons?: Required<Heroku.AddOn>[]): Promise<Required<Heroku.AddOn>>;
|
|
63
63
|
info(): Promise<void>;
|
package/lib/lib/redis/api.js
CHANGED
|
@@ -6,8 +6,8 @@ exports.default = (app, database, json, heroku) => {
|
|
|
6
6
|
const HOST = process.env.HEROKU_REDIS_HOST || 'api.data.heroku.com';
|
|
7
7
|
const ADDON = process.env.HEROKU_REDIS_ADDON_NAME || 'heroku-redis';
|
|
8
8
|
return {
|
|
9
|
-
request(path, method = 'GET', body =
|
|
10
|
-
const headers = {
|
|
9
|
+
request(path, method = 'GET', body = null) {
|
|
10
|
+
const headers = {};
|
|
11
11
|
if (process.env.HEROKU_HEADERS) {
|
|
12
12
|
Object.assign(headers, JSON.parse(process.env.HEROKU_HEADERS));
|
|
13
13
|
}
|