@devicecloud.dev/dcd 0.0.1-alpha.1 → 0.0.1-alpha.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 +2 -1
- package/dist/commands/cloud.js +91 -27
- package/dist/plan.d.ts +10 -0
- package/dist/plan.js +152 -0
- package/oclif.manifest.json +22 -3
- package/package.json +6 -2
package/dist/commands/cloud.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
export default class Cloud extends Command {
|
|
3
3
|
static args: {
|
|
4
|
-
firstFile: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
4
|
+
firstFile: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
5
|
secondFile: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
6
6
|
};
|
|
7
7
|
static description: string;
|
|
@@ -13,6 +13,7 @@ export default class Cloud extends Command {
|
|
|
13
13
|
appBinaryId: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
14
|
appFile: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
15
|
env: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
16
|
+
flows: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
16
17
|
iOSVersion: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
17
18
|
};
|
|
18
19
|
run(): Promise<void>;
|
package/dist/commands/cloud.js
CHANGED
|
@@ -2,13 +2,24 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
4
|
const archiver = require("archiver");
|
|
5
|
-
const node_fs_1 = require("node:fs");
|
|
6
5
|
const promises_1 = require("node:fs/promises");
|
|
6
|
+
const node_stream_1 = require("node:stream");
|
|
7
|
+
const plan_1 = require("../plan");
|
|
7
8
|
const mimeTypeLookupByExtension = {
|
|
8
9
|
apk: 'application/vnd.android.package-archive',
|
|
9
10
|
yaml: 'application/x-yaml',
|
|
10
11
|
zip: 'application/zip',
|
|
11
12
|
};
|
|
13
|
+
const PERMITTED_EXTENSIONS = new Set([
|
|
14
|
+
'yml',
|
|
15
|
+
'yaml',
|
|
16
|
+
'png',
|
|
17
|
+
'jpg',
|
|
18
|
+
'jpeg',
|
|
19
|
+
'gif',
|
|
20
|
+
'mp4',
|
|
21
|
+
'js',
|
|
22
|
+
]);
|
|
12
23
|
const typeSafeFetch = async (baseUrl, path, init) => {
|
|
13
24
|
const res = await fetch(baseUrl + path, init);
|
|
14
25
|
if (!res.ok) {
|
|
@@ -16,17 +27,37 @@ const typeSafeFetch = async (baseUrl, path, init) => {
|
|
|
16
27
|
}
|
|
17
28
|
return res.json();
|
|
18
29
|
};
|
|
19
|
-
const
|
|
20
|
-
const
|
|
30
|
+
const toBuffer = async (archive) => {
|
|
31
|
+
const chunks = [];
|
|
32
|
+
const writable = new node_stream_1.Writable();
|
|
33
|
+
writable._write = (chunk, _, callback) => {
|
|
34
|
+
// save to array to concatenate later
|
|
35
|
+
chunks.push(chunk);
|
|
36
|
+
callback();
|
|
37
|
+
};
|
|
38
|
+
// pipe to writable
|
|
39
|
+
archive.pipe(writable);
|
|
40
|
+
await archive.finalize();
|
|
41
|
+
// once done, concatenate chunks
|
|
42
|
+
return Buffer.concat(chunks);
|
|
43
|
+
};
|
|
44
|
+
const compress = async (sourceDir) => {
|
|
45
|
+
// const output = createWriteStream(zipTargetPath);
|
|
21
46
|
const archive = archiver('zip', {
|
|
22
47
|
zlib: { level: 9 },
|
|
23
48
|
});
|
|
24
49
|
archive.on('error', (err) => {
|
|
25
50
|
throw err;
|
|
26
51
|
});
|
|
27
|
-
archive.pipe(output);
|
|
28
|
-
archive.directory(sourceDir, false)
|
|
29
|
-
|
|
52
|
+
// archive.pipe(output);
|
|
53
|
+
archive.directory(sourceDir, false, (data) => {
|
|
54
|
+
if (PERMITTED_EXTENSIONS.has(data.name.split('.').pop())) {
|
|
55
|
+
return data;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
});
|
|
59
|
+
const buffer = await toBuffer(archive);
|
|
60
|
+
return buffer;
|
|
30
61
|
};
|
|
31
62
|
class Cloud extends core_1.Command {
|
|
32
63
|
static args = {
|
|
@@ -34,7 +65,6 @@ class Cloud extends core_1.Command {
|
|
|
34
65
|
description: 'The binary file of the app to run your flow against, e.g. app.apk for android or app.zip for ios',
|
|
35
66
|
hidden: true,
|
|
36
67
|
name: 'App file',
|
|
37
|
-
required: true,
|
|
38
68
|
}),
|
|
39
69
|
secondFile: core_1.Args.string({
|
|
40
70
|
description: 'The flow file to run against the app, e.g. test.yaml',
|
|
@@ -48,12 +78,14 @@ class Cloud extends core_1.Command {
|
|
|
48
78
|
androidApiLevel: core_1.Flags.integer({
|
|
49
79
|
aliases: ['android-api-level'],
|
|
50
80
|
description: 'Android API level to run your flow against',
|
|
81
|
+
options: ['32', '33', '34'],
|
|
51
82
|
}),
|
|
52
83
|
apiKey: core_1.Flags.string({ aliases: ['api-key'], description: 'API key' }),
|
|
53
84
|
apiUrl: core_1.Flags.string({
|
|
54
85
|
aliases: ['api-url'],
|
|
55
86
|
default: 'https://api.devicecloud.dev',
|
|
56
87
|
description: 'API base URL',
|
|
88
|
+
hidden: true,
|
|
57
89
|
}),
|
|
58
90
|
appBinaryId: core_1.Flags.string({
|
|
59
91
|
aliases: ['app-binary-id'],
|
|
@@ -69,42 +101,47 @@ class Cloud extends core_1.Command {
|
|
|
69
101
|
description: 'One or more environment variables to inject into your Flows',
|
|
70
102
|
multiple: true,
|
|
71
103
|
}),
|
|
104
|
+
flows: core_1.Flags.string({
|
|
105
|
+
aliases: ['flows'],
|
|
106
|
+
description: 'The path to the flow file or folder containing your Flows',
|
|
107
|
+
}),
|
|
72
108
|
iOSVersion: core_1.Flags.string({
|
|
73
109
|
aliases: ['ios-version'],
|
|
74
110
|
description: 'iOS version to run your flow against',
|
|
111
|
+
options: ['16.4', '17.2'],
|
|
75
112
|
}),
|
|
76
113
|
};
|
|
77
114
|
async run() {
|
|
78
115
|
const { args, flags } = await this.parse(Cloud);
|
|
79
|
-
const { apiKey, apiUrl, appBinaryId, ...rest } = flags;
|
|
116
|
+
const { apiKey, apiUrl, appBinaryId, appFile, env, flows, ...rest } = flags;
|
|
80
117
|
console.log({ args });
|
|
81
118
|
const { firstFile, secondFile } = args;
|
|
82
119
|
let finalBinaryId = appBinaryId;
|
|
83
|
-
const
|
|
84
|
-
let flowFile = secondFile;
|
|
120
|
+
const finalAppFile = appFile ?? firstFile;
|
|
121
|
+
let flowFile = flows ?? secondFile;
|
|
85
122
|
if (appBinaryId) {
|
|
86
123
|
if (secondFile) {
|
|
87
124
|
throw new Error('You cannot provide both an appBinaryId and a binary file');
|
|
88
125
|
}
|
|
89
|
-
flowFile = firstFile;
|
|
90
|
-
this.log(`you want to run the flow ${flowFile} against the binary with id ${appBinaryId} with the following flags: ${JSON.stringify(flags)}`);
|
|
126
|
+
flowFile = flows ?? firstFile;
|
|
127
|
+
this.log(`you want to run the flow(s) ${flowFile} against the binary with id ${appBinaryId} with the following flags: ${JSON.stringify(flags)}`);
|
|
91
128
|
}
|
|
92
129
|
else {
|
|
93
|
-
if (!
|
|
94
|
-
throw new Error('App file must be a .apk or .zip file');
|
|
95
|
-
}
|
|
96
|
-
if (!(flowFile && appFile)) {
|
|
130
|
+
if (!(flowFile && finalAppFile)) {
|
|
97
131
|
throw new Error('You must provide a flow file and an app binary id');
|
|
98
132
|
}
|
|
99
|
-
|
|
133
|
+
if (!finalAppFile.endsWith('.apk') && !finalAppFile.endsWith('.zip')) {
|
|
134
|
+
throw new Error('App file must be a .apk or .zip file');
|
|
135
|
+
}
|
|
136
|
+
this.log(`you want to run the flow(s) ${flowFile} against the app ${finalAppFile} with the following flags: ${JSON.stringify(flags)}`);
|
|
100
137
|
}
|
|
101
138
|
if (!finalBinaryId) {
|
|
102
139
|
const binaryFormData = new FormData();
|
|
103
|
-
const binaryBlob = new Blob([await (0, promises_1.readFile)(
|
|
104
|
-
type: mimeTypeLookupByExtension[
|
|
140
|
+
const binaryBlob = new Blob([await (0, promises_1.readFile)(finalAppFile)], {
|
|
141
|
+
type: mimeTypeLookupByExtension[finalAppFile.split('.').pop()],
|
|
105
142
|
});
|
|
106
|
-
console.log(mimeTypeLookupByExtension[
|
|
107
|
-
binaryFormData.set('file', binaryBlob,
|
|
143
|
+
console.log(mimeTypeLookupByExtension[finalAppFile.split('.').pop()]);
|
|
144
|
+
binaryFormData.set('file', binaryBlob, finalAppFile);
|
|
108
145
|
const options = {
|
|
109
146
|
body: binaryFormData,
|
|
110
147
|
headers: { 'x-app-api-key': apiKey },
|
|
@@ -116,17 +153,44 @@ class Cloud extends core_1.Command {
|
|
|
116
153
|
this.log(message);
|
|
117
154
|
finalBinaryId = binaryId;
|
|
118
155
|
}
|
|
156
|
+
const testFileNames = [];
|
|
157
|
+
let flowFileDirectory = flowFile;
|
|
158
|
+
if (flowFile?.endsWith('/')) {
|
|
159
|
+
try {
|
|
160
|
+
const executionPlan = await (0, plan_1.plan)(flowFile, [], []);
|
|
161
|
+
for (const file of executionPlan.flowsToRun) {
|
|
162
|
+
testFileNames.push(file);
|
|
163
|
+
}
|
|
164
|
+
for (const file of executionPlan.sequence?.flows ?? []) {
|
|
165
|
+
// todo: handle continueOnFailure and other sequence properties
|
|
166
|
+
testFileNames.push(file);
|
|
167
|
+
}
|
|
168
|
+
console.log(executionPlan);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error(error);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// we are working with a single file
|
|
176
|
+
flowFileDirectory = flowFile.split('/').slice(0, -1).join('/');
|
|
177
|
+
testFileNames.push(flowFile.split('/').pop());
|
|
178
|
+
}
|
|
119
179
|
const testFormData = new FormData();
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
180
|
+
// eslint-disable-next-line unicorn/no-array-reduce
|
|
181
|
+
const envObject = (env ?? []).reduce((acc, cur) => {
|
|
182
|
+
const [key, value] = cur.split('=');
|
|
183
|
+
acc[key] = value;
|
|
184
|
+
return acc;
|
|
185
|
+
}, {});
|
|
186
|
+
const buffer = await compress(flowFileDirectory);
|
|
187
|
+
const blob = new Blob([buffer], {
|
|
125
188
|
type: mimeTypeLookupByExtension.zip,
|
|
126
189
|
});
|
|
127
190
|
testFormData.set('file', blob, 'flowFile.zip');
|
|
128
191
|
testFormData.set('appBinaryId', finalBinaryId);
|
|
129
|
-
testFormData.set('
|
|
192
|
+
testFormData.set('testFileNames', JSON.stringify(testFileNames));
|
|
193
|
+
testFormData.set('env', JSON.stringify(envObject));
|
|
130
194
|
for (const [key, value] of Object.entries(rest)) {
|
|
131
195
|
if (value) {
|
|
132
196
|
testFormData.set(key, value);
|
package/dist/plan.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface IExecutionPlan {
|
|
2
|
+
flowsToRun: string[];
|
|
3
|
+
sequence?: IFlowSequence | null;
|
|
4
|
+
}
|
|
5
|
+
interface IFlowSequence {
|
|
6
|
+
continueOnFailure?: boolean;
|
|
7
|
+
flows: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function plan(input: string, includeTags: string[], excludeTags: string[]): Promise<IExecutionPlan>;
|
|
10
|
+
export {};
|
package/dist/plan.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.plan = void 0;
|
|
4
|
+
const globToRegExp = require("glob-to-regexp");
|
|
5
|
+
const yaml = require("js-yaml");
|
|
6
|
+
const fs = require("node:fs");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
function getFlowsToRunInSequence(paths, flowOrder) {
|
|
9
|
+
if (flowOrder.length === 0)
|
|
10
|
+
return [];
|
|
11
|
+
const orderSet = new Set(flowOrder);
|
|
12
|
+
const namesInOrder = Object.keys(paths).filter((key) => orderSet.has(key));
|
|
13
|
+
if (namesInOrder.length === 0)
|
|
14
|
+
return [];
|
|
15
|
+
const result = [...orderSet].filter((item) => namesInOrder.includes(item));
|
|
16
|
+
if (result.length === 0) {
|
|
17
|
+
throw new Error(`Could not find flows needed for execution in order: ${[...orderSet]
|
|
18
|
+
.filter((item) => !namesInOrder.includes(item))
|
|
19
|
+
.join(', ')}`);
|
|
20
|
+
}
|
|
21
|
+
else if (flowOrder
|
|
22
|
+
.slice(0, result.length)
|
|
23
|
+
.every((value, index) => value === result[index])) {
|
|
24
|
+
return result.map((item) => paths[item]);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function isFlowFile(filePath) {
|
|
31
|
+
return filePath.endsWith('.yaml') || filePath.endsWith('.yml');
|
|
32
|
+
}
|
|
33
|
+
const readYamlFileAsJson = (filePath) => {
|
|
34
|
+
const yamlText = fs.readFileSync(filePath, 'utf8');
|
|
35
|
+
return yaml.load(yamlText);
|
|
36
|
+
};
|
|
37
|
+
const readConfigFromYamlFileAsJson = (filePath) => {
|
|
38
|
+
const yamlText = fs.readFileSync(filePath, 'utf8');
|
|
39
|
+
if (yamlText.includes('\n---\n')) {
|
|
40
|
+
const yamlTexts = yamlText.split('\n---\n');
|
|
41
|
+
const config = yaml.load(yamlTexts[0]);
|
|
42
|
+
if (Object.keys(config).filter((key) => key === 'appId').length > 1) {
|
|
43
|
+
return config;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
};
|
|
48
|
+
async function walk(dir, filterFunction) {
|
|
49
|
+
const readDirResult = await fs.promises.readdir(dir);
|
|
50
|
+
const files = await Promise.all(readDirResult.map(async (file) => {
|
|
51
|
+
const filePath = path.join(dir, file);
|
|
52
|
+
const stats = await fs.promises.stat(filePath);
|
|
53
|
+
if (stats.isDirectory())
|
|
54
|
+
return walk(filePath);
|
|
55
|
+
if (stats.isFile())
|
|
56
|
+
if (filterFunction) {
|
|
57
|
+
if (filterFunction(filePath))
|
|
58
|
+
return filePath;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return filePath;
|
|
62
|
+
}
|
|
63
|
+
}));
|
|
64
|
+
return files.flat().filter(Boolean);
|
|
65
|
+
}
|
|
66
|
+
async function plan(input, includeTags, excludeTags) {
|
|
67
|
+
if (!fs.existsSync(input)) {
|
|
68
|
+
throw new Error(`Flow path does not exist: ${path.resolve(input)}`);
|
|
69
|
+
}
|
|
70
|
+
if (fs.lstatSync(input).isFile()) {
|
|
71
|
+
return { flowsToRun: [input] };
|
|
72
|
+
}
|
|
73
|
+
let unfilteredFlowFiles = await walk(input, isFlowFile);
|
|
74
|
+
if (unfilteredFlowFiles.length === 0) {
|
|
75
|
+
throw new Error(`Flow directory does not contain any Flow files: ${path.resolve(input)}`);
|
|
76
|
+
}
|
|
77
|
+
const configFilePath = unfilteredFlowFiles.find((file) => file.endsWith('config.yaml') || file.endsWith('config.yml'));
|
|
78
|
+
if (configFilePath)
|
|
79
|
+
unfilteredFlowFiles = unfilteredFlowFiles.filter((file) => file !== configFilePath);
|
|
80
|
+
const cleanPath = path.normalize(input);
|
|
81
|
+
const relativeFilePaths = unfilteredFlowFiles.map((file) => file.replace(cleanPath, ''));
|
|
82
|
+
const topLevelFlowFiles = relativeFilePaths.filter((file) => !file.includes('/'));
|
|
83
|
+
const workspaceConfig = configFilePath
|
|
84
|
+
? readYamlFileAsJson(configFilePath)
|
|
85
|
+
: {};
|
|
86
|
+
let unsortedFlowFiles = topLevelFlowFiles;
|
|
87
|
+
if (workspaceConfig.flows) {
|
|
88
|
+
const matchers = workspaceConfig.flows.map((glob) => glob === '*' ? /^((?!\/).)*$/ : globToRegExp(glob));
|
|
89
|
+
unsortedFlowFiles = relativeFilePaths.filter((filePath) => matchers.some((matcher) => matcher.test(filePath)));
|
|
90
|
+
}
|
|
91
|
+
if (unsortedFlowFiles.length === 0) {
|
|
92
|
+
const error = workspaceConfig.flows
|
|
93
|
+
? new Error(`Flow inclusion pattern(s) did not match any Flow files:\n${workspaceConfig.flows.join('\n')}`)
|
|
94
|
+
: new Error(`Top-level directory does not contain any Flows: ${path.resolve(input)}`);
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
// eslint-disable-next-line unicorn/no-array-reduce
|
|
98
|
+
const configPerFlowFile = unsortedFlowFiles.reduce((acc, filePath) => {
|
|
99
|
+
acc[filePath] = readConfigFromYamlFileAsJson(cleanPath + filePath);
|
|
100
|
+
return acc;
|
|
101
|
+
}, {});
|
|
102
|
+
const allIncludeTags = [
|
|
103
|
+
...includeTags,
|
|
104
|
+
...(workspaceConfig.includeTags || []),
|
|
105
|
+
];
|
|
106
|
+
const allExcludeTags = [
|
|
107
|
+
...excludeTags,
|
|
108
|
+
...(workspaceConfig.excludeTags || []),
|
|
109
|
+
];
|
|
110
|
+
const allFlows = unsortedFlowFiles.filter((filePath) => {
|
|
111
|
+
const config = configPerFlowFile[filePath];
|
|
112
|
+
const tags = config?.tags || [];
|
|
113
|
+
return ((allIncludeTags.length === 0 ||
|
|
114
|
+
tags.some((tag) => allIncludeTags.includes(tag))) &&
|
|
115
|
+
(allExcludeTags.length === 0 ||
|
|
116
|
+
!tags.some((tag) => allExcludeTags.includes(tag))));
|
|
117
|
+
});
|
|
118
|
+
if (allFlows.length === 0) {
|
|
119
|
+
throw new Error(`Include / Exclude tags did not match any Flows:\n\nInclude Tags:\n${allIncludeTags.join('\n')}\n\nExclude Tags:\n${allExcludeTags.join('\n')}`);
|
|
120
|
+
}
|
|
121
|
+
// eslint-disable-next-line unicorn/no-array-reduce
|
|
122
|
+
const pathsByName = allFlows.reduce((acc, filePath) => {
|
|
123
|
+
const config = configPerFlowFile[filePath];
|
|
124
|
+
acc[config?.name || path.parse(filePath).name] = filePath;
|
|
125
|
+
return acc;
|
|
126
|
+
}, {});
|
|
127
|
+
const flowsToRunInSequence = workspaceConfig.executionOrder?.flowsOrder
|
|
128
|
+
?.map((flowOrder) => getFlowsToRunInSequence(pathsByName, [flowOrder]))
|
|
129
|
+
.flat() || [];
|
|
130
|
+
const normalFlows = allFlows.filter((flow) => !flowsToRunInSequence.includes(flow));
|
|
131
|
+
// for (const filePath of allFlows) {
|
|
132
|
+
// const commands = YamlCommandReader.readCommands(filePath).filter(
|
|
133
|
+
// (command) => command.addMediaCommand,
|
|
134
|
+
// );
|
|
135
|
+
// const mediaPaths = commands.flatMap(
|
|
136
|
+
// (command) => command.addMediaCommand.mediaPaths,
|
|
137
|
+
// );
|
|
138
|
+
// YamlCommandsPathValidator.validatePathsExistInWorkspace(
|
|
139
|
+
// input,
|
|
140
|
+
// filePath,
|
|
141
|
+
// mediaPaths,
|
|
142
|
+
// );
|
|
143
|
+
// }
|
|
144
|
+
return {
|
|
145
|
+
flowsToRun: normalFlows,
|
|
146
|
+
sequence: {
|
|
147
|
+
continueOnFailure: workspaceConfig.executionOrder?.continueOnFailure,
|
|
148
|
+
flows: flowsToRunInSequence,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
exports.plan = plan;
|
package/oclif.manifest.json
CHANGED
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
"firstFile": {
|
|
7
7
|
"description": "The binary file of the app to run your flow against, e.g. app.apk for android or app.zip for ios",
|
|
8
8
|
"hidden": true,
|
|
9
|
-
"name": "firstFile"
|
|
10
|
-
"required": true
|
|
9
|
+
"name": "firstFile"
|
|
11
10
|
},
|
|
12
11
|
"secondFile": {
|
|
13
12
|
"description": "The flow file to run against the app, e.g. test.yaml",
|
|
@@ -28,6 +27,11 @@
|
|
|
28
27
|
"name": "androidApiLevel",
|
|
29
28
|
"hasDynamicHelp": false,
|
|
30
29
|
"multiple": false,
|
|
30
|
+
"options": [
|
|
31
|
+
"32",
|
|
32
|
+
"33",
|
|
33
|
+
"34"
|
|
34
|
+
],
|
|
31
35
|
"type": "option"
|
|
32
36
|
},
|
|
33
37
|
"apiKey": {
|
|
@@ -45,6 +49,7 @@
|
|
|
45
49
|
"api-url"
|
|
46
50
|
],
|
|
47
51
|
"description": "API base URL",
|
|
52
|
+
"hidden": true,
|
|
48
53
|
"name": "apiUrl",
|
|
49
54
|
"default": "https://api.devicecloud.dev",
|
|
50
55
|
"hasDynamicHelp": false,
|
|
@@ -82,6 +87,16 @@
|
|
|
82
87
|
"multiple": true,
|
|
83
88
|
"type": "option"
|
|
84
89
|
},
|
|
90
|
+
"flows": {
|
|
91
|
+
"aliases": [
|
|
92
|
+
"flows"
|
|
93
|
+
],
|
|
94
|
+
"description": "The path to the flow file or folder containing your Flows",
|
|
95
|
+
"name": "flows",
|
|
96
|
+
"hasDynamicHelp": false,
|
|
97
|
+
"multiple": false,
|
|
98
|
+
"type": "option"
|
|
99
|
+
},
|
|
85
100
|
"iOSVersion": {
|
|
86
101
|
"aliases": [
|
|
87
102
|
"ios-version"
|
|
@@ -90,6 +105,10 @@
|
|
|
90
105
|
"name": "iOSVersion",
|
|
91
106
|
"hasDynamicHelp": false,
|
|
92
107
|
"multiple": false,
|
|
108
|
+
"options": [
|
|
109
|
+
"16.4",
|
|
110
|
+
"17.2"
|
|
111
|
+
],
|
|
93
112
|
"type": "option"
|
|
94
113
|
}
|
|
95
114
|
},
|
|
@@ -109,5 +128,5 @@
|
|
|
109
128
|
]
|
|
110
129
|
}
|
|
111
130
|
},
|
|
112
|
-
"version": "0.0.1-alpha.
|
|
131
|
+
"version": "0.0.1-alpha.3"
|
|
113
132
|
}
|
package/package.json
CHANGED
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"@oclif/plugin-plugins": "^4",
|
|
12
12
|
"@oclif/plugin-update": "^4.1.11",
|
|
13
13
|
"@oclif/plugin-warn-if-update-available": "^3.0.10",
|
|
14
|
-
"archiver": "^6.0.1"
|
|
14
|
+
"archiver": "^6.0.1",
|
|
15
|
+
"glob-to-regexp": "^0.4.1",
|
|
16
|
+
"js-yaml": "^4.1.0"
|
|
15
17
|
},
|
|
16
18
|
"description": "Better cloud maestro testing",
|
|
17
19
|
"devDependencies": {
|
|
@@ -19,6 +21,8 @@
|
|
|
19
21
|
"@oclif/test": "^3",
|
|
20
22
|
"@types/archiver": "^6.0.2",
|
|
21
23
|
"@types/chai": "^4",
|
|
24
|
+
"@types/glob-to-regexp": "^0.4.4",
|
|
25
|
+
"@types/js-yaml": "^4.0.9",
|
|
22
26
|
"@types/mocha": "^9.0.0",
|
|
23
27
|
"chai": "^4",
|
|
24
28
|
"eslint": "^8.56.0",
|
|
@@ -70,7 +74,7 @@
|
|
|
70
74
|
"test": "mocha --forbid-only \"test/**/*.test.ts\"",
|
|
71
75
|
"version": "oclif readme && git add README.md"
|
|
72
76
|
},
|
|
73
|
-
"version": "0.0.1-alpha.
|
|
77
|
+
"version": "0.0.1-alpha.3",
|
|
74
78
|
"bugs": {
|
|
75
79
|
"url": "https://discord.gg/GzZBHcUJ"
|
|
76
80
|
},
|