@devicecloud.dev/dcd 1.0.11 → 2.0.0-rc.3
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/dist/commands/cloud.d.ts +3 -0
- package/dist/commands/cloud.js +40 -73
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +14 -0
- package/dist/index.js +0 -1
- package/dist/methods.d.ts +4 -2
- package/dist/methods.js +92 -6
- package/dist/plan.js +60 -71
- package/dist/planMethods.d.ts +3 -4
- package/dist/planMethods.js +19 -22
- package/oclif.manifest.json +17 -1
- package/package.json +1 -5
package/dist/commands/cloud.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
+
export declare const mimeTypeLookupByExtension: Record<string, string>;
|
|
2
3
|
export declare enum EiOSDevices {
|
|
3
4
|
'ipad-pro-6th-gen' = "ipad-pro-6th-gen",
|
|
4
5
|
'iphone-13' = "iphone-13",
|
|
@@ -23,6 +24,8 @@ export default class Cloud extends Command {
|
|
|
23
24
|
static description: string;
|
|
24
25
|
static examples: string[];
|
|
25
26
|
static flags: {
|
|
27
|
+
'additional-app-binary-ids': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
28
|
+
'additional-app-files': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
26
29
|
'android-api-level': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
27
30
|
'android-device': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
28
31
|
apiKey: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
package/dist/commands/cloud.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EiOSDevices = void 0;
|
|
3
|
+
exports.EiOSDevices = exports.mimeTypeLookupByExtension = void 0;
|
|
4
4
|
/* eslint-disable complexity */
|
|
5
5
|
const core_1 = require("@oclif/core");
|
|
6
6
|
const cli_ux_1 = require("@oclif/core/lib/cli-ux");
|
|
7
7
|
const errors_1 = require("@oclif/core/lib/errors");
|
|
8
|
-
const supabase_js_1 = require("@supabase/supabase-js");
|
|
9
|
-
const file_1 = require("@web-std/file");
|
|
10
|
-
const promises_1 = require("node:fs/promises");
|
|
11
8
|
const path = require("node:path");
|
|
12
9
|
const constants_1 = require("../constants");
|
|
13
10
|
const methods_1 = require("../methods");
|
|
14
11
|
const plan_1 = require("../plan");
|
|
15
|
-
|
|
12
|
+
exports.mimeTypeLookupByExtension = {
|
|
16
13
|
apk: 'application/vnd.android.package-archive',
|
|
17
14
|
yaml: 'application/x-yaml',
|
|
18
15
|
zip: 'application/zip',
|
|
@@ -58,16 +55,20 @@ class Cloud extends core_1.Command {
|
|
|
58
55
|
}
|
|
59
56
|
await (0, methods_1.versionCheck)(this.config.version);
|
|
60
57
|
const { args, flags, raw } = await this.parse(Cloud);
|
|
61
|
-
const { 'android-api-level': androidApiLevel, 'android-device': androidDevice, apiKey, apiUrl, 'app-binary-id': appBinaryId, 'app-file': appFile, arm64, async, 'device-locale': deviceLocale, 'download-artifacts': downloadArtifacts, env, 'exclude-flows': excludeFlows, 'exclude-tags': excludeTags, flows, 'google-play': googlePlay, 'include-tags': includeTags, 'ios-device': iOSDevice, 'ios-version': iOSVersion, 'maestro-version': maestroVersion, name, orientation, quiet, ...rest } = flags;
|
|
58
|
+
const { 'additional-app-binary-ids': nonFlatAdditionalAppBinaryIds, 'additional-app-files': nonFlatAdditionalAppFiles, 'android-api-level': androidApiLevel, 'android-device': androidDevice, apiKey, apiUrl, 'app-binary-id': appBinaryId, 'app-file': appFile, arm64, async, 'device-locale': deviceLocale, 'download-artifacts': downloadArtifacts, env, 'exclude-flows': excludeFlows, 'exclude-tags': excludeTags, flows, 'google-play': googlePlay, 'include-tags': includeTags, 'ios-device': iOSDevice, 'ios-version': iOSVersion, 'maestro-version': maestroVersion, name, orientation, quiet, ...rest } = flags;
|
|
62
59
|
if (arm64) {
|
|
63
60
|
(0, cli_ux_1.info)('Contact hello@devicecloud.dev to enquire about arm64 devices');
|
|
64
61
|
(0, cli_ux_1.exit)();
|
|
65
62
|
}
|
|
63
|
+
if (!apiKey)
|
|
64
|
+
throw new Error('You must provide an API key');
|
|
65
|
+
const additionalAppBinaryIds = nonFlatAdditionalAppBinaryIds?.flat();
|
|
66
|
+
const additionalAppFiles = nonFlatAdditionalAppFiles?.flat();
|
|
66
67
|
const { firstFile, secondFile } = args;
|
|
67
68
|
let finalBinaryId = appBinaryId;
|
|
69
|
+
let finalAdditionalBinaryIds = additionalAppBinaryIds;
|
|
68
70
|
const finalAppFile = appFile ?? firstFile;
|
|
69
71
|
let flowFile = flows ?? secondFile;
|
|
70
|
-
let metadata;
|
|
71
72
|
if (appBinaryId) {
|
|
72
73
|
if (secondFile) {
|
|
73
74
|
throw new Error('You cannot provide both an appBinaryId and a binary file');
|
|
@@ -100,6 +101,18 @@ class Cloud extends core_1.Command {
|
|
|
100
101
|
process.exit(2);
|
|
101
102
|
}
|
|
102
103
|
const { allExcludeTags, allIncludeTags, flowsToRun: testFileNames, referencedFiles, sequence, } = executionPlan;
|
|
104
|
+
const pathsShortestToLongest = [
|
|
105
|
+
...testFileNames,
|
|
106
|
+
...referencedFiles,
|
|
107
|
+
].sort((a, b) => a.split(path.sep).length - b.split(path.sep).length);
|
|
108
|
+
let commonRoot = path.parse(process.cwd()).root;
|
|
109
|
+
const folders = pathsShortestToLongest[0].split(path.sep);
|
|
110
|
+
for (const [index] of folders.entries()) {
|
|
111
|
+
const folderPath = folders.slice(0, index + 1).join(path.sep);
|
|
112
|
+
const isRoot = pathsShortestToLongest.every((file) => file.startsWith(folderPath));
|
|
113
|
+
if (isRoot)
|
|
114
|
+
commonRoot = folderPath;
|
|
115
|
+
}
|
|
103
116
|
const { continueOnFailure = true, flows: sequentialFlows = [] } = sequence ?? {};
|
|
104
117
|
if (!appBinaryId) {
|
|
105
118
|
if (!(flowFile && finalAppFile)) {
|
|
@@ -112,6 +125,7 @@ class Cloud extends core_1.Command {
|
|
|
112
125
|
await (0, methods_1.verifyAppZip)(finalAppFile);
|
|
113
126
|
}
|
|
114
127
|
}
|
|
128
|
+
await (0, methods_1.verifyAdditionalAppFiles)(additionalAppFiles);
|
|
115
129
|
const flagLogs = [];
|
|
116
130
|
for (const [k, v] of Object.entries(flags)) {
|
|
117
131
|
if (v && v.toString().length > 0) {
|
|
@@ -123,6 +137,8 @@ class Cloud extends core_1.Command {
|
|
|
123
137
|
Submitting new job
|
|
124
138
|
→ Flow(s): ${flowFile}
|
|
125
139
|
→ App: ${appBinaryId || finalAppFile}
|
|
140
|
+
${Boolean(additionalAppBinaryIds || additionalAppFiles) &&
|
|
141
|
+
`→ Additional app(s): ${additionalAppBinaryIds} ${additionalAppFiles}`}
|
|
126
142
|
|
|
127
143
|
With options
|
|
128
144
|
→ ${flagLogs.join(`
|
|
@@ -130,69 +146,18 @@ class Cloud extends core_1.Command {
|
|
|
130
146
|
|
|
131
147
|
`);
|
|
132
148
|
if (!finalBinaryId) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
throw new Error(message);
|
|
146
|
-
let file;
|
|
147
|
-
if (finalAppFile?.endsWith('.app')) {
|
|
148
|
-
const zippedAppBlob = await (0, methods_1.compressFolderToBlob)(finalAppFile);
|
|
149
|
-
const filePath = finalAppFile + '.zip';
|
|
150
|
-
file = new file_1.File([zippedAppBlob], filePath);
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
const binaryBlob = new Blob([await (0, promises_1.readFile)(finalAppFile)], {
|
|
154
|
-
type: mimeTypeLookupByExtension[finalAppFile.split('.').pop()],
|
|
155
|
-
});
|
|
156
|
-
file = new file_1.File([binaryBlob], finalAppFile);
|
|
157
|
-
}
|
|
158
|
-
try {
|
|
159
|
-
metadata = finalAppFile?.endsWith('.apk')
|
|
160
|
-
? await (0, methods_1.extractAppMetadataAndroid)(finalAppFile)
|
|
161
|
-
: finalAppFile?.endsWith('.zip')
|
|
162
|
-
? await (0, methods_1.extractAppMetadataIosZip)(finalAppFile)
|
|
163
|
-
: await (0, methods_1.extractAppMetadataIos)(finalAppFile);
|
|
164
|
-
}
|
|
165
|
-
catch {
|
|
166
|
-
this.warn('Failed to extract app metadata, please share with support@devicecloud.dev so we can improve our parsing.');
|
|
167
|
-
}
|
|
168
|
-
// this needs to made nicer by using envs or maybe fetching the keys from the getSignedURL call
|
|
169
|
-
const SB = {
|
|
170
|
-
dev: {
|
|
171
|
-
SUPABASE_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxibXNvd2VodGp3bnFsdXJwZW1iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDkyMTg0ODcsImV4cCI6MjAyNDc5NDQ4N30.zeLTMAuZ_WwYvGdeP0kdvL_Zrs-RQee5APPyxmWq7qQ',
|
|
172
|
-
SUPABASE_URL: 'https://lbmsowehtjwnqlurpemb.supabase.co',
|
|
173
|
-
},
|
|
174
|
-
prod: {
|
|
175
|
-
SUPABASE_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBneWRucGhiaW1ldGluc2dma2JvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDc1OTQzNDYsImV4cCI6MjAyMzE3MDM0Nn0.hAYOMFxxwX1exkQkY9xyQJGC_GhGnyogkj2N-kBkMI8',
|
|
176
|
-
SUPABASE_URL: 'https://pgydnphbimetinsgfkbo.supabase.co',
|
|
177
|
-
},
|
|
178
|
-
};
|
|
179
|
-
const { SUPABASE_KEY, SUPABASE_URL } = SB[apiUrl === 'https://api.devicecloud.dev' ? 'prod' : 'dev'];
|
|
180
|
-
const supabase = (0, supabase_js_1.createClient)(SUPABASE_URL, SUPABASE_KEY);
|
|
181
|
-
const uploadToUrl = await supabase.storage
|
|
182
|
-
.from('organizations')
|
|
183
|
-
.uploadToSignedUrl(path, token, file);
|
|
184
|
-
if (uploadToUrl.error)
|
|
185
|
-
throw new Error(uploadToUrl.error);
|
|
186
|
-
const { error } = await (0, methods_1.typeSafePost)(apiUrl, '/uploads/finaliseUpload', {
|
|
187
|
-
body: JSON.stringify({ id, metadata, path }),
|
|
188
|
-
headers: {
|
|
189
|
-
'content-type': 'application/json',
|
|
190
|
-
'x-app-api-key': apiKey,
|
|
191
|
-
},
|
|
192
|
-
});
|
|
193
|
-
if (error)
|
|
194
|
-
throw new Error(error);
|
|
195
|
-
core_1.ux.action.stop(`\nBinary uploaded with id: ${finalBinaryId}`);
|
|
149
|
+
if (!finalAppFile)
|
|
150
|
+
throw new Error('You must provide either an app binary id or an app file');
|
|
151
|
+
const binaryId = await (0, methods_1.uploadBinary)(finalAppFile, apiUrl, apiKey);
|
|
152
|
+
finalBinaryId = binaryId;
|
|
153
|
+
}
|
|
154
|
+
let uploadedBinaryIds = [];
|
|
155
|
+
if (additionalAppFiles?.length) {
|
|
156
|
+
uploadedBinaryIds = await (0, methods_1.uploadBinaries)(additionalAppFiles, apiUrl, apiKey);
|
|
157
|
+
finalAdditionalBinaryIds = [
|
|
158
|
+
...finalAdditionalBinaryIds,
|
|
159
|
+
...uploadedBinaryIds,
|
|
160
|
+
];
|
|
196
161
|
}
|
|
197
162
|
const testFormData = new FormData();
|
|
198
163
|
// eslint-disable-next-line unicorn/no-array-reduce
|
|
@@ -210,17 +175,18 @@ class Cloud extends core_1.Command {
|
|
|
210
175
|
...testFileNames,
|
|
211
176
|
...sequentialFlows,
|
|
212
177
|
]),
|
|
213
|
-
]);
|
|
178
|
+
], commonRoot);
|
|
214
179
|
const blob = new Blob([buffer], {
|
|
215
|
-
type: mimeTypeLookupByExtension.zip,
|
|
180
|
+
type: exports.mimeTypeLookupByExtension.zip,
|
|
216
181
|
});
|
|
217
182
|
testFormData.set('file', blob, 'flowFile.zip');
|
|
218
183
|
testFormData.set('appBinaryId', finalBinaryId);
|
|
219
|
-
testFormData.set('testFileNames', JSON.stringify(testFileNames));
|
|
184
|
+
testFormData.set('testFileNames', JSON.stringify(testFileNames.map((t) => t.replaceAll(commonRoot, '.'))));
|
|
220
185
|
testFormData.set('sequentialFlows', JSON.stringify(sequentialFlows));
|
|
221
186
|
testFormData.set('env', JSON.stringify(envObject));
|
|
222
187
|
testFormData.set('googlePlay', googlePlay ? 'true' : 'false');
|
|
223
188
|
testFormData.set('config', JSON.stringify({
|
|
189
|
+
additionalAppBinaryIds: finalAdditionalBinaryIds,
|
|
224
190
|
allExcludeTags,
|
|
225
191
|
allIncludeTags,
|
|
226
192
|
continueOnFailure,
|
|
@@ -228,6 +194,7 @@ class Cloud extends core_1.Command {
|
|
|
228
194
|
maestroVersion,
|
|
229
195
|
orientation,
|
|
230
196
|
raw,
|
|
197
|
+
uploadedBinaryIds,
|
|
231
198
|
version: this.config.version,
|
|
232
199
|
}));
|
|
233
200
|
if (androidApiLevel)
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { EiOSDevices } from './commands/cloud';
|
|
2
2
|
export declare const flags: {
|
|
3
|
+
'additional-app-binary-ids': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
4
|
+
'additional-app-files': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
3
5
|
'android-api-level': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
4
6
|
'android-device': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
5
7
|
apiKey: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
package/dist/constants.js
CHANGED
|
@@ -3,6 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.iOSCompatibilityLookup = exports.flags = void 0;
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
exports.flags = {
|
|
6
|
+
'additional-app-binary-ids': core_1.Flags.string({
|
|
7
|
+
default: [],
|
|
8
|
+
description: 'The ID of the additional app binary(s) previously uploaded to devicecloud.dev to install before execution',
|
|
9
|
+
multiple: true,
|
|
10
|
+
multipleNonGreedy: true,
|
|
11
|
+
parse: (input) => input.split(','),
|
|
12
|
+
}),
|
|
13
|
+
'additional-app-files': core_1.Flags.file({
|
|
14
|
+
default: [],
|
|
15
|
+
description: 'Additional app binary(s) to install before execution',
|
|
16
|
+
multiple: true,
|
|
17
|
+
multipleNonGreedy: true,
|
|
18
|
+
parse: (input) => input.split(','),
|
|
19
|
+
}),
|
|
6
20
|
'android-api-level': core_1.Flags.integer({
|
|
7
21
|
description: '[Android only] Android API level to run your flow against',
|
|
8
22
|
options: ['33', '34'],
|
package/dist/index.js
CHANGED
package/dist/methods.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
1
|
import * as archiver from 'archiver';
|
|
3
2
|
import { paths } from '../../api/schema.types';
|
|
4
3
|
import { TAppMetadata } from './types';
|
|
@@ -18,8 +17,11 @@ export declare const typeSafeGet: <T extends keyof paths>(baseUrl: string, path:
|
|
|
18
17
|
export declare const toBuffer: (archive: archiver.Archiver) => Promise<Buffer>;
|
|
19
18
|
export declare const compressDir: (sourceDir: string) => Promise<Buffer>;
|
|
20
19
|
export declare const compressFolderToBlob: (sourceDir: string) => Promise<Blob>;
|
|
21
|
-
export declare const compressFilesFromRelativePath: (path: string, files: string[]) => Promise<Buffer>;
|
|
20
|
+
export declare const compressFilesFromRelativePath: (path: string, files: string[], commonRoot: string) => Promise<Buffer>;
|
|
22
21
|
export declare const verifyAppZip: (zipPath: string) => Promise<void>;
|
|
23
22
|
export declare const extractAppMetadataAndroid: (appFilePath: string) => Promise<TAppMetadata>;
|
|
24
23
|
export declare const extractAppMetadataIosZip: (appFilePath: string) => Promise<TAppMetadata>;
|
|
25
24
|
export declare const extractAppMetadataIos: (appFolderPath: string) => Promise<TAppMetadata>;
|
|
25
|
+
export declare const uploadBinary: (filePath: string, apiUrl: string, apiKey: string) => Promise<string>;
|
|
26
|
+
export declare const uploadBinaries: (finalAppFiles: string[], apiUrl: string, apiKey: string) => Promise<string[]>;
|
|
27
|
+
export declare const verifyAdditionalAppFiles: (appFiles: string[] | undefined) => Promise<void>;
|
package/dist/methods.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.extractAppMetadataIos = exports.extractAppMetadataIosZip = exports.extractAppMetadataAndroid = exports.verifyAppZip = exports.compressFilesFromRelativePath = exports.compressFolderToBlob = exports.compressDir = exports.toBuffer = exports.typeSafeGet = exports.typeSafePostDownload = exports.typeSafePost = exports.versionCheck = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
3
|
+
exports.verifyAdditionalAppFiles = exports.uploadBinaries = exports.uploadBinary = exports.extractAppMetadataIos = exports.extractAppMetadataIosZip = exports.extractAppMetadataAndroid = exports.verifyAppZip = exports.compressFilesFromRelativePath = exports.compressFolderToBlob = exports.compressDir = exports.toBuffer = exports.typeSafeGet = exports.typeSafePostDownload = exports.typeSafePost = exports.versionCheck = void 0;
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const supabase_js_1 = require("@supabase/supabase-js");
|
|
6
|
+
// required polyfill for node 18
|
|
7
|
+
const file_1 = require("@web-std/file");
|
|
6
8
|
// @ts-ignore
|
|
7
9
|
const AppInfoParser = require("app-info-parser");
|
|
8
10
|
const archiver = require("archiver");
|
|
@@ -14,6 +16,7 @@ const node_stream_1 = require("node:stream");
|
|
|
14
16
|
const promises_2 = require("node:stream/promises");
|
|
15
17
|
const StreamZip = require("node-stream-zip");
|
|
16
18
|
const plist_1 = require("plist");
|
|
19
|
+
const cloud_1 = require("./commands/cloud");
|
|
17
20
|
const PERMITTED_EXTENSIONS = new Set([
|
|
18
21
|
'yml',
|
|
19
22
|
'yaml',
|
|
@@ -29,7 +32,7 @@ const versionCheck = async (currentVersion) => {
|
|
|
29
32
|
const versionResponseJson = await versionResponse.json();
|
|
30
33
|
const latestVersion = versionResponseJson.version;
|
|
31
34
|
if (latestVersion !== currentVersion) {
|
|
32
|
-
|
|
35
|
+
core_1.ux.warn(`
|
|
33
36
|
-------------------
|
|
34
37
|
A new version of the devicecloud.dev CLI is available: ${latestVersion}
|
|
35
38
|
Run 'npm install -g @devicecloud.dev/dcd@latest' to update to the latest version
|
|
@@ -118,7 +121,7 @@ const compressFolderToBlob = async (sourceDir) => {
|
|
|
118
121
|
return new Blob([buffer], { type: 'application/zip' });
|
|
119
122
|
};
|
|
120
123
|
exports.compressFolderToBlob = compressFolderToBlob;
|
|
121
|
-
const compressFilesFromRelativePath = async (path, files) => {
|
|
124
|
+
const compressFilesFromRelativePath = async (path, files, commonRoot) => {
|
|
122
125
|
const archive = archiver('zip', {
|
|
123
126
|
zlib: { level: 9 },
|
|
124
127
|
});
|
|
@@ -126,7 +129,9 @@ const compressFilesFromRelativePath = async (path, files) => {
|
|
|
126
129
|
throw err;
|
|
127
130
|
});
|
|
128
131
|
for (const file of files) {
|
|
129
|
-
archive.file(nodePath.resolve(path, file), {
|
|
132
|
+
archive.file(nodePath.resolve(path, file), {
|
|
133
|
+
name: file.replace(commonRoot, ''),
|
|
134
|
+
});
|
|
130
135
|
}
|
|
131
136
|
const buffer = await (0, exports.toBuffer)(archive);
|
|
132
137
|
// await writeFile('./my-zip.zip', buffer);
|
|
@@ -200,3 +205,84 @@ const extractAppMetadataIos = async (appFolderPath) => {
|
|
|
200
205
|
return { appId, platform: 'ios' };
|
|
201
206
|
};
|
|
202
207
|
exports.extractAppMetadataIos = extractAppMetadataIos;
|
|
208
|
+
const uploadBinary = async (filePath, apiUrl, apiKey) => {
|
|
209
|
+
core_1.ux.action.start('Uploading binary', 'Initializing', { stdout: true });
|
|
210
|
+
const { id, message, path, token } = await (0, exports.typeSafePost)(apiUrl, '/uploads/getBinaryUploadUrl', {
|
|
211
|
+
body: JSON.stringify({
|
|
212
|
+
platform: filePath?.endsWith('.apk') ? 'android' : 'ios',
|
|
213
|
+
}),
|
|
214
|
+
headers: {
|
|
215
|
+
'content-type': 'application/json',
|
|
216
|
+
'x-app-api-key': apiKey,
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
if (!path)
|
|
220
|
+
throw new Error(message);
|
|
221
|
+
let file;
|
|
222
|
+
if (filePath?.endsWith('.app')) {
|
|
223
|
+
const zippedAppBlob = await (0, exports.compressFolderToBlob)(filePath);
|
|
224
|
+
file = new file_1.File([zippedAppBlob], filePath + '.zip');
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
const binaryBlob = new Blob([await (0, promises_1.readFile)(filePath)], {
|
|
228
|
+
type: cloud_1.mimeTypeLookupByExtension[filePath.split('.').pop()],
|
|
229
|
+
});
|
|
230
|
+
file = new file_1.File([binaryBlob], filePath);
|
|
231
|
+
}
|
|
232
|
+
let metadata;
|
|
233
|
+
try {
|
|
234
|
+
metadata = filePath?.endsWith('.apk')
|
|
235
|
+
? await (0, exports.extractAppMetadataAndroid)(filePath)
|
|
236
|
+
: filePath?.endsWith('.zip')
|
|
237
|
+
? await (0, exports.extractAppMetadataIosZip)(filePath)
|
|
238
|
+
: await (0, exports.extractAppMetadataIos)(filePath);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
core_1.ux.warn('Failed to extract app metadata, please share with support@devicecloud.dev so we can improve our parsing.');
|
|
242
|
+
}
|
|
243
|
+
// this needs to made nicer by using envs or maybe fetching the keys from the getSignedURL call
|
|
244
|
+
const SB = {
|
|
245
|
+
dev: {
|
|
246
|
+
SUPABASE_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxibXNvd2VodGp3bnFsdXJwZW1iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDkyMTg0ODcsImV4cCI6MjAyNDc5NDQ4N30.zeLTMAuZ_WwYvGdeP0kdvL_Zrs-RQee5APPyxmWq7qQ',
|
|
247
|
+
SUPABASE_URL: 'https://lbmsowehtjwnqlurpemb.supabase.co',
|
|
248
|
+
},
|
|
249
|
+
prod: {
|
|
250
|
+
SUPABASE_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBneWRucGhiaW1ldGluc2dma2JvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDc1OTQzNDYsImV4cCI6MjAyMzE3MDM0Nn0.hAYOMFxxwX1exkQkY9xyQJGC_GhGnyogkj2N-kBkMI8',
|
|
251
|
+
SUPABASE_URL: 'https://pgydnphbimetinsgfkbo.supabase.co',
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
const { SUPABASE_KEY, SUPABASE_URL } = SB[apiUrl === 'https://api.devicecloud.dev' ? 'prod' : 'dev'];
|
|
255
|
+
const supabase = (0, supabase_js_1.createClient)(SUPABASE_URL, SUPABASE_KEY);
|
|
256
|
+
const uploadToUrl = await supabase.storage
|
|
257
|
+
.from('organizations')
|
|
258
|
+
.uploadToSignedUrl(path, token, file);
|
|
259
|
+
if (uploadToUrl.error)
|
|
260
|
+
throw new Error(uploadToUrl.error);
|
|
261
|
+
const { error } = await (0, exports.typeSafePost)(apiUrl, '/uploads/finaliseUpload', {
|
|
262
|
+
body: JSON.stringify({ id, metadata, path }),
|
|
263
|
+
headers: {
|
|
264
|
+
'content-type': 'application/json',
|
|
265
|
+
'x-app-api-key': apiKey,
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
if (error)
|
|
269
|
+
throw new Error(error);
|
|
270
|
+
core_1.ux.action.stop(`\nBinary uploaded with id: ${id}`);
|
|
271
|
+
return id;
|
|
272
|
+
};
|
|
273
|
+
exports.uploadBinary = uploadBinary;
|
|
274
|
+
const uploadBinaries = async (finalAppFiles, apiUrl, apiKey) => Promise.all(finalAppFiles.map((f) => (0, exports.uploadBinary)(f, apiUrl, apiKey)));
|
|
275
|
+
exports.uploadBinaries = uploadBinaries;
|
|
276
|
+
const verifyAdditionalAppFiles = async (appFiles) => {
|
|
277
|
+
if (appFiles?.length) {
|
|
278
|
+
if (!appFiles.every((f) => ['apk', '.app', '.zip'].some((ext) => f.endsWith(ext)))) {
|
|
279
|
+
throw new Error('App file must be a .apk for android or .app/.zip file for iOS');
|
|
280
|
+
}
|
|
281
|
+
await Promise.all(appFiles.map(async (f) => {
|
|
282
|
+
if (f.endsWith('.zip')) {
|
|
283
|
+
await (0, exports.verifyAppZip)(f);
|
|
284
|
+
}
|
|
285
|
+
}));
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
exports.verifyAdditionalAppFiles = verifyAdditionalAppFiles;
|
package/dist/plan.js
CHANGED
|
@@ -1,56 +1,70 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.plan =
|
|
3
|
+
exports.plan = plan;
|
|
4
4
|
const glob_1 = require("glob");
|
|
5
5
|
const fs = require("node:fs");
|
|
6
6
|
const path = require("node:path");
|
|
7
7
|
const planMethods_1 = require("./planMethods");
|
|
8
|
-
async function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const { config, testSteps } = (0, planMethods_1.readTestYamlFileAsJson)(input);
|
|
8
|
+
async function checkDependencies(input) {
|
|
9
|
+
const checkedDependencies = [];
|
|
10
|
+
const uncheckedDependencies = [input];
|
|
11
|
+
while (uncheckedDependencies.length > 0) {
|
|
12
|
+
const fileToCheck = uncheckedDependencies.shift();
|
|
13
|
+
const { config, testSteps } = (0, planMethods_1.readTestYamlFileAsJson)(fileToCheck);
|
|
15
14
|
const { allErrors, allFiles } = (0, planMethods_1.processDependencies)({
|
|
16
15
|
config,
|
|
17
|
-
|
|
18
|
-
input,
|
|
16
|
+
input: fileToCheck,
|
|
19
17
|
testSteps,
|
|
20
18
|
});
|
|
21
19
|
if (allErrors.length > 0) {
|
|
22
|
-
throw new Error('The following flow files are not present in the
|
|
20
|
+
throw new Error('The following flow files are not present in the filesystem: \n' +
|
|
23
21
|
allErrors.join('\n'));
|
|
24
22
|
}
|
|
23
|
+
for (const file of allFiles) {
|
|
24
|
+
if (!(0, planMethods_1.isFlowFile)(file)) {
|
|
25
|
+
// js/media files don't have dependencies
|
|
26
|
+
checkedDependencies.push(file);
|
|
27
|
+
}
|
|
28
|
+
if (!checkedDependencies.includes(file)) {
|
|
29
|
+
uncheckedDependencies.push(file);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (!checkedDependencies.includes(fileToCheck)) {
|
|
33
|
+
checkedDependencies.push(fileToCheck);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return checkedDependencies;
|
|
37
|
+
}
|
|
38
|
+
function filterFlowFiles(unfilteredFlowFiles, excludeFlows) {
|
|
39
|
+
if (excludeFlows) {
|
|
40
|
+
return unfilteredFlowFiles.filter((file) => !excludeFlows.some((flow) => file.startsWith(path.resolve(flow))));
|
|
41
|
+
}
|
|
42
|
+
return unfilteredFlowFiles;
|
|
43
|
+
}
|
|
44
|
+
function getWorkspaceConfig(input, unfilteredFlowFiles) {
|
|
45
|
+
const configFilePath = unfilteredFlowFiles.find((file) => file === input + 'config.yaml' || file === input + 'config.yml');
|
|
46
|
+
return configFilePath
|
|
47
|
+
? (0, planMethods_1.readYamlFileAsJson)(configFilePath)
|
|
48
|
+
: {};
|
|
49
|
+
}
|
|
50
|
+
async function plan(input, includeTags, excludeTags, excludeFlows) {
|
|
51
|
+
if (!fs.existsSync(input)) {
|
|
52
|
+
throw new Error(`Flow path does not exist: ${path.resolve(input)}`);
|
|
53
|
+
}
|
|
54
|
+
if (fs.lstatSync(input).isFile()) {
|
|
55
|
+
const checkedDependancies = await checkDependencies(input);
|
|
25
56
|
return {
|
|
26
|
-
flowsToRun: [input
|
|
27
|
-
referencedFiles: [...new Set(
|
|
57
|
+
flowsToRun: [input],
|
|
58
|
+
referencedFiles: [...new Set(checkedDependancies)],
|
|
28
59
|
totalFlowFiles: 1,
|
|
29
60
|
};
|
|
30
61
|
}
|
|
31
|
-
|
|
32
|
-
let unfilteredFlowFiles = await (0, planMethods_1.walk)(input, planMethods_1.isFlowFile);
|
|
62
|
+
let unfilteredFlowFiles = await (0, planMethods_1.readDirectory)(input, planMethods_1.isFlowFile);
|
|
33
63
|
if (unfilteredFlowFiles.length === 0) {
|
|
34
64
|
throw new Error(`Flow directory does not contain any Flow files: ${path.resolve(input)}`);
|
|
35
65
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
for (const flow of excludeFlows) {
|
|
39
|
-
unfilteredFlowFiles = unfilteredFlowFiles.filter((file) => !file.startsWith(path.resolve(flow)));
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
// if config file present at top level, use it
|
|
43
|
-
const configFilePath = unfilteredFlowFiles.find((file) => file === input + 'config.yaml' || file === input + 'config.yml');
|
|
44
|
-
// remove all config files from the list to prevent processing them
|
|
45
|
-
unfilteredFlowFiles = unfilteredFlowFiles.filter((file) => !(file.endsWith('config.yaml') || file.endsWith('config.yml')));
|
|
46
|
-
const cleanPath = path.normalize(input);
|
|
47
|
-
const relativeFilePaths = unfilteredFlowFiles.map((file) => file.replace(cleanPath, ''));
|
|
48
|
-
const topLevelFlowFiles = relativeFilePaths.filter((file) => !file.includes('/'));
|
|
49
|
-
const workspaceConfig = configFilePath
|
|
50
|
-
? (0, planMethods_1.readYamlFileAsJson)(configFilePath)
|
|
51
|
-
: {};
|
|
52
|
-
// list of relative file paths from search
|
|
53
|
-
let unsortedFlowFiles = topLevelFlowFiles;
|
|
66
|
+
unfilteredFlowFiles = filterFlowFiles(unfilteredFlowFiles, excludeFlows);
|
|
67
|
+
const workspaceConfig = getWorkspaceConfig(input, unfilteredFlowFiles);
|
|
54
68
|
if (workspaceConfig.flows) {
|
|
55
69
|
const globs = workspaceConfig.flows.map((glob) => glob);
|
|
56
70
|
const matchedFiles = await (0, glob_1.glob)(globs, {
|
|
@@ -58,49 +72,25 @@ async function plan(input, includeTags, excludeTags, excludeFlows) {
|
|
|
58
72
|
nodir: true,
|
|
59
73
|
});
|
|
60
74
|
// overwrite the list of files with the globbed ones
|
|
61
|
-
|
|
62
|
-
(file
|
|
75
|
+
unfilteredFlowFiles = matchedFiles
|
|
76
|
+
.filter((file) => file !== 'config.yaml' &&
|
|
77
|
+
(file.endsWith('.yaml') || file.endsWith('.yml')))
|
|
78
|
+
.map((file) => path.resolve(input, file));
|
|
63
79
|
}
|
|
64
|
-
if (
|
|
80
|
+
if (unfilteredFlowFiles.length === 0) {
|
|
65
81
|
const error = workspaceConfig.flows
|
|
66
82
|
? new Error(`Flow inclusion pattern(s) did not match any Flow files:\n${workspaceConfig.flows.join('\n')}`)
|
|
67
|
-
: new Error(`
|
|
83
|
+
: new Error(`Workspace does not contain any Flows: ${path.resolve(input)}`);
|
|
68
84
|
throw error;
|
|
69
85
|
}
|
|
86
|
+
const configPerFlowFile =
|
|
70
87
|
// eslint-disable-next-line unicorn/no-array-reduce
|
|
71
|
-
|
|
72
|
-
const { config } = (0, planMethods_1.readTestYamlFileAsJson)(
|
|
73
|
-
acc
|
|
88
|
+
unfilteredFlowFiles.reduce((acc, filePath) => {
|
|
89
|
+
const { config } = (0, planMethods_1.readTestYamlFileAsJson)(filePath);
|
|
90
|
+
acc[filePath] = config;
|
|
74
91
|
return acc;
|
|
75
|
-
}, {
|
|
76
|
-
|
|
77
|
-
});
|
|
78
|
-
// eslint-disable-next-line unicorn/no-array-reduce
|
|
79
|
-
const { testStepsPerFlowFile } = unfilteredFlowFiles.reduce((acc, filePath) => {
|
|
80
|
-
const { testSteps } = (0, planMethods_1.readTestYamlFileAsJson)(filePath);
|
|
81
|
-
acc.testStepsPerFlowFile[filePath] = testSteps;
|
|
82
|
-
return acc;
|
|
83
|
-
}, {
|
|
84
|
-
testStepsPerFlowFile: {},
|
|
85
|
-
});
|
|
86
|
-
let errors = [];
|
|
87
|
-
let allFiles = [];
|
|
88
|
-
for (const [filePath, testSteps] of Object.entries(testStepsPerFlowFile)) {
|
|
89
|
-
if (!testSteps)
|
|
90
|
-
break;
|
|
91
|
-
const { allErrors: errs, allFiles: deps } = (0, planMethods_1.processDependencies)({
|
|
92
|
-
config: configPerFlowFile[path.relative(cleanPath, filePath)],
|
|
93
|
-
directory: cleanPath,
|
|
94
|
-
input: filePath,
|
|
95
|
-
testSteps,
|
|
96
|
-
});
|
|
97
|
-
allFiles = [...allFiles, ...deps];
|
|
98
|
-
errors = [...errors, ...errs];
|
|
99
|
-
}
|
|
100
|
-
if (errors.length > 0) {
|
|
101
|
-
throw new Error('The following flow files are not present in the provided directory: \n' +
|
|
102
|
-
errors.join('\n'));
|
|
103
|
-
}
|
|
92
|
+
}, {});
|
|
93
|
+
const allFiles = await Promise.all(unfilteredFlowFiles.map((filePath) => checkDependencies(filePath))).then((results) => [...new Set(results.flat())]);
|
|
104
94
|
const allIncludeTags = [
|
|
105
95
|
...includeTags,
|
|
106
96
|
...(workspaceConfig.includeTags || []),
|
|
@@ -109,7 +99,7 @@ async function plan(input, includeTags, excludeTags, excludeFlows) {
|
|
|
109
99
|
...excludeTags,
|
|
110
100
|
...(workspaceConfig.excludeTags || []),
|
|
111
101
|
];
|
|
112
|
-
const allFlows =
|
|
102
|
+
const allFlows = unfilteredFlowFiles.filter((filePath) => {
|
|
113
103
|
const config = configPerFlowFile[filePath];
|
|
114
104
|
const tags = config?.tags || [];
|
|
115
105
|
return ((allIncludeTags.length === 0 ||
|
|
@@ -142,4 +132,3 @@ async function plan(input, includeTags, excludeTags, excludeFlows) {
|
|
|
142
132
|
totalFlowFiles: unfilteredFlowFiles.length,
|
|
143
133
|
};
|
|
144
134
|
}
|
|
145
|
-
exports.plan = plan;
|
package/dist/planMethods.d.ts
CHANGED
|
@@ -10,18 +10,17 @@ export declare const readTestYamlFileAsJson: (filePath: string) => {
|
|
|
10
10
|
config: null;
|
|
11
11
|
testSteps: Record<string, unknown>[];
|
|
12
12
|
};
|
|
13
|
-
export declare function
|
|
14
|
-
export declare const checkIfFilesExistInWorkspace: (commandName: string, command: Record<string, string> | string | string[],
|
|
13
|
+
export declare function readDirectory(dir: string, filterFunction?: (filePath: string) => boolean): Promise<string[]>;
|
|
14
|
+
export declare const checkIfFilesExistInWorkspace: (commandName: string, command: Record<string, string> | string | string[], absoluteFilePath: string) => {
|
|
15
15
|
errors: string[];
|
|
16
16
|
files: string[];
|
|
17
17
|
};
|
|
18
18
|
interface IProcessDependencies {
|
|
19
19
|
config?: Record<string, unknown> | null;
|
|
20
|
-
directory: string;
|
|
21
20
|
input: string;
|
|
22
21
|
testSteps: Record<string, unknown>[];
|
|
23
22
|
}
|
|
24
|
-
export declare const processDependencies: ({ config,
|
|
23
|
+
export declare const processDependencies: ({ config, input, testSteps, }: IProcessDependencies) => {
|
|
25
24
|
allErrors: string[];
|
|
26
25
|
allFiles: string[];
|
|
27
26
|
};
|
package/dist/planMethods.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.processDependencies = exports.checkIfFilesExistInWorkspace = exports.
|
|
3
|
+
exports.processDependencies = exports.checkIfFilesExistInWorkspace = exports.readTestYamlFileAsJson = exports.readYamlFileAsJson = void 0;
|
|
4
|
+
exports.getFlowsToRunInSequence = getFlowsToRunInSequence;
|
|
5
|
+
exports.isFlowFile = isFlowFile;
|
|
6
|
+
exports.readDirectory = readDirectory;
|
|
4
7
|
/* eslint-disable unicorn/filename-case */
|
|
5
8
|
const yaml = require("js-yaml");
|
|
6
9
|
const fs = require("node:fs");
|
|
@@ -28,11 +31,9 @@ function getFlowsToRunInSequence(paths, flowOrder) {
|
|
|
28
31
|
return [];
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
|
-
exports.getFlowsToRunInSequence = getFlowsToRunInSequence;
|
|
32
34
|
function isFlowFile(filePath) {
|
|
33
35
|
return filePath.endsWith('.yaml') || filePath.endsWith('.yml');
|
|
34
36
|
}
|
|
35
|
-
exports.isFlowFile = isFlowFile;
|
|
36
37
|
const readYamlFileAsJson = (filePath) => {
|
|
37
38
|
const yamlText = fs.readFileSync(filePath, 'utf8');
|
|
38
39
|
return yaml.load(yamlText);
|
|
@@ -55,13 +56,11 @@ const readTestYamlFileAsJson = (filePath) => {
|
|
|
55
56
|
return { config: null, testSteps };
|
|
56
57
|
};
|
|
57
58
|
exports.readTestYamlFileAsJson = readTestYamlFileAsJson;
|
|
58
|
-
async function
|
|
59
|
+
async function readDirectory(dir, filterFunction) {
|
|
59
60
|
const readDirResult = await fs.promises.readdir(dir);
|
|
60
61
|
const files = await Promise.all(readDirResult.map(async (file) => {
|
|
61
62
|
const filePath = path.join(dir, file);
|
|
62
63
|
const stats = await fs.promises.stat(filePath);
|
|
63
|
-
if (stats.isDirectory())
|
|
64
|
-
return walk(filePath, filterFunction);
|
|
65
64
|
if (stats.isFile())
|
|
66
65
|
if (filterFunction) {
|
|
67
66
|
if (filterFunction(filePath))
|
|
@@ -73,22 +72,22 @@ async function walk(dir, filterFunction) {
|
|
|
73
72
|
}));
|
|
74
73
|
return files.flat().filter(Boolean);
|
|
75
74
|
}
|
|
76
|
-
|
|
77
|
-
const checkIfFilesExistInWorkspace = (commandName, command, filePath, cleanPath) => {
|
|
75
|
+
const checkIfFilesExistInWorkspace = (commandName, command, absoluteFilePath) => {
|
|
78
76
|
const errors = [];
|
|
79
77
|
const files = [];
|
|
80
|
-
const directory = path.dirname(
|
|
81
|
-
const buildError = (error) => `Flow file "${
|
|
78
|
+
const directory = path.dirname(absoluteFilePath);
|
|
79
|
+
const buildError = (error) => `Flow file "${absoluteFilePath}" has a command "${commandName}" that references a ${error} ${JSON.stringify(command)}`;
|
|
82
80
|
const processFilePath = (relativePath) => {
|
|
83
81
|
const absoluteFilePath = path.resolve(directory, relativePath);
|
|
84
|
-
const error = checkFile(absoluteFilePath
|
|
82
|
+
const error = checkFile(absoluteFilePath);
|
|
85
83
|
if (error)
|
|
86
84
|
errors.push(buildError(error));
|
|
87
|
-
files.push(absoluteFilePath
|
|
85
|
+
files.push(absoluteFilePath);
|
|
88
86
|
};
|
|
89
87
|
// simple command
|
|
90
|
-
if (typeof command === 'string')
|
|
91
|
-
processFilePath(command);
|
|
88
|
+
if (typeof command === 'string') {
|
|
89
|
+
processFilePath(path.normalize(directory + '/' + command));
|
|
90
|
+
}
|
|
92
91
|
// array command
|
|
93
92
|
if (Array.isArray(command)) {
|
|
94
93
|
for (const file of command) {
|
|
@@ -102,13 +101,11 @@ const checkIfFilesExistInWorkspace = (commandName, command, filePath, cleanPath)
|
|
|
102
101
|
return { errors, files };
|
|
103
102
|
};
|
|
104
103
|
exports.checkIfFilesExistInWorkspace = checkIfFilesExistInWorkspace;
|
|
105
|
-
const checkFile = (filePath
|
|
104
|
+
const checkFile = (filePath) => {
|
|
106
105
|
if (!fs.existsSync(filePath))
|
|
107
106
|
return `non-existent file`;
|
|
108
|
-
if (!filePath.startsWith(cleanPath))
|
|
109
|
-
return `file outside the workspace`;
|
|
110
107
|
};
|
|
111
|
-
const checkStepsArray = (steps,
|
|
108
|
+
const checkStepsArray = (steps, absoluteFilePath) => {
|
|
112
109
|
let errors = [];
|
|
113
110
|
let files = [];
|
|
114
111
|
for (const command of steps) {
|
|
@@ -116,14 +113,14 @@ const checkStepsArray = (steps, input, directory) => {
|
|
|
116
113
|
continue;
|
|
117
114
|
for (const [commandName, commandValue] of Object.entries(command)) {
|
|
118
115
|
if (commandsThatRequireFiles.has(commandName)) {
|
|
119
|
-
const { errors: newErrors, files: newFiles } = (0, exports.checkIfFilesExistInWorkspace)(commandName, commandValue, path.normalize(
|
|
116
|
+
const { errors: newErrors, files: newFiles } = (0, exports.checkIfFilesExistInWorkspace)(commandName, commandValue, path.normalize(absoluteFilePath));
|
|
120
117
|
errors = [...errors, ...newErrors];
|
|
121
118
|
files = [...files, ...newFiles];
|
|
122
119
|
}
|
|
123
120
|
const nestedCommands = typeof commandValue === 'object' &&
|
|
124
121
|
commandValue.commands;
|
|
125
122
|
if (nestedCommands) {
|
|
126
|
-
const { errors: newErrors, files: newFiles } = checkStepsArray(nestedCommands,
|
|
123
|
+
const { errors: newErrors, files: newFiles } = checkStepsArray(nestedCommands, absoluteFilePath);
|
|
127
124
|
errors = [...errors, ...newErrors];
|
|
128
125
|
files = [...files, ...newFiles];
|
|
129
126
|
}
|
|
@@ -131,7 +128,7 @@ const checkStepsArray = (steps, input, directory) => {
|
|
|
131
128
|
}
|
|
132
129
|
return { errors, files };
|
|
133
130
|
};
|
|
134
|
-
const processDependencies = ({ config,
|
|
131
|
+
const processDependencies = ({ config, input, testSteps, }) => {
|
|
135
132
|
let allErrors = [];
|
|
136
133
|
let allFiles = [];
|
|
137
134
|
const { onFlowComplete, onFlowStart } = config ?? {};
|
|
@@ -142,7 +139,7 @@ const processDependencies = ({ config, directory, input, testSteps, }) => {
|
|
|
142
139
|
stepsArray.push(onFlowComplete);
|
|
143
140
|
for (const [index, steps] of stepsArray.entries()) {
|
|
144
141
|
try {
|
|
145
|
-
const { errors, files } = checkStepsArray(steps, input
|
|
142
|
+
const { errors, files } = checkStepsArray(steps, input);
|
|
146
143
|
allErrors = [...allErrors, ...errors];
|
|
147
144
|
allFiles = [...allFiles, ...files];
|
|
148
145
|
}
|
package/oclif.manifest.json
CHANGED
|
@@ -19,6 +19,22 @@
|
|
|
19
19
|
"<%= config.bin %> <%= command.id %>"
|
|
20
20
|
],
|
|
21
21
|
"flags": {
|
|
22
|
+
"additional-app-binary-ids": {
|
|
23
|
+
"description": "The ID of the additional app binary(s) previously uploaded to devicecloud.dev to install before execution",
|
|
24
|
+
"name": "additional-app-binary-ids",
|
|
25
|
+
"default": [],
|
|
26
|
+
"hasDynamicHelp": false,
|
|
27
|
+
"multiple": true,
|
|
28
|
+
"type": "option"
|
|
29
|
+
},
|
|
30
|
+
"additional-app-files": {
|
|
31
|
+
"description": "Additional app binary(s) to install before execution",
|
|
32
|
+
"name": "additional-app-files",
|
|
33
|
+
"default": [],
|
|
34
|
+
"hasDynamicHelp": false,
|
|
35
|
+
"multiple": true,
|
|
36
|
+
"type": "option"
|
|
37
|
+
},
|
|
22
38
|
"android-api-level": {
|
|
23
39
|
"description": "[Android only] Android API level to run your flow against",
|
|
24
40
|
"name": "android-api-level",
|
|
@@ -275,5 +291,5 @@
|
|
|
275
291
|
]
|
|
276
292
|
}
|
|
277
293
|
},
|
|
278
|
-
"version": "
|
|
294
|
+
"version": "2.0.0-rc.3"
|
|
279
295
|
}
|
package/package.json
CHANGED
|
@@ -64,10 +64,6 @@
|
|
|
64
64
|
]
|
|
65
65
|
},
|
|
66
66
|
"private": false,
|
|
67
|
-
"repository": {
|
|
68
|
-
"type": "git",
|
|
69
|
-
"url": "@devicecloud.dev/dcd"
|
|
70
|
-
},
|
|
71
67
|
"scripts": {
|
|
72
68
|
"dcd": "./bin/dev.js",
|
|
73
69
|
"prod": "./bin/run.js",
|
|
@@ -80,7 +76,7 @@
|
|
|
80
76
|
"test": "mocha --forbid-only \"test/**/*.test.ts\"",
|
|
81
77
|
"version": "oclif readme && git add README.md"
|
|
82
78
|
},
|
|
83
|
-
"version": "
|
|
79
|
+
"version": "2.0.0-rc.3",
|
|
84
80
|
"bugs": {
|
|
85
81
|
"url": "https://discord.gg/gm3mJwcNw8"
|
|
86
82
|
},
|