@devicecloud.dev/dcd 3.5.0 → 3.6.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/dist/commands/cloud.d.ts +2 -0
- package/dist/commands/cloud.js +17 -4
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +9 -2
- package/dist/methods.d.ts +1 -1
- package/dist/methods.js +22 -2
- package/dist/plan.d.ts +1 -1
- package/dist/plan.js +18 -4
- package/oclif.manifest.json +21 -3
- package/package.json +2 -3
package/dist/commands/cloud.d.ts
CHANGED
|
@@ -39,8 +39,10 @@ export default class Cloud extends Command {
|
|
|
39
39
|
'app-binary-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
40
40
|
'app-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
41
41
|
async: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
42
|
+
config: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
42
43
|
'device-locale': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
43
44
|
'download-artifacts': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
45
|
+
'artifacts-path': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
44
46
|
env: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
45
47
|
'exclude-flows': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
46
48
|
'exclude-tags': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
package/dist/commands/cloud.js
CHANGED
|
@@ -80,7 +80,7 @@ class Cloud extends core_1.Command {
|
|
|
80
80
|
let output = null;
|
|
81
81
|
try {
|
|
82
82
|
const { args, flags, raw } = await this.parse(Cloud);
|
|
83
|
-
let { 'additional-app-binary-ids': nonFlatAdditionalAppBinaryIds, 'additional-app-files': nonFlatAdditionalAppFiles, 'android-api-level': androidApiLevel, 'android-device': androidDevice, apiKey: apiKeyFlag, apiUrl, 'app-binary-id': appBinaryId, 'app-file': appFile, async, 'device-locale': deviceLocale, 'download-artifacts': downloadArtifacts, env, 'exclude-flows': excludeFlows, 'exclude-tags': excludeTags, flows, 'google-play': googlePlay, 'include-tags': includeTags, 'ignore-sha-check': ignoreShaCheck, 'ios-device': iOSDevice, 'ios-version': iOSVersion, 'maestro-version': maestroVersion, name, orientation, quiet, retry, report, 'runner-type': runnerType, 'x86-arch': x86Arch, json, ...rest } = flags;
|
|
83
|
+
let { 'additional-app-binary-ids': nonFlatAdditionalAppBinaryIds, 'additional-app-files': nonFlatAdditionalAppFiles, 'android-api-level': androidApiLevel, 'android-device': androidDevice, apiKey: apiKeyFlag, apiUrl, 'app-binary-id': appBinaryId, 'app-file': appFile, 'artifacts-path': artifactsPath, async, config: configFile, 'device-locale': deviceLocale, 'download-artifacts': downloadArtifacts, env, 'exclude-flows': excludeFlows, 'exclude-tags': excludeTags, flows, 'google-play': googlePlay, 'include-tags': includeTags, 'ignore-sha-check': ignoreShaCheck, 'ios-device': iOSDevice, 'ios-version': iOSVersion, 'maestro-version': maestroVersion, name, orientation, quiet, retry, report, 'runner-type': runnerType, 'x86-arch': x86Arch, json, ...rest } = flags;
|
|
84
84
|
// If in JSON mode, temporarily intercept stdout to suppress the warning
|
|
85
85
|
if (json) {
|
|
86
86
|
const originalStdoutWrite = process.stdout.write;
|
|
@@ -118,6 +118,19 @@ class Cloud extends core_1.Command {
|
|
|
118
118
|
throw new Error('runnerType m4 only supports iOS');
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
+
if (runnerType === 'm1') {
|
|
122
|
+
this.log('Note: Runner Type m1 is experimental and currently supports Android only, iOS will revert to default.');
|
|
123
|
+
// todo - better platform checking
|
|
124
|
+
if (iOSDevice || iOSVersion) {
|
|
125
|
+
this.log('runnerType m1 only supports Android, reverting to default');
|
|
126
|
+
runnerType = 'default';
|
|
127
|
+
}
|
|
128
|
+
if (androidApiLevel || androidDevice) {
|
|
129
|
+
this.log('Runner Type m1 only supports API Level 34 and Pixel 7, unsetting your android options.');
|
|
130
|
+
androidApiLevel = undefined;
|
|
131
|
+
androidDevice = undefined;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
121
134
|
const additionalAppBinaryIds = nonFlatAdditionalAppBinaryIds?.flat();
|
|
122
135
|
const additionalAppFiles = nonFlatAdditionalAppFiles?.flat();
|
|
123
136
|
const { firstFile, secondFile } = args;
|
|
@@ -164,7 +177,7 @@ class Cloud extends core_1.Command {
|
|
|
164
177
|
}
|
|
165
178
|
let executionPlan;
|
|
166
179
|
try {
|
|
167
|
-
executionPlan = await (0, plan_1.plan)(flowFile, includeTags.flat(), excludeTags.flat(), excludeFlows.flat());
|
|
180
|
+
executionPlan = await (0, plan_1.plan)(flowFile, includeTags.flat(), excludeTags.flat(), excludeFlows.flat(), configFile);
|
|
168
181
|
}
|
|
169
182
|
catch (error) {
|
|
170
183
|
throw error;
|
|
@@ -375,9 +388,9 @@ class Cloud extends core_1.Command {
|
|
|
375
388
|
'content-type': 'application/json',
|
|
376
389
|
'x-app-api-key': apiKey,
|
|
377
390
|
},
|
|
378
|
-
});
|
|
391
|
+
}, artifactsPath);
|
|
379
392
|
this.log('\n');
|
|
380
|
-
this.log(
|
|
393
|
+
this.log(`Test artifacts have been downloaded to ${artifactsPath || './artifacts.zip'}`);
|
|
381
394
|
}
|
|
382
395
|
catch {
|
|
383
396
|
this.warn('Failed to download artifacts');
|
package/dist/constants.d.ts
CHANGED
|
@@ -9,8 +9,10 @@ export declare const flags: {
|
|
|
9
9
|
'app-binary-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
10
|
'app-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
11
|
async: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
config: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
13
|
'device-locale': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
14
|
'download-artifacts': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
|
+
'artifacts-path': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
16
|
env: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
17
|
'exclude-flows': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
16
18
|
'exclude-tags': import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
package/dist/constants.js
CHANGED
|
@@ -52,6 +52,9 @@ exports.flags = {
|
|
|
52
52
|
async: core_1.Flags.boolean({
|
|
53
53
|
description: 'Immediately return (exit code 0) from the command without waiting for the results of the run (useful for saving CI minutes)',
|
|
54
54
|
}),
|
|
55
|
+
config: core_1.Flags.file({
|
|
56
|
+
description: 'Path to custom config.yaml file. If not provided, defaults to config.yaml in root flows folders.',
|
|
57
|
+
}),
|
|
55
58
|
'device-locale': core_1.Flags.string({
|
|
56
59
|
description: 'Locale that will be set to a device, ISO-639-1 code and uppercase ISO-3166-1 code e.g. "de_DE" for Germany',
|
|
57
60
|
}),
|
|
@@ -59,6 +62,10 @@ exports.flags = {
|
|
|
59
62
|
description: 'Download a zip containing the logs, screenshots and videos for each result in this run. You will debited a $0.01 egress fee for each result. Use --download-artifacts=FAILED for failures only or --download-artifacts=ALL for every result.',
|
|
60
63
|
options: ['ALL', 'FAILED'],
|
|
61
64
|
}),
|
|
65
|
+
'artifacts-path': core_1.Flags.string({
|
|
66
|
+
description: 'Custom file path for downloaded artifacts (default: ./artifacts.zip)',
|
|
67
|
+
dependsOn: ['download-artifacts'],
|
|
68
|
+
}),
|
|
62
69
|
env: core_1.Flags.file({
|
|
63
70
|
char: 'e',
|
|
64
71
|
description: 'One or more environment variables to inject into your flows',
|
|
@@ -162,9 +169,9 @@ exports.flags = {
|
|
|
162
169
|
description: 'Automatically retry the run up to the number of times specified (same as pressing retry in the UI) - this is free of charge',
|
|
163
170
|
}),
|
|
164
171
|
'runner-type': core_1.Flags.string({
|
|
165
|
-
description: '[
|
|
172
|
+
description: '[experimental] The type of runner to use - note: anything other than default will incur premium pricing tiers, see https://docs.devicecloud.dev/reference/runner-type for more information',
|
|
166
173
|
default: 'default',
|
|
167
|
-
options: ['default', 'm4'],
|
|
174
|
+
options: ['default', 'm4', 'm1'],
|
|
168
175
|
}),
|
|
169
176
|
report: core_1.Flags.string({
|
|
170
177
|
aliases: ['format'],
|
package/dist/methods.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare const typeSafePost: <T extends keyof paths>(baseUrl: string, path
|
|
|
8
8
|
export declare const typeSafePostDownload: (baseUrl: string, path: string, init?: {
|
|
9
9
|
body?: BodyInit;
|
|
10
10
|
headers?: HeadersInit;
|
|
11
|
-
}) => Promise<void>;
|
|
11
|
+
}, artifactsPath?: string) => Promise<void>;
|
|
12
12
|
export declare const typeSafeGet: <T extends keyof paths>(baseUrl: string, path: string, init?: {
|
|
13
13
|
body?: FormData;
|
|
14
14
|
headers?: HeadersInit;
|
package/dist/methods.js
CHANGED
|
@@ -18,6 +18,8 @@ const StreamZip = require("node-stream-zip");
|
|
|
18
18
|
const plist_1 = require("plist");
|
|
19
19
|
const node_crypto_1 = require("node:crypto");
|
|
20
20
|
const path = require("path");
|
|
21
|
+
const node_path_1 = require("node:path");
|
|
22
|
+
const os = require("node:os");
|
|
21
23
|
const cloud_1 = require("./commands/cloud");
|
|
22
24
|
const PERMITTED_EXTENSIONS = new Set([
|
|
23
25
|
'yml',
|
|
@@ -40,7 +42,7 @@ const typeSafePost = async (baseUrl, path, init) => {
|
|
|
40
42
|
return res.json();
|
|
41
43
|
};
|
|
42
44
|
exports.typeSafePost = typeSafePost;
|
|
43
|
-
const typeSafePostDownload = async (baseUrl, path, init) => {
|
|
45
|
+
const typeSafePostDownload = async (baseUrl, path, init, artifactsPath) => {
|
|
44
46
|
const res = await fetch(baseUrl + path, {
|
|
45
47
|
...init,
|
|
46
48
|
method: 'POST',
|
|
@@ -48,7 +50,25 @@ const typeSafePostDownload = async (baseUrl, path, init) => {
|
|
|
48
50
|
if (!res.ok) {
|
|
49
51
|
throw new Error(await res.text());
|
|
50
52
|
}
|
|
51
|
-
|
|
53
|
+
let outputPath = artifactsPath || './artifacts.zip';
|
|
54
|
+
// Handle tilde expansion for home directory
|
|
55
|
+
if (outputPath.startsWith('~/') || outputPath === '~') {
|
|
56
|
+
outputPath = outputPath.replace(/^~(?=$|\/|\\)/, os.homedir());
|
|
57
|
+
}
|
|
58
|
+
// Create directory structure if it doesn't exist
|
|
59
|
+
const directory = (0, node_path_1.dirname)(outputPath);
|
|
60
|
+
if (directory !== '.') {
|
|
61
|
+
try {
|
|
62
|
+
(0, node_fs_1.mkdirSync)(directory, { recursive: true });
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
// Ignore if directory already exists
|
|
66
|
+
if (error.code !== 'EEXIST') {
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const fileStream = (0, node_fs_1.createWriteStream)(outputPath, { flags: 'wx' });
|
|
52
72
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
73
|
await (0, promises_2.finished)(node_stream_1.Readable.fromWeb(res.body).pipe(fileStream));
|
|
54
74
|
};
|
package/dist/plan.d.ts
CHANGED
|
@@ -25,5 +25,5 @@ interface IFlowSequence {
|
|
|
25
25
|
continueOnFailure?: boolean;
|
|
26
26
|
flows: string[];
|
|
27
27
|
}
|
|
28
|
-
export declare function plan(input: string, includeTags: string[], excludeTags: string[], excludeFlows?: string[]): Promise<IExecutionPlan>;
|
|
28
|
+
export declare function plan(input: string, includeTags: string[], excludeTags: string[], excludeFlows?: string[], configFile?: string): Promise<IExecutionPlan>;
|
|
29
29
|
export {};
|
package/dist/plan.js
CHANGED
|
@@ -52,7 +52,7 @@ function getWorkspaceConfig(input, unfilteredFlowFiles) {
|
|
|
52
52
|
: {};
|
|
53
53
|
return config;
|
|
54
54
|
}
|
|
55
|
-
async function plan(input, includeTags, excludeTags, excludeFlows) {
|
|
55
|
+
async function plan(input, includeTags, excludeTags, excludeFlows, configFile) {
|
|
56
56
|
const normalizedInput = path.normalize(input);
|
|
57
57
|
if (!fs.existsSync(normalizedInput)) {
|
|
58
58
|
throw new Error(`Flow path does not exist: ${path.resolve(normalizedInput)}`);
|
|
@@ -60,7 +60,7 @@ async function plan(input, includeTags, excludeTags, excludeFlows) {
|
|
|
60
60
|
if (fs.lstatSync(normalizedInput).isFile()) {
|
|
61
61
|
if (normalizedInput.endsWith('config.yaml') ||
|
|
62
62
|
normalizedInput.endsWith('config.yml')) {
|
|
63
|
-
throw new Error('If using config.yaml, pass the workspace folder path, not the config file');
|
|
63
|
+
throw new Error('If using config.yaml, pass the workspace folder path, not the config file or a custom path via --config');
|
|
64
64
|
}
|
|
65
65
|
const checkedDependancies = await checkDependencies(normalizedInput);
|
|
66
66
|
return {
|
|
@@ -74,7 +74,17 @@ async function plan(input, includeTags, excludeTags, excludeFlows) {
|
|
|
74
74
|
throw new Error(`Flow directory does not contain any Flow files: ${path.resolve(normalizedInput)}`);
|
|
75
75
|
}
|
|
76
76
|
unfilteredFlowFiles = filterFlowFiles(unfilteredFlowFiles, excludeFlows);
|
|
77
|
-
|
|
77
|
+
let workspaceConfig;
|
|
78
|
+
if (configFile) {
|
|
79
|
+
const configFilePath = path.resolve(process.cwd(), configFile);
|
|
80
|
+
if (!fs.existsSync(configFilePath)) {
|
|
81
|
+
throw new Error(`Config file does not exist: ${configFilePath}`);
|
|
82
|
+
}
|
|
83
|
+
workspaceConfig = (0, planMethods_1.readYamlFileAsJson)(configFilePath);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
workspaceConfig = getWorkspaceConfig(normalizedInput, unfilteredFlowFiles);
|
|
87
|
+
}
|
|
78
88
|
if (workspaceConfig.flows) {
|
|
79
89
|
const globs = workspaceConfig.flows.map((glob) => glob);
|
|
80
90
|
const matchedFiles = await (0, glob_1.glob)(globs, {
|
|
@@ -84,12 +94,16 @@ async function plan(input, includeTags, excludeTags, excludeFlows) {
|
|
|
84
94
|
// overwrite the list of files with the globbed ones
|
|
85
95
|
unfilteredFlowFiles = matchedFiles
|
|
86
96
|
.filter((file) => file !== 'config.yaml' &&
|
|
97
|
+
file !== 'config.yml' &&
|
|
98
|
+
(!configFile || file !== path.basename(configFile)) &&
|
|
87
99
|
(file.endsWith('.yaml') || file.endsWith('.yml')))
|
|
88
100
|
.map((file) => path.resolve(normalizedInput, file));
|
|
89
101
|
}
|
|
90
102
|
else {
|
|
91
103
|
// workspace config has no flows, so we need to remove the config file from the test list
|
|
92
|
-
unfilteredFlowFiles = unfilteredFlowFiles.filter((file) => !file.endsWith('config.yaml') &&
|
|
104
|
+
unfilteredFlowFiles = unfilteredFlowFiles.filter((file) => !file.endsWith('config.yaml') &&
|
|
105
|
+
!file.endsWith('config.yml') &&
|
|
106
|
+
(!configFile || !file.endsWith(configFile)));
|
|
93
107
|
}
|
|
94
108
|
if (unfilteredFlowFiles.length === 0) {
|
|
95
109
|
const error = workspaceConfig.flows
|
package/oclif.manifest.json
CHANGED
|
@@ -120,6 +120,13 @@
|
|
|
120
120
|
"allowNo": false,
|
|
121
121
|
"type": "boolean"
|
|
122
122
|
},
|
|
123
|
+
"config": {
|
|
124
|
+
"description": "Path to custom config.yaml file. If not provided, defaults to config.yaml in root flows folders.",
|
|
125
|
+
"name": "config",
|
|
126
|
+
"hasDynamicHelp": false,
|
|
127
|
+
"multiple": false,
|
|
128
|
+
"type": "option"
|
|
129
|
+
},
|
|
123
130
|
"device-locale": {
|
|
124
131
|
"description": "Locale that will be set to a device, ISO-639-1 code and uppercase ISO-3166-1 code e.g. \"de_DE\" for Germany",
|
|
125
132
|
"name": "device-locale",
|
|
@@ -138,6 +145,16 @@
|
|
|
138
145
|
],
|
|
139
146
|
"type": "option"
|
|
140
147
|
},
|
|
148
|
+
"artifacts-path": {
|
|
149
|
+
"dependsOn": [
|
|
150
|
+
"download-artifacts"
|
|
151
|
+
],
|
|
152
|
+
"description": "Custom file path for downloaded artifacts (default: ./artifacts.zip)",
|
|
153
|
+
"name": "artifacts-path",
|
|
154
|
+
"hasDynamicHelp": false,
|
|
155
|
+
"multiple": false,
|
|
156
|
+
"type": "option"
|
|
157
|
+
},
|
|
141
158
|
"env": {
|
|
142
159
|
"char": "e",
|
|
143
160
|
"description": "One or more environment variables to inject into your flows",
|
|
@@ -300,14 +317,15 @@
|
|
|
300
317
|
"type": "option"
|
|
301
318
|
},
|
|
302
319
|
"runner-type": {
|
|
303
|
-
"description": "[
|
|
320
|
+
"description": "[experimental] The type of runner to use - note: anything other than default will incur premium pricing tiers, see https://docs.devicecloud.dev/reference/runner-type for more information",
|
|
304
321
|
"name": "runner-type",
|
|
305
322
|
"default": "default",
|
|
306
323
|
"hasDynamicHelp": false,
|
|
307
324
|
"multiple": false,
|
|
308
325
|
"options": [
|
|
309
326
|
"default",
|
|
310
|
-
"m4"
|
|
327
|
+
"m4",
|
|
328
|
+
"m1"
|
|
311
329
|
],
|
|
312
330
|
"type": "option"
|
|
313
331
|
},
|
|
@@ -483,5 +501,5 @@
|
|
|
483
501
|
]
|
|
484
502
|
}
|
|
485
503
|
},
|
|
486
|
-
"version": "3.
|
|
504
|
+
"version": "3.6.1"
|
|
487
505
|
}
|
package/package.json
CHANGED
|
@@ -71,16 +71,15 @@
|
|
|
71
71
|
"scripts": {
|
|
72
72
|
"dcd": "./bin/dev.js",
|
|
73
73
|
"prod": "./bin/run.js",
|
|
74
|
-
"build": "shx rm -rf dist && tsc -b",
|
|
74
|
+
"build": "shx rm -rf dist && tsc -b",
|
|
75
75
|
"lint": "eslint . --ext .ts",
|
|
76
76
|
"postpack": "shx rm -f oclif.manifest.json",
|
|
77
77
|
"posttest": "yarn lint",
|
|
78
78
|
"prepack": "yarn build && oclif manifest",
|
|
79
79
|
"prepare": "yarn build",
|
|
80
|
-
"test": "mocha --forbid-only \"test/**/*.test.ts\"",
|
|
81
80
|
"version": "oclif readme && git add README.md"
|
|
82
81
|
},
|
|
83
|
-
"version": "3.
|
|
82
|
+
"version": "3.6.1",
|
|
84
83
|
"bugs": {
|
|
85
84
|
"url": "https://discord.gg/gm3mJwcNw8"
|
|
86
85
|
},
|