eas-cli 2.6.0 → 2.7.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 +101 -50
- package/build/branch/queries.d.ts +8 -1
- package/build/branch/queries.js +50 -1
- package/build/build/android/build.js +1 -0
- package/build/build/ios/build.js +1 -0
- package/build/build/local.js +1 -1
- package/build/build/validate.d.ts +1 -0
- package/build/build/validate.js +121 -1
- package/build/channel/queries.d.ts +11 -0
- package/build/channel/queries.js +46 -1
- package/build/commandUtils/context/contextUtils/getProjectIdAsync.js +1 -1
- package/build/commands/branch/create.d.ts +0 -3
- package/build/commands/branch/create.js +2 -27
- package/build/commands/channel/create.d.ts +0 -7
- package/build/commands/channel/create.js +4 -31
- package/build/commands/credentials.d.ts +1 -0
- package/build/commands/credentials.js +3 -2
- package/build/commands/update/configure.d.ts +1 -0
- package/build/commands/update/configure.js +10 -218
- package/build/commands/update/index.d.ts +3 -9
- package/build/commands/update/index.js +136 -143
- package/build/credentials/manager/HelperActions.d.ts +2 -0
- package/build/credentials/manager/ManageAndroid.js +8 -1
- package/build/credentials/manager/ManageIos.js +8 -1
- package/build/credentials/manager/SelectPlatform.d.ts +3 -1
- package/build/credentials/manager/SelectPlatform.js +2 -1
- package/build/graphql/generated.d.ts +26 -17
- package/build/graphql/types/Build.js +1 -0
- package/build/log.d.ts +1 -0
- package/build/log.js +3 -0
- package/build/project/projectUtils.d.ts +3 -1
- package/build/project/projectUtils.js +10 -3
- package/build/project/publish.d.ts +13 -10
- package/build/project/publish.js +68 -38
- package/build/project/workflow.d.ts +1 -0
- package/build/project/workflow.js +9 -1
- package/build/prompts.js +3 -1
- package/build/submit/ArchiveSource.js +12 -16
- package/build/update/configure.d.ts +22 -0
- package/build/update/configure.js +200 -0
- package/build/utils/expoCli.d.ts +6 -0
- package/build/utils/expoCli.js +46 -1
- package/build/utils/expodash/memoize.d.ts +2 -0
- package/build/utils/expodash/memoize.js +17 -0
- package/build/utils/image.d.ts +6 -0
- package/build/utils/image.js +107 -0
- package/build/utils/statuspageService.js +1 -0
- package/oclif.manifest.json +1 -1
- package/package.json +8 -4
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureEASUpdatesIsConfiguredAsync = exports.DEFAULT_BARE_RUNTIME_VERSION = exports.DEFAULT_MANAGED_RUNTIME_VERSION = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const config_1 = require("@expo/config");
|
|
6
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
7
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
8
|
+
const api_1 = require("../api");
|
|
9
|
+
const generated_1 = require("../graphql/generated");
|
|
10
|
+
const log_1 = tslib_1.__importStar(require("../log"));
|
|
11
|
+
const platform_1 = require("../platform");
|
|
12
|
+
const projectUtils_1 = require("../project/projectUtils");
|
|
13
|
+
const workflow_1 = require("../project/workflow");
|
|
14
|
+
const UpdatesModule_1 = require("./android/UpdatesModule");
|
|
15
|
+
const UpdatesModule_2 = require("./ios/UpdatesModule");
|
|
16
|
+
exports.DEFAULT_MANAGED_RUNTIME_VERSION = { policy: 'sdkVersion' };
|
|
17
|
+
exports.DEFAULT_BARE_RUNTIME_VERSION = '1.0.0';
|
|
18
|
+
function getDefaultRuntimeVersion(workflow) {
|
|
19
|
+
return workflow === eas_build_job_1.Workflow.GENERIC
|
|
20
|
+
? exports.DEFAULT_BARE_RUNTIME_VERSION
|
|
21
|
+
: exports.DEFAULT_MANAGED_RUNTIME_VERSION;
|
|
22
|
+
}
|
|
23
|
+
function isRuntimeEqual(runtimeVersionA, runtimeVersionB) {
|
|
24
|
+
if (typeof runtimeVersionA === 'string' && typeof runtimeVersionB === 'string') {
|
|
25
|
+
return runtimeVersionA === runtimeVersionB;
|
|
26
|
+
}
|
|
27
|
+
else if (typeof runtimeVersionA === 'object' && typeof runtimeVersionB === 'object') {
|
|
28
|
+
return runtimeVersionA.policy === runtimeVersionB.policy;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function replaceUndefinedObjectValues(value, replacement) {
|
|
35
|
+
for (const key in value) {
|
|
36
|
+
if (value[key] === undefined) {
|
|
37
|
+
value[key] = replacement;
|
|
38
|
+
}
|
|
39
|
+
else if (typeof value[key] === 'object') {
|
|
40
|
+
value[key] = replaceUndefinedObjectValues(value[key], replacement);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Partially merge the EAS Update config with the existing Expo config.
|
|
47
|
+
* This preserves and merges the nested update-related properties.
|
|
48
|
+
*/
|
|
49
|
+
function mergeExpoConfig(exp, modifyExp) {
|
|
50
|
+
var _a;
|
|
51
|
+
return {
|
|
52
|
+
runtimeVersion: (_a = modifyExp.runtimeVersion) !== null && _a !== void 0 ? _a : exp.runtimeVersion,
|
|
53
|
+
updates: { ...exp.updates, ...modifyExp.updates },
|
|
54
|
+
android: {
|
|
55
|
+
...exp.android,
|
|
56
|
+
...modifyExp.android,
|
|
57
|
+
},
|
|
58
|
+
ios: {
|
|
59
|
+
...exp.ios,
|
|
60
|
+
...modifyExp.ios,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Make sure the `app.json` is configured to use EAS Updates.
|
|
66
|
+
* This does a couple of things:
|
|
67
|
+
* - Ensure update URL is set to the project EAS endpoint
|
|
68
|
+
* - Ensure runtimeVersion is defined for both or individual platforms
|
|
69
|
+
* - Output the changes made, or the changes required to make manually
|
|
70
|
+
*/
|
|
71
|
+
async function ensureEASUpdatesIsConfiguredInExpoConfigAsync({ exp, projectId, projectDir, platform, workflows, }) {
|
|
72
|
+
var _a, _b, _c, _d, _e, _f;
|
|
73
|
+
const modifyConfig = {};
|
|
74
|
+
if (((_a = exp.updates) === null || _a === void 0 ? void 0 : _a.url) !== (0, api_1.getEASUpdateURL)(projectId)) {
|
|
75
|
+
modifyConfig.updates = { url: (0, api_1.getEASUpdateURL)(projectId) };
|
|
76
|
+
}
|
|
77
|
+
let androidRuntimeVersion = (_c = (_b = exp.android) === null || _b === void 0 ? void 0 : _b.runtimeVersion) !== null && _c !== void 0 ? _c : exp.runtimeVersion;
|
|
78
|
+
let iosRuntimeVersion = (_e = (_d = exp.ios) === null || _d === void 0 ? void 0 : _d.runtimeVersion) !== null && _e !== void 0 ? _e : exp.runtimeVersion;
|
|
79
|
+
if ((['all', 'android'].includes(platform) && !androidRuntimeVersion) ||
|
|
80
|
+
(['all', 'ios'].includes(platform) && !iosRuntimeVersion)) {
|
|
81
|
+
androidRuntimeVersion = androidRuntimeVersion !== null && androidRuntimeVersion !== void 0 ? androidRuntimeVersion : getDefaultRuntimeVersion(workflows.android);
|
|
82
|
+
iosRuntimeVersion = iosRuntimeVersion !== null && iosRuntimeVersion !== void 0 ? iosRuntimeVersion : getDefaultRuntimeVersion(workflows.ios);
|
|
83
|
+
if (platform === 'all' && isRuntimeEqual(androidRuntimeVersion, iosRuntimeVersion)) {
|
|
84
|
+
modifyConfig.runtimeVersion = androidRuntimeVersion;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
if (['all', 'android'].includes(platform)) {
|
|
88
|
+
modifyConfig.runtimeVersion = undefined;
|
|
89
|
+
modifyConfig.android = { runtimeVersion: androidRuntimeVersion };
|
|
90
|
+
}
|
|
91
|
+
if (['all', 'ios'].includes(platform)) {
|
|
92
|
+
modifyConfig.runtimeVersion = undefined;
|
|
93
|
+
modifyConfig.ios = { runtimeVersion: iosRuntimeVersion };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (Object.keys(modifyConfig).length === 0) {
|
|
98
|
+
return { exp, projectChanged: false };
|
|
99
|
+
}
|
|
100
|
+
const mergedExp = mergeExpoConfig(exp, modifyConfig);
|
|
101
|
+
const result = await (0, config_1.modifyConfigAsync)(projectDir, mergedExp);
|
|
102
|
+
switch (result.type) {
|
|
103
|
+
case 'success':
|
|
104
|
+
logEasUpdatesAutoConfig({ exp, modifyConfig });
|
|
105
|
+
return {
|
|
106
|
+
projectChanged: true,
|
|
107
|
+
// TODO(cedric): fix return type of `modifyConfigAsync` to avoid `null` for type === success repsonses
|
|
108
|
+
exp: (_f = result.config) === null || _f === void 0 ? void 0 : _f.expo,
|
|
109
|
+
};
|
|
110
|
+
case 'warn':
|
|
111
|
+
warnEASUpdatesManualConfig({ modifyConfig, workflows });
|
|
112
|
+
throw new Error(result.message);
|
|
113
|
+
case 'fail':
|
|
114
|
+
throw new Error(result.message);
|
|
115
|
+
default:
|
|
116
|
+
throw new Error(`Unexpected result type "${result.type}" received when modifying the project config.`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function logEasUpdatesAutoConfig({ modifyConfig, exp, }) {
|
|
120
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
121
|
+
if ((_a = modifyConfig.updates) === null || _a === void 0 ? void 0 : _a.url) {
|
|
122
|
+
log_1.default.withTick(((_b = exp.updates) === null || _b === void 0 ? void 0 : _b.url)
|
|
123
|
+
? `Overwrote updates.url "${exp.updates.url}" with "${modifyConfig.updates.url}"`
|
|
124
|
+
: `Configured updates.url to "${modifyConfig.updates.url}"`);
|
|
125
|
+
}
|
|
126
|
+
if ((_d = (_c = modifyConfig.android) === null || _c === void 0 ? void 0 : _c.runtimeVersion) !== null && _d !== void 0 ? _d : modifyConfig.runtimeVersion) {
|
|
127
|
+
log_1.default.withTick(`Configured runtimeVersion for ${platform_1.appPlatformDisplayNames[generated_1.AppPlatform.Android]} with "${JSON.stringify((_f = (_e = modifyConfig.android) === null || _e === void 0 ? void 0 : _e.runtimeVersion) !== null && _f !== void 0 ? _f : modifyConfig.runtimeVersion)}"`);
|
|
128
|
+
}
|
|
129
|
+
if ((_h = (_g = modifyConfig.ios) === null || _g === void 0 ? void 0 : _g.runtimeVersion) !== null && _h !== void 0 ? _h : modifyConfig.runtimeVersion) {
|
|
130
|
+
log_1.default.withTick(`Configured runtimeVersion for ${platform_1.appPlatformDisplayNames[generated_1.AppPlatform.Ios]} with "${JSON.stringify((_k = (_j = modifyConfig.ios) === null || _j === void 0 ? void 0 : _j.runtimeVersion) !== null && _k !== void 0 ? _k : modifyConfig.runtimeVersion)}"`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function warnEASUpdatesManualConfig({ modifyConfig, workflows, }) {
|
|
134
|
+
log_1.default.addNewLineIfNone();
|
|
135
|
+
log_1.default.warn(`It looks like you are using a dynamic configuration! ${(0, log_1.learnMore)('https://docs.expo.dev/workflow/configuration/#dynamic-configuration-with-appconfigjs)')}`);
|
|
136
|
+
log_1.default.warn(`Finish configuring EAS Update by adding the following to the project app.config.js:\n${(0, log_1.learnMore)('https://expo.fyi/eas-update-config')}\n`);
|
|
137
|
+
log_1.default.log(chalk_1.default.bold(JSON.stringify(replaceUndefinedObjectValues(modifyConfig, '<remove this key>'), null, 2)));
|
|
138
|
+
log_1.default.addNewLineIfNone();
|
|
139
|
+
if (workflows.android === eas_build_job_1.Workflow.GENERIC || workflows.ios === eas_build_job_1.Workflow.GENERIC) {
|
|
140
|
+
log_1.default.warn((0, chalk_1.default) `The native config files {bold Expo.plist & AndroidManifest.xml} must be updated to support EAS Update. ${(0, log_1.learnMore)('https://expo.fyi/eas-update-config.md#native-configuration')}`);
|
|
141
|
+
}
|
|
142
|
+
log_1.default.addNewLineIfNone();
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Make sure that the current `app.json` configuration for EAS Updates is set natively.
|
|
146
|
+
*/
|
|
147
|
+
async function ensureEASUpdatesIsConfiguredNativelyAsync(graphqlClient, { exp, projectId, projectDir, platform, workflows, }) {
|
|
148
|
+
if (['all', 'android'].includes(platform) && workflows.android === eas_build_job_1.Workflow.GENERIC) {
|
|
149
|
+
await (0, UpdatesModule_1.syncUpdatesConfigurationAsync)(graphqlClient, projectDir, exp, projectId);
|
|
150
|
+
log_1.default.withTick(`Configured ${chalk_1.default.bold('AndroidManifest.xml')} for EAS Update`);
|
|
151
|
+
}
|
|
152
|
+
if (['all', 'ios'].includes(platform) && workflows.ios === eas_build_job_1.Workflow.GENERIC) {
|
|
153
|
+
await (0, UpdatesModule_2.syncUpdatesConfigurationAsync)(graphqlClient, projectDir, exp, projectId);
|
|
154
|
+
log_1.default.withTick(`Configured ${chalk_1.default.bold('Expo.plist')} for EAS Update`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Make sure EAS Update is fully configured in the current project.
|
|
159
|
+
* This goes over a checklist and performs the following checks or changes:
|
|
160
|
+
* - Enure the `expo-updates` package is currently installed.
|
|
161
|
+
* - Ensure `app.json` is configured for EAS Updates
|
|
162
|
+
* - Sets `runtimeVersion` if not set
|
|
163
|
+
* - Sets `updates.url` if not set
|
|
164
|
+
* - Ensure latest changes are reflected in the native config, if any
|
|
165
|
+
*/
|
|
166
|
+
async function ensureEASUpdatesIsConfiguredAsync(graphqlClient, { exp: expWithoutUpdates, projectId, projectDir, platform, }) {
|
|
167
|
+
const hasExpoUpdates = (0, projectUtils_1.isExpoUpdatesInstalledOrAvailable)(projectDir, expWithoutUpdates.sdkVersion);
|
|
168
|
+
if (!hasExpoUpdates) {
|
|
169
|
+
await (0, projectUtils_1.installExpoUpdatesAsync)(projectDir, { silent: !log_1.default.isDebug });
|
|
170
|
+
log_1.default.withTick('Installed expo updates');
|
|
171
|
+
}
|
|
172
|
+
// Bail out if using a platform that doesn't require runtime versions
|
|
173
|
+
// or native setup, i.e. web.
|
|
174
|
+
if (!platform) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const workflows = await (0, workflow_1.resolveWorkflowPerPlatformAsync)(projectDir);
|
|
178
|
+
const { projectChanged, exp: expWithUpdates } = await ensureEASUpdatesIsConfiguredInExpoConfigAsync({
|
|
179
|
+
exp: expWithoutUpdates,
|
|
180
|
+
projectDir,
|
|
181
|
+
projectId,
|
|
182
|
+
platform,
|
|
183
|
+
workflows,
|
|
184
|
+
});
|
|
185
|
+
if (projectChanged || !hasExpoUpdates) {
|
|
186
|
+
await ensureEASUpdatesIsConfiguredNativelyAsync(graphqlClient, {
|
|
187
|
+
exp: expWithUpdates,
|
|
188
|
+
projectDir,
|
|
189
|
+
projectId,
|
|
190
|
+
platform,
|
|
191
|
+
workflows,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (projectChanged) {
|
|
195
|
+
log_1.default.addNewLineIfNone();
|
|
196
|
+
log_1.default.warn(`All builds of your app going forward will be eligible to receive updates published with EAS Update.`);
|
|
197
|
+
log_1.default.newLine();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
exports.ensureEASUpdatesIsConfiguredAsync = ensureEASUpdatesIsConfiguredAsync;
|
package/build/utils/expoCli.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { ExpoConfig } from '@expo/config-types';
|
|
2
|
+
/**
|
|
3
|
+
* @returns `true` if the project is SDK +46, has `@expo/cli`, and `EXPO_USE_LOCAL_CLI` is not set to a _false_ value.
|
|
4
|
+
*/
|
|
5
|
+
export declare function shouldUseVersionedExpoCLIExpensive(projectDir: string, exp: Pick<ExpoConfig, 'sdkVersion'>): boolean;
|
|
6
|
+
export declare const shouldUseVersionedExpoCLI: typeof shouldUseVersionedExpoCLIExpensive;
|
|
1
7
|
export declare function expoCommandAsync(projectDir: string, args: string[], { silent }?: {
|
|
2
8
|
silent?: boolean;
|
|
3
9
|
}): Promise<void>;
|
package/build/utils/expoCli.js
CHANGED
|
@@ -1,11 +1,56 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.expoCommandAsync = void 0;
|
|
3
|
+
exports.expoCommandAsync = exports.shouldUseVersionedExpoCLI = exports.shouldUseVersionedExpoCLIExpensive = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const spawn_async_1 = tslib_1.__importDefault(require("@expo/spawn-async"));
|
|
6
6
|
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
7
|
+
const getenv_1 = require("getenv");
|
|
7
8
|
const resolve_from_1 = tslib_1.__importDefault(require("resolve-from"));
|
|
9
|
+
const semver_1 = tslib_1.__importDefault(require("semver"));
|
|
8
10
|
const log_1 = tslib_1.__importDefault(require("../log"));
|
|
11
|
+
const memoize_1 = require("./expodash/memoize");
|
|
12
|
+
// Aggressively returns `true` (UNVERSIONED, invalid SDK version format) to push users towards the versioned CLI.
|
|
13
|
+
function gteSdkVersion(fromSdkVersion, sdkVersion) {
|
|
14
|
+
if (fromSdkVersion === 'UNVERSIONED') {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
return semver_1.default.gte(fromSdkVersion, sdkVersion);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* @returns `true` if the project is SDK +46, has `@expo/cli`, and `EXPO_USE_LOCAL_CLI` is not set to a _false_ value.
|
|
26
|
+
*/
|
|
27
|
+
function shouldUseVersionedExpoCLIExpensive(projectDir, exp) {
|
|
28
|
+
// Users can disable local CLI settings EXPO_USE_LOCAL_CLI=false
|
|
29
|
+
// https://github.com/expo/expo/blob/69eddda7bb1dbfab44258f468cf7f22984c1e44e/packages/expo/bin/cli.js#L10
|
|
30
|
+
// Run the environment variable check first as it's the cheapest.
|
|
31
|
+
const userDefinedVersionedCliEnabled = (0, getenv_1.boolish)('EXPO_USE_LOCAL_CLI', true);
|
|
32
|
+
if (!userDefinedVersionedCliEnabled) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
// NOTE(EvanBacon): The CLI package could be available through a monorepo
|
|
37
|
+
// we need to ensure the project is specifically using a known supported Expo SDK version.
|
|
38
|
+
if (
|
|
39
|
+
// If the version isn't defined then skip the check.
|
|
40
|
+
// Users running in a non-standard way should use the latest supported behavior (local CLI).
|
|
41
|
+
exp.sdkVersion &&
|
|
42
|
+
!gteSdkVersion(exp.sdkVersion, '46.0.0')) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
log_1.default.debug('Error detecting Expo SDK version for enabling versioned Expo CLI:', error);
|
|
48
|
+
}
|
|
49
|
+
// Finally ensure the CLI is available for sanity.
|
|
50
|
+
return !!resolve_from_1.default.silent(projectDir, '@expo/cli');
|
|
51
|
+
}
|
|
52
|
+
exports.shouldUseVersionedExpoCLIExpensive = shouldUseVersionedExpoCLIExpensive;
|
|
53
|
+
exports.shouldUseVersionedExpoCLI = (0, memoize_1.memoize)(shouldUseVersionedExpoCLIExpensive);
|
|
9
54
|
async function expoCommandAsync(projectDir, args, { silent = false } = {}) {
|
|
10
55
|
const expoCliPath = (0, resolve_from_1.default)(projectDir, 'expo/bin/cli.js');
|
|
11
56
|
const spawnPromise = (0, spawn_async_1.default)(expoCliPath, args, {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.memoize = void 0;
|
|
4
|
+
/** `lodash.memoize` */
|
|
5
|
+
function memoize(fn) {
|
|
6
|
+
const cache = {};
|
|
7
|
+
return ((...args) => {
|
|
8
|
+
const key = JSON.stringify(args);
|
|
9
|
+
if (cache[key]) {
|
|
10
|
+
return cache[key];
|
|
11
|
+
}
|
|
12
|
+
const result = fn(...args);
|
|
13
|
+
cache[key] = result;
|
|
14
|
+
return result;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
exports.memoize = memoize;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare class ImageNonPngError extends Error {
|
|
2
|
+
}
|
|
3
|
+
export declare class ImageTransparencyError extends Error {
|
|
4
|
+
}
|
|
5
|
+
export declare function ensurePNGIsNotTransparentAsync(imagePathOrURL: string): Promise<void>;
|
|
6
|
+
export declare function isPNGAsync(imagePathOrURL: string): Promise<boolean>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPNGAsync = exports.ensurePNGIsNotTransparentAsync = exports.ImageTransparencyError = exports.ImageNonPngError = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
6
|
+
const pngjs_1 = require("pngjs");
|
|
7
|
+
const fetch_1 = tslib_1.__importDefault(require("../fetch"));
|
|
8
|
+
class ImageNonPngError extends Error {
|
|
9
|
+
}
|
|
10
|
+
exports.ImageNonPngError = ImageNonPngError;
|
|
11
|
+
class ImageTransparencyError extends Error {
|
|
12
|
+
}
|
|
13
|
+
exports.ImageTransparencyError = ImageTransparencyError;
|
|
14
|
+
async function ensurePNGIsNotTransparentAsync(imagePathOrURL) {
|
|
15
|
+
let hasAlreadyResolved = false;
|
|
16
|
+
const stream = await getImageStreamAsync(imagePathOrURL);
|
|
17
|
+
let metadata;
|
|
18
|
+
return new Promise((res, rej) => {
|
|
19
|
+
stream
|
|
20
|
+
.pipe(new pngjs_1.PNG({ filterType: 4 }))
|
|
21
|
+
.on('error', err => {
|
|
22
|
+
if (err.message.match(/Invalid file signature/)) {
|
|
23
|
+
rej(new ImageNonPngError());
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
rej(err);
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
.on('metadata', _metadata => {
|
|
30
|
+
metadata = _metadata;
|
|
31
|
+
const { alpha } = metadata;
|
|
32
|
+
if (!alpha) {
|
|
33
|
+
hasAlreadyResolved = true;
|
|
34
|
+
if (stream instanceof fs_extra_1.default.ReadStream) {
|
|
35
|
+
stream.close();
|
|
36
|
+
}
|
|
37
|
+
res();
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
.on('parsed', (png) => {
|
|
41
|
+
if (hasAlreadyResolved) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
// metadata should be set but assume the png is fine if it's not available
|
|
46
|
+
if (!metadata) {
|
|
47
|
+
res();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
validateAlphaChannelIsEmpty(png, { width: metadata.width, height: metadata.height });
|
|
51
|
+
res();
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
rej(err);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
exports.ensurePNGIsNotTransparentAsync = ensurePNGIsNotTransparentAsync;
|
|
60
|
+
async function isPNGAsync(imagePathOrURL) {
|
|
61
|
+
const stream = await getImageStreamAsync(imagePathOrURL);
|
|
62
|
+
return new Promise((res, rej) => {
|
|
63
|
+
stream
|
|
64
|
+
.pipe(new pngjs_1.PNG({ filterType: 4 }))
|
|
65
|
+
.on('error', err => {
|
|
66
|
+
if (err.message.match(/Invalid file signature/)) {
|
|
67
|
+
res(false);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
rej(err);
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
.on('parsed', () => {
|
|
74
|
+
res(true);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
exports.isPNGAsync = isPNGAsync;
|
|
79
|
+
async function getImageStreamAsync(imagePathOrURL) {
|
|
80
|
+
if (isURL(imagePathOrURL)) {
|
|
81
|
+
const response = await (0, fetch_1.default)(imagePathOrURL);
|
|
82
|
+
return response.body;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
return fs_extra_1.default.createReadStream(imagePathOrURL);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function isURL(imagePathOrURL) {
|
|
89
|
+
try {
|
|
90
|
+
// eslint-disable-next-line no-new
|
|
91
|
+
new URL(imagePathOrURL);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function validateAlphaChannelIsEmpty(data, { width, height }) {
|
|
99
|
+
for (let y = 0; y < height; y++) {
|
|
100
|
+
for (let x = 0; x < width; x++) {
|
|
101
|
+
const idx = (width * y + x) * 4;
|
|
102
|
+
if (data[idx + 3] !== 255) {
|
|
103
|
+
throw new ImageTransparencyError();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -23,6 +23,7 @@ function warnAboutServiceOutage(service) {
|
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
const outageType = service.status === generated_1.StatuspageServiceStatus.MajorOutage ? 'major' : 'partial';
|
|
26
|
+
log_1.default.addNewLineIfNone();
|
|
26
27
|
log_1.default.warn(chalk_1.default.bold(`${humanReadableServiceName[service.name]} is experiencing a ${outageType} outage.`));
|
|
27
28
|
if (service.incidents.length > 0) {
|
|
28
29
|
const [currentIncident] = service.incidents;
|