heroku 10.12.0-beta.0 → 10.13.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/lib/commands/container/push.js +1 -1
- package/lib/commands/status.js +150 -14
- package/lib/lib/container/docker_helper.d.ts +1 -1
- package/lib/lib/container/docker_helper.js +13 -2
- package/lib/lib/status/util.d.ts +1 -1
- package/lib/lib/status/util.js +8 -9
- package/oclif.manifest.json +542 -542
- package/package.json +5 -5
|
@@ -81,7 +81,7 @@ class Push extends command_1.Command {
|
|
|
81
81
|
else {
|
|
82
82
|
heroku_cli_util_1.hux.styledHeader(`Pushing ${job.name} (${job.dockerfile})`);
|
|
83
83
|
}
|
|
84
|
-
await DockerHelper.pushImage(job.resource);
|
|
84
|
+
await DockerHelper.pushImage(job.resource, this.config.arch);
|
|
85
85
|
}
|
|
86
86
|
const plural = jobs.length !== 1;
|
|
87
87
|
core_1.ux.log(`Your image${plural ? 's have' : ' has'} been successfully pushed. You can now release ${plural ? 'them' : 'it'} with the 'container:release' command.`);
|
package/lib/commands/status.js
CHANGED
|
@@ -7,6 +7,7 @@ const util_1 = require("@oclif/core/lib/util");
|
|
|
7
7
|
const date_fns_1 = require("date-fns");
|
|
8
8
|
const http_call_1 = require("@heroku/http-call");
|
|
9
9
|
const util_2 = require("../lib/status/util");
|
|
10
|
+
const errorMessage = 'Heroku platform status is unavailable at this time. Refer to https://status.salesforce.com/products/Heroku or try again later.';
|
|
10
11
|
const printStatus = (status) => {
|
|
11
12
|
const colorize = color_1.default[status];
|
|
12
13
|
let message = (0, util_1.capitalize)(status);
|
|
@@ -15,27 +16,162 @@ const printStatus = (status) => {
|
|
|
15
16
|
}
|
|
16
17
|
return colorize(message);
|
|
17
18
|
};
|
|
19
|
+
const getTrustStatus = async () => {
|
|
20
|
+
const trustHost = process.env.SF_TRUST_STAGING ? 'https://status-api-stg.test.edgekey.net/v1' : 'https://api.status.salesforce.com/v1';
|
|
21
|
+
const currentDateTime = new Date(Date.now()).toISOString();
|
|
22
|
+
let instances = [];
|
|
23
|
+
let activeIncidents = [];
|
|
24
|
+
let maintenances = [];
|
|
25
|
+
let localizations = [];
|
|
26
|
+
try {
|
|
27
|
+
const [instanceResponse, activeIncidentsResponse, maintenancesResponse, localizationsResponse] = await Promise.all([
|
|
28
|
+
http_call_1.default.get(`${trustHost}/instances?products=Heroku`),
|
|
29
|
+
http_call_1.default.get(`${trustHost}/incidents/active`),
|
|
30
|
+
http_call_1.default.get(`${trustHost}/maintenances?startTime=${currentDateTime}&limit=10&offset=0&product=Heroku&locale=en`),
|
|
31
|
+
http_call_1.default.get(`${trustHost}/localizations?locale=en`),
|
|
32
|
+
]);
|
|
33
|
+
instances = instanceResponse.body;
|
|
34
|
+
activeIncidents = activeIncidentsResponse.body;
|
|
35
|
+
maintenances = maintenancesResponse.body;
|
|
36
|
+
localizations = localizationsResponse.body;
|
|
37
|
+
}
|
|
38
|
+
catch (_a) {
|
|
39
|
+
core_1.ux.error(errorMessage, { exit: 1 });
|
|
40
|
+
}
|
|
41
|
+
return formatTrustResponse(instances, activeIncidents, maintenances, localizations);
|
|
42
|
+
};
|
|
43
|
+
const determineIncidentSeverity = (incidents) => {
|
|
44
|
+
const severityArray = [];
|
|
45
|
+
incidents.forEach(incident => {
|
|
46
|
+
incident.IncidentImpacts.forEach(impact => {
|
|
47
|
+
if (!impact.endTime && impact.severity) {
|
|
48
|
+
severityArray.push(impact.severity);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
if (severityArray.includes('major'))
|
|
53
|
+
return 'red';
|
|
54
|
+
if (severityArray.includes('minor'))
|
|
55
|
+
return 'yellow';
|
|
56
|
+
return 'green';
|
|
57
|
+
};
|
|
58
|
+
const formatTrustResponse = (instances, activeIncidents, maintenances, localizations) => {
|
|
59
|
+
const systemStatus = [];
|
|
60
|
+
const incidents = [];
|
|
61
|
+
const scheduled = [];
|
|
62
|
+
const instanceKeyArray = new Set(instances.map(instance => instance.key));
|
|
63
|
+
const herokuActiveIncidents = activeIncidents.filter(incident => {
|
|
64
|
+
return incident.instanceKeys.some(key => instanceKeyArray.has(key));
|
|
65
|
+
});
|
|
66
|
+
const toolsIncidents = herokuActiveIncidents.filter(incident => {
|
|
67
|
+
const tools = ['TOOLS', 'Tools', 'CLI', 'Dashboard', 'Platform API'];
|
|
68
|
+
return tools.some(tool => incident.serviceKeys.includes(tool));
|
|
69
|
+
});
|
|
70
|
+
const appsIncidents = herokuActiveIncidents.filter(incident => {
|
|
71
|
+
return incident.serviceKeys.includes('HerokuApps') || incident.serviceKeys.includes('Apps');
|
|
72
|
+
});
|
|
73
|
+
const dataIncidents = herokuActiveIncidents.filter(incident => {
|
|
74
|
+
return incident.serviceKeys.includes('HerokuData') || incident.serviceKeys.includes('Data');
|
|
75
|
+
});
|
|
76
|
+
if (appsIncidents.length > 0) {
|
|
77
|
+
const severity = determineIncidentSeverity(appsIncidents);
|
|
78
|
+
systemStatus.push({ system: 'Apps', status: severity });
|
|
79
|
+
incidents.push(...appsIncidents);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
systemStatus.push({ system: 'Apps', status: 'green' });
|
|
83
|
+
}
|
|
84
|
+
if (dataIncidents.length > 0) {
|
|
85
|
+
const severity = determineIncidentSeverity(dataIncidents);
|
|
86
|
+
systemStatus.push({ system: 'Data', status: severity });
|
|
87
|
+
incidents.push(...dataIncidents);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
systemStatus.push({ system: 'Data', status: 'green' });
|
|
91
|
+
}
|
|
92
|
+
if (toolsIncidents.length > 0) {
|
|
93
|
+
const severity = determineIncidentSeverity(toolsIncidents);
|
|
94
|
+
systemStatus.push({ system: 'Tools', status: severity });
|
|
95
|
+
incidents.push(...toolsIncidents);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
systemStatus.push({ system: 'Tools', status: 'green' });
|
|
99
|
+
}
|
|
100
|
+
if (maintenances.length > 0)
|
|
101
|
+
scheduled.push(...maintenances);
|
|
102
|
+
if (incidents.length > 0) {
|
|
103
|
+
incidents.forEach(incident => {
|
|
104
|
+
incident.IncidentEvents.forEach(event => {
|
|
105
|
+
var _a;
|
|
106
|
+
event.localizedType = (_a = localizations.find((l) => l.modelKey === event.type)) === null || _a === void 0 ? void 0 : _a.text;
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
status: systemStatus,
|
|
112
|
+
incidents,
|
|
113
|
+
scheduled,
|
|
114
|
+
};
|
|
115
|
+
};
|
|
18
116
|
class Status extends core_1.Command {
|
|
19
117
|
async run() {
|
|
118
|
+
var _a;
|
|
20
119
|
const { flags } = await this.parse(Status);
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
120
|
+
const herokuApiPath = '/api/v4/current-status';
|
|
121
|
+
let herokuStatus;
|
|
122
|
+
let formattedTrustStatus;
|
|
123
|
+
if (process.env.TRUST_ONLY) {
|
|
124
|
+
formattedTrustStatus = await getTrustStatus();
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
try {
|
|
128
|
+
// Try calling the Heroku status API first
|
|
129
|
+
const herokuHost = process.env.HEROKU_STATUS_HOST || 'https://status.heroku.com';
|
|
130
|
+
const herokuStatusResponse = await http_call_1.default.get(herokuHost + herokuApiPath);
|
|
131
|
+
herokuStatus = herokuStatusResponse.body;
|
|
132
|
+
}
|
|
133
|
+
catch (_b) {
|
|
134
|
+
// If the Heroku status API call fails, call the SF Trust API
|
|
135
|
+
formattedTrustStatus = await getTrustStatus();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!herokuStatus && !formattedTrustStatus)
|
|
139
|
+
core_1.ux.error(errorMessage, { exit: 1 });
|
|
24
140
|
if (flags.json) {
|
|
25
|
-
heroku_cli_util_1.hux.styledJSON(
|
|
141
|
+
heroku_cli_util_1.hux.styledJSON(herokuStatus !== null && herokuStatus !== void 0 ? herokuStatus : formattedTrustStatus);
|
|
26
142
|
return;
|
|
27
143
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
144
|
+
const systemStatus = herokuStatus ? herokuStatus.status : formattedTrustStatus === null || formattedTrustStatus === void 0 ? void 0 : formattedTrustStatus.status;
|
|
145
|
+
if (systemStatus) {
|
|
146
|
+
for (const item of systemStatus) {
|
|
147
|
+
const message = printStatus(item.status);
|
|
148
|
+
this.log(`${(item.system + ':').padEnd(11)}${message}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
core_1.ux.error(errorMessage, { exit: 1 });
|
|
153
|
+
}
|
|
154
|
+
if (herokuStatus) {
|
|
155
|
+
for (const incident of herokuStatus.incidents) {
|
|
156
|
+
core_1.ux.log();
|
|
157
|
+
heroku_cli_util_1.hux.styledHeader(`${incident.title} ${color_1.default.yellow(incident.created_at)} ${color_1.default.cyan(incident.full_url)}`);
|
|
158
|
+
const padding = (0, util_2.getMaxUpdateTypeLength)(incident.updates.map(update => update.update_type));
|
|
159
|
+
for (const u of incident.updates) {
|
|
160
|
+
core_1.ux.log(`${color_1.default.yellow(u.update_type.padEnd(padding))} ${new Date(u.updated_at).toISOString()} (${(0, date_fns_1.formatDistanceToNow)(new Date(u.updated_at))} ago)`);
|
|
161
|
+
core_1.ux.log(`${u.contents}\n`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
31
164
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
165
|
+
else if (formattedTrustStatus) {
|
|
166
|
+
for (const incident of formattedTrustStatus.incidents) {
|
|
167
|
+
core_1.ux.log();
|
|
168
|
+
heroku_cli_util_1.hux.styledHeader(`${incident.id} ${color_1.default.yellow(incident.createdAt)} ${color_1.default.cyan(`https://status.salesforce.com/incidents/${incident.id}`)}`);
|
|
169
|
+
const padding = (0, util_2.getMaxUpdateTypeLength)(incident.IncidentEvents.map(event => { var _a; return (_a = event.localizedType) !== null && _a !== void 0 ? _a : event.type; }));
|
|
170
|
+
for (const event of incident.IncidentEvents) {
|
|
171
|
+
const eventType = (_a = event.localizedType) !== null && _a !== void 0 ? _a : event.type;
|
|
172
|
+
core_1.ux.log(`${color_1.default.yellow(eventType.padEnd(padding))} ${new Date(event.createdAt).toISOString()} (${(0, date_fns_1.formatDistanceToNow)(new Date(event.createdAt))} ago)`);
|
|
173
|
+
core_1.ux.log(`${event.message}\n`);
|
|
174
|
+
}
|
|
39
175
|
}
|
|
40
176
|
}
|
|
41
177
|
}
|
|
@@ -27,6 +27,6 @@ declare type BuildImageParams = {
|
|
|
27
27
|
arch?: string;
|
|
28
28
|
};
|
|
29
29
|
export declare const buildImage: ({ dockerfile, resource, buildArgs, path, arch }: BuildImageParams) => Promise<string>;
|
|
30
|
-
export declare const pushImage: (resource: string) => Promise<string>;
|
|
30
|
+
export declare const pushImage: (resource: string, arch: string) => Promise<string>;
|
|
31
31
|
export declare const runImage: (resource: string, command: string, port: number) => Promise<string>;
|
|
32
32
|
export {};
|
|
@@ -145,8 +145,14 @@ const buildImage = async function ({ dockerfile, resource, buildArgs, path, arch
|
|
|
145
145
|
const args = ['build', '-f', dockerfile, '-t', resource];
|
|
146
146
|
// Older Docker versions don't allow for this flag, but we are
|
|
147
147
|
// adding it here when necessary to allow for pushing a docker build from m1/m2 Macs.
|
|
148
|
-
if (arch === 'arm64')
|
|
148
|
+
if (arch === 'arm64' || arch === 'aarch64')
|
|
149
149
|
args.push('--platform', 'linux/amd64');
|
|
150
|
+
// newer docker versions support attestations and software bill of materials, so we want to disable them to save time/space
|
|
151
|
+
// Heroku's container registry doesn't support pushing them right now
|
|
152
|
+
if (await (0, exports.version)() >= [24, 0, 0]) {
|
|
153
|
+
args.push('--provenance', 'false');
|
|
154
|
+
args.push('--sbom', 'false');
|
|
155
|
+
}
|
|
150
156
|
for (const element of buildArgs) {
|
|
151
157
|
if (element.length > 0) {
|
|
152
158
|
args.push('--build-arg', element);
|
|
@@ -156,8 +162,13 @@ const buildImage = async function ({ dockerfile, resource, buildArgs, path, arch
|
|
|
156
162
|
return (0, exports.cmd)('docker', args);
|
|
157
163
|
};
|
|
158
164
|
exports.buildImage = buildImage;
|
|
159
|
-
const pushImage = async function (resource) {
|
|
165
|
+
const pushImage = async function (resource, arch) {
|
|
160
166
|
const args = ['push', resource];
|
|
167
|
+
// Older Docker versions don't allow for this flag, but we are
|
|
168
|
+
// adding it here when necessary to allow for pushing a docker build from m1/m2 Macs.
|
|
169
|
+
// Heroku's container registry doesn't support pushing multi-arch images so we need to push the expected arch
|
|
170
|
+
if (arch === 'arm64' || arch === 'aarch64')
|
|
171
|
+
args.push('--platform', 'linux/amd64');
|
|
161
172
|
return (0, exports.cmd)('docker', args);
|
|
162
173
|
};
|
|
163
174
|
exports.pushImage = pushImage;
|
package/lib/lib/status/util.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function
|
|
1
|
+
export declare function getMaxUpdateTypeLength(updateTypes: string[]): number;
|
package/lib/lib/status/util.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
function
|
|
5
|
-
let max;
|
|
6
|
-
for (const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
max = { i, element: cur };
|
|
3
|
+
exports.getMaxUpdateTypeLength = void 0;
|
|
4
|
+
function getMaxUpdateTypeLength(updateTypes) {
|
|
5
|
+
let max = 0;
|
|
6
|
+
for (const update of updateTypes) {
|
|
7
|
+
if (!max || update.length > max) {
|
|
8
|
+
max = update.length;
|
|
10
9
|
}
|
|
11
10
|
}
|
|
12
|
-
return max
|
|
11
|
+
return max;
|
|
13
12
|
}
|
|
14
|
-
exports.
|
|
13
|
+
exports.getMaxUpdateTypeLength = getMaxUpdateTypeLength;
|