UNPKG

126 kBJavaScriptView Raw
1#!/usr/bin/env node
2/*----------------------------------------------
3 * Generated by rollup. Written by John Schmidt.
4 * Rally Tools CLI v1.20.0
5 *--------------------------------------------*/
6const importLazy = require("import-lazy")(require);
7
8'use strict';
9
10function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
11
12var argparse = _interopDefault(require('minimist'));
13var chalk$1 = _interopDefault(require('chalk'));
14var os = require('os');
15var fs = require('fs');
16var fs__default = _interopDefault(fs);
17var child_process = require('child_process');
18var perf_hooks = require('perf_hooks');
19var rp = _interopDefault(require('request-promise'));
20var path = require('path');
21var path__default = _interopDefault(path);
22var moment = _interopDefault(require('moment'));
23
24function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
25 var desc = {};
26 Object['ke' + 'ys'](descriptor).forEach(function (key) {
27 desc[key] = descriptor[key];
28 });
29 desc.enumerable = !!desc.enumerable;
30 desc.configurable = !!desc.configurable;
31
32 if ('value' in desc || desc.initializer) {
33 desc.writable = true;
34 }
35
36 desc = decorators.slice().reverse().reduce(function (desc, decorator) {
37 return decorator(target, property, desc) || desc;
38 }, desc);
39
40 if (context && desc.initializer !== void 0) {
41 desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
42 desc.initializer = undefined;
43 }
44
45 if (desc.initializer === void 0) {
46 Object['define' + 'Property'](target, property, desc);
47 desc = null;
48 }
49
50 return desc;
51}
52
53let configFile = null;
54
55if (os.homedir) {
56 configFile = os.homedir() + "/.rallyconfig";
57}
58
59let configObject;
60function loadConfig(file) {
61 if (file) configFile = file;
62 if (!configFile) return;
63 configObject = {
64 hasConfig: true
65 };
66
67 try {
68 let json = fs.readFileSync(configFile);
69 configObject = JSON.parse(json);
70 configObject.hasConfig = true;
71 } catch (e) {
72 if (e.code == "ENOENT") {
73 configObject.hasConfig = false; //ok, they should probably make a config
74 } else {
75 throw e;
76 }
77 }
78}
79function loadConfigFromArgs(args) {
80 let tempConfig = {
81 hasConfig: true,
82 ...args.config
83 };
84 configObject = tempConfig;
85}
86function setConfig(obj) {
87 configObject = obj;
88}
89
90//these are the help entries for each command
91let helpEntries = {};
92
93let helpEntry = name => helpEntries[name] ? helpEntries[name] : helpEntries[name] = {
94 name
95}; //short description
96
97
98function helpText(text) {
99 return function (func, name) {
100 helpEntry(name).text = text;
101 return func;
102 };
103} //flag type argument like -f or --file
104
105function arg(long, short, desc) {
106 return function (func, name) {
107 let args = helpEntry(name).args = helpEntry(name).args || [];
108 args.unshift({
109 long,
110 short,
111 desc
112 });
113 return func;
114 };
115} //normal argument
116
117function param(param, desc) {
118 return function (func, name) {
119 let params = helpEntry(name).params = helpEntry(name).params || [];
120 params.unshift({
121 param,
122 desc
123 });
124 return func;
125 };
126} //usage string
127
128function usage(usage) {
129 return function (func, name) {
130 usage = usage.replace(/[\[<](\w+)[\]>]/g, chalk`[{blue $1}]`);
131 helpEntry(name).usage = usage;
132 return func;
133 };
134}
135//function retuns obj.a.b.c
136
137function deepAccess(obj, path) {
138 let o = obj;
139
140 for (let key of path) {
141 if (!o) return [];
142 o = o[key];
143 }
144
145 return o;
146} //This takes a class as the first argument, then adds a getter/setter pair that
147//corresponds to an object in this.data
148
149
150function defineAssoc(classname, shortname, path) {
151 path = path.split(".");
152 let lastKey = path.pop();
153 Object.defineProperty(classname.prototype, shortname, {
154 get() {
155 return deepAccess(this, path)[lastKey];
156 },
157
158 set(val) {
159 deepAccess(this, path)[lastKey] = val;
160 }
161
162 });
163}
164
165function spawn(options, ...args) {
166 if (typeof options !== "object") {
167 args.unshift(options);
168 options = {};
169 } //todo options
170
171
172 return new Promise((resolve, reject) => {
173 let start = perf_hooks.performance.now();
174 let stdout = "";
175 let stderr = "";
176 let cp = child_process.spawn(...args);
177 let write = global.write;
178
179 if (options.noecho) {
180 write = () => {};
181 }
182
183 if (cp.stdout) cp.stdout.on("data", chunk => {
184 stdout += chunk;
185 write(chunk);
186 });
187 if (cp.stderr) cp.stderr.on("data", chunk => {
188 stderr += chunk;
189 write(chunk);
190 });
191 cp.on("error", reject);
192 cp.on("close", code => {
193 let end = perf_hooks.performance.now();
194 let time = end - start;
195 let timestr = time > 1000 ? (time / 100 | 0) / 10 + "s" : (time | 0) + "ms";
196 resolve({
197 stdout,
198 stderr,
199 exitCode: code,
200 time,
201 timestr
202 });
203 });
204 });
205}
206
207global.chalk = chalk$1;
208
209global.log = (...text) => console.log(...text);
210
211global.write = (...text) => process.stdout.write(...text);
212
213global.elog = (...text) => console.error(...text);
214
215global.ewrite = (...text) => process.stderr.write(...text);
216
217global.errorLog = (...text) => log(...text.map(chalk$1.red));
218
219class lib {
220 //This function takes 2 required arguemnts:
221 // env: the enviornment you wish to use
222 // and either:
223 // 'path', the short path to the resource. ex '/presets/'
224 // 'path_full', the full path to the resource like 'https://discovery-dev.sdvi.com/presets'
225 //
226 // If the method is anything but GET, either payload or body should be set.
227 // payload should be a javascript object to be turned into json as the request body
228 // body should be a string that is passed as the body. for example: the python code of a preset.
229 //
230 // qs are the querystring parameters, in a key: value object.
231 // {filter: "name=test name"} becomes something like 'filter=name=test+name'
232 //
233 // headers are the headers of the request. "Content-Type" is already set if
234 // payload is given as a parameter
235 //
236 // fullResponse should be true if you want to receive the request object,
237 // not just the returned data.
238 static async makeAPIRequest({
239 env,
240 path,
241 path_full,
242 fullPath,
243 payload,
244 body,
245 method = "GET",
246 qs,
247 headers = {},
248 fullResponse = false,
249 timeout = configObject.timeout || 20000
250 }) {
251 var _configObject$api;
252
253 //backwards compatability from ruby script
254 if (fullPath) path_full = fullPath; //Keys are defined in enviornment variables
255
256 let config = configObject === null || configObject === void 0 ? void 0 : (_configObject$api = configObject.api) === null || _configObject$api === void 0 ? void 0 : _configObject$api[env];
257
258 if (!config) {
259 throw new UnconfiguredEnvError(env);
260 }
261
262 if (method !== "GET" && !configObject.dangerModify) {
263 if (env === "UAT" && configObject.restrictUAT || env === "PROD") {
264 throw new ProtectedEnvError(env);
265 }
266 }
267
268 let rally_api_key = config.key;
269 let rally_api = config.url;
270
271 if (path && path.startsWith("/v1.0/")) {
272 rally_api = rally_api.replace("/api/v2", "/api");
273 }
274
275 path = path_full || rally_api + path;
276
277 if (payload) {
278 body = JSON.stringify(payload, null, 4);
279 }
280
281 if (payload) {
282 headers["Content-Type"] = "application/vnd.api+json";
283 }
284
285 let fullHeaders = {
286 //SDVI ignores this header sometimes.
287 Accept: "application/vnd.api+json",
288 "X-SDVI-Client-Application": "Discovery-rtlib-" + (configObject.appName || "commandline"),
289 ...headers
290 };
291
292 if (configObject.vvverbose) {
293 log(`${method} @ ${path}`);
294 log(JSON.stringify(fullHeaders, null, 4));
295
296 if (body) {
297 log(body);
298 } else {
299 log("(No body");
300 }
301 }
302
303 let requestOptions = {
304 method,
305 body,
306 qs,
307 uri: path,
308 timeout,
309 auth: {
310 bearer: rally_api_key
311 },
312 headers: fullHeaders,
313 simple: false,
314 resolveWithFullResponse: true
315 };
316 let response;
317
318 try {
319 response = await rp(requestOptions);
320
321 if (configObject.vverbose || configObject.vvverbose) {
322 log(chalk$1`${method} @ ${response.request.uri.href}`);
323 }
324 } catch (e) {
325 if ((e === null || e === void 0 ? void 0 : e.cause.code) === "ESOCKETTIMEDOUT") {
326 throw new APIError(response || {}, requestOptions, body);
327 } else {
328 throw e;
329 }
330 } //Throw an error for any 5xx or 4xx
331
332
333 if (!fullResponse && ![200, 201, 202, 203, 204].includes(response.statusCode)) {
334 throw new APIError(response, requestOptions, body);
335 }
336
337 let contentType = response.headers["content-type"];
338 let isJSONResponse = contentType === "application/vnd.api+json" || contentType === "application/json";
339
340 if (configObject.vvverbose) {
341 log(response.body);
342 }
343
344 if (fullResponse) {
345 return response;
346 } else if (isJSONResponse) {
347 var _response, _response$body;
348
349 if ([200, 201, 202, 203, 204].includes(response.statusCode) && !((_response = response) === null || _response === void 0 ? void 0 : (_response$body = _response.body) === null || _response$body === void 0 ? void 0 : _response$body.trim())) return {};
350
351 try {
352 return JSON.parse(response.body);
353 } catch (e) {
354 log(response.body);
355 throw new AbortError("Body is not valid json: ");
356 }
357 } else {
358 return response.body;
359 }
360 } //Index a json endpoint that returns a {links} field.
361 //This function returns the merged data objects as an array
362 //
363 //Additonal options (besides makeAPIRequest options):
364 // - Observe: function to be called for each set of data from the api
365
366
367 static async indexPath(env, path) {
368 let all = [];
369 let opts = typeof env === "string" ? {
370 env,
371 path
372 } : env;
373 let json = await this.makeAPIRequest(opts);
374 let [numPages, pageSize] = this.numPages(json.links.last); //log(`num pages: ${numPages} * ${pageSize}`);
375
376 all = [...json.data];
377
378 while (json.links.next) {
379 json = await this.makeAPIRequest({ ...opts,
380 path_full: json.links.next
381 });
382 if (opts.observe) await opts.observe(json.data);
383 all = [...all, ...json.data];
384 }
385
386 return all;
387 } //Returns number of pages and pagination size
388
389
390 static numPages(str) {
391 return /page=(\d+)p(\d+)/.exec(str).slice(1);
392 }
393
394 static arrayChunk(array, chunkSize) {
395 let newArr = [];
396
397 for (let i = 0; i < array.length; i += chunkSize) {
398 newArr.push(array.slice(i, i + chunkSize));
399 }
400
401 return newArr;
402 }
403
404 static async doPromises(promises, result = [], cb) {
405 for (let promise of promises) {
406 let res = await promise;
407 result.push(res);
408
409 if (cb) {
410 cb(res.data);
411 }
412 }
413
414 return result;
415 }
416
417 static clearProgress(size = 30) {
418 if (!configObject.globalProgress) return;
419 process.stderr.write(`\r${" ".repeat(size + 15)}\r`);
420 }
421
422 static async drawProgress(i, max, size = process.stdout.columns - 15 || 15) {
423 if (!configObject.globalProgress) return;
424 if (size > 45) size = 45;
425 let pct = Number(i) / Number(max); //clamp between 0 and 1
426
427 pct = pct < 0 ? 0 : pct > 1 ? 1 : pct;
428 let numFilled = Math.floor(pct * size);
429 let numEmpty = size - numFilled;
430 this.clearProgress(size);
431 process.stderr.write(`[${"*".repeat(numFilled)}${" ".repeat(numEmpty)}] ${i} / ${max}`);
432 }
433
434 static async keepalive(func, inputData, {
435 chunksize = 20,
436 observe = async _ => _,
437 progress = configObject.globalProgress
438 } = {}) {
439 let total = inputData ? inputData.length : func.length;
440 let i = 0;
441
442 let createPromise = () => {
443 let ret;
444 if (i >= total) return [];
445
446 if (inputData) {
447 ret = [i, func(inputData[i])];
448 } else {
449 ret = [i, func[i]()];
450 }
451
452 i++;
453 return ret;
454 };
455
456 let values = [];
457 let finished = 0;
458 if (progress) process.stderr.write("\n");
459 let threads = [...this.range(chunksize)].map(async whichThread => {
460 while (true) {
461 let [i, currentPromise] = createPromise();
462 if (i == undefined) break;
463 values[i] = await observe((await currentPromise));
464 if (progress) this.drawProgress(++finished, total);
465 }
466 });
467 await Promise.all(threads);
468 if (progress) process.stderr.write("\n");
469 return values;
470 }
471
472 static *range(start, end) {
473 if (end === undefined) {
474 end = start;
475 start = 0;
476 }
477
478 while (start < end) yield start++;
479 } //Index a json endpoint that returns a {links} field.
480 //
481 //This function is faster than indexPath because it can guess the pages it
482 //needs to retreive so that it can request all assets at once.
483 //
484 //This function assumes that the content from the inital request is the
485 //first page, so starting on another page may cause issues. Consider
486 //indexPath for that.
487 //
488 //Additional opts, besides default indexPath opts:
489 // - chunksize[10]: How often to break apart concurrent requests
490
491
492 static async indexPathFast(env, path) {
493 let opts = typeof env === "string" ? {
494 env,
495 path
496 } : env; //Create a copy of the options in case we need to have a special first request
497
498 let start = opts.start || 1;
499 let initOpts = { ...opts
500 };
501
502 if (opts.pageSize) {
503 initOpts.qs = { ...opts.qs
504 };
505 initOpts.qs.page = `${start}p${opts.pageSize}`;
506 }
507
508 let json = await this.makeAPIRequest(initOpts);
509 if (opts.observe && opts.start !== 1) json = await opts.observe(json);
510 let baselink = json.links.first;
511
512 const linkToPage = page => baselink.replace(`page=1p`, `page=${page}p`);
513
514 let [numPages, pageSize] = this.numPages(json.links.last); //Construct an array of all the requests that are done simultanously.
515 //Assume that the content from the inital request is the first page.
516
517 let allResults = await this.keepalive(this.makeAPIRequest, [...this.range(start + 1, Number(numPages) + 1 || opts.limit + 1)].map(i => ({ ...opts,
518 path_full: linkToPage(i)
519 })), {
520 chunksize: opts.chunksize,
521 observe: opts.observe
522 });
523
524 if (start == 1) {
525 allResults.unshift(json);
526 }
527
528 this.clearProgress();
529 let all = [];
530
531 for (let result of allResults) {
532 for (let item of result.data) {
533 all.push(item);
534 }
535 }
536
537 return all;
538 }
539
540 static isLocalEnv(env) {
541 return !env || env === "LOCAL" || env === "LOC";
542 }
543
544 static envName(env) {
545 if (this.isLocalEnv(env)) return "LOCAL";
546 return env;
547 }
548
549}
550class AbortError extends Error {
551 constructor(message) {
552 super(message);
553 Error.captureStackTrace(this, this.constructor);
554 this.name = "AbortError";
555 }
556
557}
558class APIError extends Error {
559 constructor(response, opts, body) {
560 super(chalk$1`
561{reset Request returned} {yellow ${response === null || response === void 0 ? void 0 : response.statusCode}}{
562{green ${JSON.stringify(opts, null, 4)}}
563{green ${body}}
564{reset ${response.body}}
565===============================
566{red ${response.body ? "Request timed out" : "Bad response from API"}}
567===============================
568 `);
569 this.response = response;
570 this.opts = opts;
571 this.body = body;
572 Error.captureStackTrace(this, this.constructor);
573 this.name = "ApiError";
574 }
575
576}
577class UnconfiguredEnvError extends AbortError {
578 constructor(env) {
579 super("Unconfigured enviornment: " + env);
580 this.name = "Unconfigured Env Error";
581 }
582
583}
584class ProtectedEnvError extends AbortError {
585 constructor(env) {
586 super("Protected enviornment: " + env);
587 this.name = "Protected Env Error";
588 }
589
590}
591class FileTooLargeError extends Error {
592 constructor(file) {
593 super(`File ${file.parentAsset ? file.parentAsset.name : "(unknown)"}/${file.name} size is: ${file.sizeGB}g (> ~.2G)`);
594 this.name = "File too large error";
595 }
596
597}
598class Collection {
599 constructor(arr) {
600 this.arr = arr;
601 }
602
603 [Symbol.iterator]() {
604 return this.arr[Symbol.iterator]();
605 }
606
607 findById(id) {
608 return this.arr.find(x => x.id == id);
609 }
610
611 findByName(name) {
612 return this.arr.find(x => x.name == name);
613 }
614
615 findByNameContains(name) {
616 return this.arr.find(x => x.name.includes(name));
617 }
618
619 log() {
620 for (let d of this) {
621 if (d) {
622 log(d.chalkPrint(true));
623 } else {
624 log(chalk$1`{red (None)}`);
625 }
626 }
627 }
628
629 get length() {
630 return this.arr.length;
631 }
632
633}
634class RallyBase {
635 static handleCaching() {
636 if (!this.cache) this.cache = [];
637 }
638
639 static isLoaded(env) {
640 if (!this.hasLoadedAll) return;
641 return this.hasLoadedAll[env];
642 }
643
644 static async getById(env, id, qs) {
645 this.handleCaching();
646
647 for (let item of this.cache) {
648 if (item.id == id && item.remote === env || `${env}-${id}` === item.metastring) return item;
649 }
650
651 let data = await lib.makeAPIRequest({
652 env,
653 path: `/${this.endpoint}/${id}`,
654 qs
655 });
656
657 if (data.data) {
658 let o = new this({
659 data: data.data,
660 remote: env,
661 included: data.included
662 });
663 this.cache.push(o);
664 return o;
665 }
666 }
667
668 static async getByName(env, name, qs) {
669 this.handleCaching();
670
671 for (let item of this.cache) {
672 if (item.name === name && item.remote === env) return item;
673 }
674
675 let data = await lib.makeAPIRequest({
676 env,
677 path: `/${this.endpoint}`,
678 qs: { ...qs,
679 filter: `name=${name}` + (qs ? qs.filter : "")
680 }
681 }); //TODO included might not wokr correctly here
682
683 if (data.data[0]) {
684 let o = new this({
685 data: data.data[0],
686 remote: env,
687 included: data.included
688 });
689 this.cache.push(o);
690 return o;
691 }
692 }
693
694 static async getAllPreCollect(d) {
695 return d;
696 }
697
698 static async getAll(env) {
699 this.handleCaching();
700 let datas = await lib.indexPathFast({
701 env,
702 path: `/${this.endpoint}`,
703 pageSize: "50",
704 qs: {
705 sort: "id"
706 }
707 });
708 datas = await this.getAllPreCollect(datas);
709 let all = new Collection(datas.map(data => new this({
710 data,
711 remote: env
712 })));
713 this.cache = [...this.cache, ...all.arr];
714 return all;
715 }
716
717 static async removeCache(env) {
718 this.handleCaching();
719 this.cache = this.cache.filter(x => x.remote !== env);
720 } //Specific turns name into id based on env
721 //Generic turns ids into names
722
723
724 async resolveApply(type, dataObj, direction) {
725 let obj;
726
727 if (direction == "generic") {
728 obj = await type.getById(this.remote, dataObj.id);
729
730 if (obj) {
731 dataObj.name = obj.name;
732 }
733 } else if (direction == "specific") {
734 obj = await type.getByName(this.remote, dataObj.name);
735
736 if (obj) {
737 dataObj.id = obj.id;
738 }
739 }
740
741 return obj;
742 } //Type is the baseclass you are looking for (should extend RallyBase)
743 //name is the name of the field
744 //isArray is true if it has multiple cardinailty, false if it is single
745 //direction gets passed directly to resolveApply
746
747
748 async resolveField(type, name, isArray = false, direction = "generic") {
749 // ignore empty fields
750 let field = this.relationships[name];
751 if (!(field === null || field === void 0 ? void 0 : field.data)) return;
752
753 if (isArray) {
754 return await Promise.all(field.data.map(o => this.resolveApply(type, o, direction)));
755 } else {
756 return await this.resolveApply(type, field.data, direction);
757 }
758 }
759
760 cleanup() {
761 for (let [key, val] of Object.entries(this.relationships)) {
762 //Remove ids from data
763 if (val.data) {
764 if (val.data.id) {
765 delete val.data.id;
766 } else if (val.data[0]) {
767 for (let x of val.data) delete x.id;
768 }
769 }
770
771 delete val.links;
772 } // organization is unused (?)
773
774
775 delete this.relationships.organization; // id is specific to envs
776 // but save source inside meta string in case we need it
777
778 this.metastring = this.remote + "-" + this.data.id;
779 delete this.data.id; // links too
780
781 delete this.data.links;
782 }
783
784}
785function sleep(time = 1000) {
786 return new Promise(resolve => setTimeout(resolve, time));
787}
788
789const inquirer = importLazy("inquirer");
790const readdir = importLazy("recursive-readdir");
791let hasAutoCompletePrompt = false;
792function addAutoCompletePrompt() {
793 if (hasAutoCompletePrompt) return;
794 hasAutoCompletePrompt = true;
795 inquirer.registerPrompt("autocomplete", require("inquirer-autocomplete-prompt"));
796}
797async function $api(propArray) {
798 let q;
799 q = await inquirer.prompt([{
800 type: "input",
801 name: "company",
802 message: `What is your company?`,
803 default: `discovery`
804 }]);
805 let company = q.company;
806 const defaults = {
807 DEV: `https://${company}-dev.sdvi.com/api/v2`,
808 UAT: `https://${company}-uat.sdvi.com/api/v2`,
809 QA: `https://${company}-qa.sdvi.com/api/v2`,
810 PROD: `https://${company}.sdvi.com/api/v2`
811 };
812
813 if (propArray && propArray[1]) {
814 q = {
815 envs: [propArray[1]]
816 };
817 } else {
818 //Create a checkbox prompt to choose enviornments
819 q = await inquirer.prompt([{
820 type: "checkbox",
821 name: "envs",
822 message: `What enviornments would you like to configure?`,
823 choices: Object.keys(defaults).map(name => ({
824 name,
825 checked: true
826 }))
827 }]);
828 } //Each env should ask 2 for two things: The url and the key.
829
830
831 let questions = q.envs.map(env => {
832 let defaultKey = process.env[`rally_api_key_${env}`];
833
834 if (configObject && configObject.api && configObject.api[env]) {
835 defaultKey = configObject.api[env].key;
836 }
837
838 return [{
839 type: "input",
840 name: `api.${env}.url`,
841 message: `What is the api endpoint for ${env}?`,
842 default: defaults[env]
843 }, {
844 type: "input",
845 name: `api.${env}.key`,
846 message: `What is your api key for ${env}?`,
847 default: defaultKey
848 }];
849 }); //flatten and ask
850
851 questions = [].concat(...questions);
852 q = await inquirer.prompt(questions);
853
854 if (propArray) {
855 q.api = { ...configObject.api,
856 ...q.api
857 };
858 }
859
860 return q;
861}
862async function $chalk(propArray) {
863 return {
864 chalk: await askQuestion("Would you like chalk enabled (Adds coloring)?")
865 };
866}
867async function $restrictUAT(propArray) {
868 return {
869 restrictUAT: await askQuestion("Would you like to protect UAT?")
870 };
871}
872async function $repodir(propArray) {
873 return await inquirer.prompt([{
874 type: "input",
875 name: `repodir`,
876 message: `Where is your rally repository?`,
877 default: process.env["rally_repo_path"]
878 }]);
879}
880async function $appName(propArray) {
881 let defaultAppName = "cmdline-" + (process.env.USERNAME || process.env.LOGNAME);
882 let project = await askInput("Application name?", defaultAppName);
883
884 if (project === "none" || project === "-" || project === "" || !project) {
885 project = null;
886 }
887
888 return {
889 appName: project
890 };
891}
892async function $project(propArray) {
893 let project = await askInput("Subproject directory?");
894
895 if (project === "none" || project === "-" || project === "" || !project) {
896 project = null;
897 }
898
899 return {
900 project
901 };
902}
903async function $defaultEnv(propArray) {
904 return await inquirer.prompt([{
905 type: "input",
906 name: `defaultEnv`,
907 message: `Default enviornment?`,
908 default: "DEV"
909 }]);
910} //Internal usage/testing
911
912async function selectProvider(providers, autoDefault = false) {
913 addAutoCompletePrompt();
914 let defaultProvider = providers.findByName("SdviEvaluate");
915
916 if (autoDefault) {
917 return defaultProvider;
918 } else {
919 let choices = providers.arr.map(x => ({
920 name: x.chalkPrint(true),
921 value: x
922 }));
923 let q = await inquirer.prompt([{
924 type: "autocomplete",
925 name: "provider",
926 default: defaultProvider,
927 source: async (sofar, input) => {
928 return choices.filter(x => input ? x.value.name.toLowerCase().includes(input.toLowerCase()) : true);
929 }
930 }]);
931 return q.provider;
932 }
933}
934async function loadLocals(path$1, Class) {
935 let basePath = configObject.repodir;
936 let objs = (await readdir(basePath)).filter(name => name.includes(path$1)).filter(name => !path.basename(name).startsWith(".")).map(name => new Class({
937 path: name
938 }));
939 return objs;
940}
941async function selectLocal(path, typeName, Class, canSelectNone = true) {
942 addAutoCompletePrompt();
943 let objs = await loadLocals(path, Class);
944 let objsMap = objs.map(x => ({
945 name: x.chalkPrint(true),
946 value: x
947 }));
948 let none = {
949 name: chalk` {red None}: {red None}`,
950 value: null
951 };
952 if (canSelectNone) objsMap.unshift(none);
953 let q = await inquirer.prompt([{
954 type: "autocomplete",
955 name: "obj",
956 message: `What ${typeName} do you want?`,
957 source: async (sofar, input) => {
958 return objsMap.filter(x => input ? x.name.toLowerCase().includes(input.toLowerCase()) : true);
959 }
960 }]);
961 return q.obj;
962}
963async function selectPreset({
964 purpose = "preset",
965 canSelectNone
966}) {
967 return selectLocal("silo-presets", purpose, Preset, canSelectNone);
968}
969async function selectRule({
970 purpose = "rule",
971 canSelectNone
972}) {
973 return selectLocal("silo-rules", purpose, Rule, canSelectNone);
974}
975async function askInput(question, def) {
976 return (await inquirer.prompt([{
977 type: "input",
978 name: "ok",
979 message: question,
980 default: def
981 }])).ok;
982}
983async function askQuestion(question) {
984 return (await inquirer.prompt([{
985 type: "confirm",
986 name: "ok",
987 message: question
988 }])).ok;
989}
990
991var configHelpers = /*#__PURE__*/Object.freeze({
992 __proto__: null,
993 inquirer: inquirer,
994 addAutoCompletePrompt: addAutoCompletePrompt,
995 $api: $api,
996 $chalk: $chalk,
997 $restrictUAT: $restrictUAT,
998 $repodir: $repodir,
999 $appName: $appName,
1000 $project: $project,
1001 $defaultEnv: $defaultEnv,
1002 selectProvider: selectProvider,
1003 loadLocals: loadLocals,
1004 selectLocal: selectLocal,
1005 selectPreset: selectPreset,
1006 selectRule: selectRule,
1007 askInput: askInput,
1008 askQuestion: askQuestion
1009});
1010
1011class Provider extends RallyBase {
1012 constructor({
1013 data,
1014 remote
1015 }) {
1016 super();
1017 this.data = data;
1018 this.meta = {};
1019 this.remote = remote;
1020 } //cached
1021
1022
1023 async getEditorConfig() {
1024 if (this.editorConfig) return this.editorConfig;
1025 this.editorConfig = await lib.makeAPIRequest({
1026 env: this.remote,
1027 path_full: this.data.links.editorConfig
1028 });
1029 this.editorConfig.fileExt = await this.getFileExtension();
1030 return this.editorConfig;
1031 }
1032
1033 static async getAllPreCollect(providers) {
1034 return providers.sort((a, b) => {
1035 return a.attributes.category.localeCompare(b.attributes.category) || a.attributes.name.localeCompare(b.attributes.name);
1036 });
1037 }
1038
1039 async getFileExtension() {
1040 let config = await this.getEditorConfig();
1041 let map = {
1042 python: "py",
1043 text: "txt",
1044
1045 getmap(key) {
1046 if (this.name === "Aurora") return "zip";
1047 if (this[key]) return this[key];
1048 return key;
1049 }
1050
1051 };
1052 return map.getmap(config.lang);
1053 }
1054
1055 chalkPrint(pad = true) {
1056 let id = String(this.id);
1057 if (pad) id = id.padStart(4);
1058 return chalk`{green ${id}}: {blue ${this.category}} - {green ${this.name}}`;
1059 }
1060
1061}
1062
1063defineAssoc(Provider, "id", "data.id");
1064defineAssoc(Provider, "name", "data.attributes.name");
1065defineAssoc(Provider, "category", "data.attributes.category");
1066defineAssoc(Provider, "remote", "meta.remote");
1067defineAssoc(Provider, "editorConfig", "meta.editorConfig");
1068Provider.endpoint = "providerTypes";
1069
1070class File extends RallyBase {
1071 constructor({
1072 data,
1073 remote,
1074 included,
1075 parent
1076 }) {
1077 super();
1078 this.data = data;
1079 this.meta = {};
1080 this.remote = remote;
1081 this.parentAsset = parent;
1082 }
1083
1084 chalkPrint(pad = false) {
1085 let id = String("F-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
1086 if (pad) id = id.padStart(15);
1087 return chalk`{green ${id}}: {blue ${this.data.attributes ? this.name : "(lite file)"}} {red ${this.sizeHR}}`;
1088 }
1089
1090 canBeDownloaded() {
1091 return this.sizeGB <= .2;
1092 }
1093
1094 async getContent(force = false) {
1095 if (!this.canBeDownloaded() && !force) {
1096 throw new FileTooLargeError(this);
1097 }
1098
1099 return lib.makeAPIRequest({
1100 env: this.remote,
1101 fullPath: this.contentLink
1102 });
1103 }
1104
1105 async delete(remove = true) {
1106 return lib.makeAPIRequest({
1107 env: this.remote,
1108 fullPath: this.selfLink,
1109 method: "DELETE"
1110 });
1111 }
1112
1113 get size() {
1114 return Object.values(this.data.attributes.instances)[0].size;
1115 }
1116
1117 get sizeGB() {
1118 return Math.round(this.size / 1024 / 1024 / 1024 * 10) / 10;
1119 }
1120
1121 get sizeHR() {
1122 let units = ["B", "K", "M", "G", "T"];
1123 let unitIdx = 0;
1124 let size = this.size;
1125
1126 while (size > 1000) {
1127 size /= 1024;
1128 unitIdx++;
1129 }
1130
1131 if (size > 100) {
1132 size = Math.round(size);
1133 } else {
1134 size = Math.round(size * 10) / 10;
1135 }
1136
1137 return size + units[unitIdx];
1138 }
1139
1140 get instancesList() {
1141 let instances = [];
1142
1143 for (let [key, val] of Object.entries(this.instances)) {
1144 let n = {
1145 id: key
1146 };
1147 Object.assign(n, val);
1148 instances.push(n);
1149 }
1150
1151 return instances;
1152 }
1153
1154 static rslURL(instance) {
1155 return `rsl://${instance.storageLocationName}/${instance.name}`;
1156 }
1157
1158}
1159
1160defineAssoc(File, "id", "data.id");
1161defineAssoc(File, "name", "data.attributes.label");
1162defineAssoc(File, "contentLink", "data.links.content");
1163defineAssoc(File, "selfLink", "data.links.self");
1164defineAssoc(File, "label", "data.attributes.label");
1165defineAssoc(File, "md5", "data.attributes.md5");
1166defineAssoc(File, "sha512", "data.attributes.sha512");
1167defineAssoc(File, "tags", "data.attributes.tagList");
1168defineAssoc(File, "instances", "data.attributes.instances");
1169File.endpoint = null;
1170
1171class Asset extends RallyBase {
1172 constructor({
1173 data,
1174 remote,
1175 included,
1176 lite
1177 }) {
1178 super();
1179 this.data = data;
1180 this.meta = {};
1181 this.remote = remote;
1182
1183 if (included) {
1184 this.meta.metadata = Asset.normalizeMetadata(included);
1185 }
1186
1187 this.lite = !!lite;
1188 }
1189
1190 static normalizeMetadata(payload) {
1191 let newMetadata = {};
1192
1193 for (let md of payload) {
1194 if (md.type !== "metadata") continue;
1195 newMetadata[md.attributes.usage] = md.attributes.metadata;
1196 }
1197
1198 return newMetadata;
1199 }
1200
1201 async getMetadata(forceRefresh = false) {
1202 if (this.meta.metadata && !forceRefresh) return this.meta.metadata;
1203 let req = await lib.makeAPIRequest({
1204 env: this.remote,
1205 path: `/movies/${this.id}/metadata`
1206 });
1207 return this.meta.metadata = Asset.normalizeMetadata(req.data);
1208 }
1209
1210 async patchMetadata(metadata) {
1211 if (metadata.Workflow) {
1212 //FIXME
1213 //Currently, WORKFLOW_METADATA cannot be patched via api: we need to
1214 //start a ephemeral eval to upload it
1215 let md = JSON.stringify(JSON.stringify(metadata.Workflow));
1216 let fakePreset = {
1217 code: `WORKFLOW_METADATA.update(json.loads(${md}))`
1218 };
1219 await this.startEphemeralEvaluateIdeal(fakePreset);
1220 log("WFMD Patched using ephemeralEval");
1221 }
1222
1223 if (metadata.Metadata) {
1224 let req = await lib.makeAPIRequest({
1225 env: this.remote,
1226 path: `/movies/${this.id}/metadata/Metadata`,
1227 method: "PATCH",
1228 payload: {
1229 "data": {
1230 "type": "metadata",
1231 "attributes": {
1232 "metadata": metadata.Metadata
1233 }
1234 }
1235 }
1236 });
1237 log("MD Patched");
1238 }
1239 }
1240
1241 static lite(id, remote) {
1242 return new this({
1243 data: {
1244 id
1245 },
1246 remote,
1247 lite: true
1248 });
1249 }
1250
1251 chalkPrint(pad = false) {
1252 let id = String("A-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
1253 if (pad) id = id.padStart(15);
1254 return chalk`{green ${id}}: {blue ${this.data.attributes ? this.name : "(lite asset)"}}`;
1255 }
1256
1257 static async createNew(name, env) {
1258 let req = await lib.makeAPIRequest({
1259 env,
1260 path: "/assets",
1261 method: "POST",
1262 payload: {
1263 data: {
1264 attributes: {
1265 name
1266 },
1267 type: "assets"
1268 }
1269 }
1270 });
1271 return new this({
1272 data: req.data,
1273 remote: env
1274 });
1275 }
1276
1277 async delete() {
1278 let req = await lib.makeAPIRequest({
1279 env: this.remote,
1280 path: "/assets/" + this.id,
1281 method: "DELETE"
1282 });
1283 }
1284
1285 async getFiles() {
1286 let req = await lib.indexPathFast({
1287 env: this.remote,
1288 path: `/assets/${this.id}/files`,
1289 method: "GET"
1290 }); //return req;
1291
1292 return new Collection(req.map(x => new File({
1293 data: x,
1294 remote: this.remote,
1295 parent: this
1296 })));
1297 }
1298
1299 async addFile(label, fileuris) {
1300 if (!Array.isArray(fileuris)) fileuris = [fileuris];
1301 let instances = {};
1302
1303 for (let i = 0; i < fileuris.length; i++) {
1304 instances[String(i + 1)] = {
1305 uri: fileuris[i]
1306 };
1307 }
1308
1309 let req = await lib.makeAPIRequest({
1310 env: this.remote,
1311 path: "/files",
1312 method: "POST",
1313 payload: {
1314 "data": {
1315 "attributes": {
1316 label,
1317 instances
1318 },
1319 "relationships": {
1320 "asset": {
1321 "data": {
1322 id: this.id,
1323 "type": "assets"
1324 }
1325 }
1326 },
1327 "type": "files"
1328 }
1329 }
1330 });
1331 return req;
1332 }
1333
1334 async startWorkflow(jobName, {
1335 initData,
1336 priority
1337 } = {}) {
1338 let attributes = {};
1339
1340 if (initData) {
1341 //Convert init data to string
1342 initData = typeof initData === "string" ? initData : JSON.stringify(initData);
1343 attributes.initData = initData;
1344 }
1345
1346 if (priority) {
1347 attributes.priority = priority;
1348 }
1349
1350 let req = await lib.makeAPIRequest({
1351 env: this.remote,
1352 path: "/workflows",
1353 method: "POST",
1354 payload: {
1355 "data": {
1356 "type": "workflows",
1357 attributes,
1358 "relationships": {
1359 "movie": {
1360 "data": {
1361 id: this.id,
1362 "type": "movies"
1363 }
1364 },
1365 "rule": {
1366 "data": {
1367 "attributes": {
1368 "name": jobName
1369 },
1370 "type": "rules"
1371 }
1372 }
1373 }
1374 }
1375 }
1376 });
1377 return req;
1378 }
1379
1380 static async startAnonWorkflow(env, jobName, {
1381 initData,
1382 priority
1383 } = {}) {
1384 let attributes = {};
1385
1386 if (initData) {
1387 //Convert init data to string
1388 initData = typeof initData === "string" ? initData : JSON.stringify(initData);
1389 attributes.initData = initData;
1390 }
1391
1392 if (priority) {
1393 attributes.priority = priority;
1394 }
1395
1396 let req = await lib.makeAPIRequest({
1397 env,
1398 path: "/workflows",
1399 method: "POST",
1400 payload: {
1401 "data": {
1402 "type": "workflows",
1403 attributes,
1404 "relationships": {
1405 "rule": {
1406 "data": {
1407 "attributes": {
1408 "name": jobName
1409 },
1410 "type": "rules"
1411 }
1412 }
1413 }
1414 }
1415 }
1416 });
1417 return req;
1418 }
1419
1420 async startEphemeralEvaluateIdeal(preset, dynamicPresetData, isBinary = false) {
1421 let res;
1422 const env = this.remote;
1423 let provider = await Provider.getByName(this.remote, "SdviEvaluate");
1424 write(chalk`Starting ephemeral evaluate on ${this.chalkPrint(false)}...`); // Fire and forget.
1425
1426 let evalInfo = await lib.makeAPIRequest({
1427 env: this.remote,
1428 path: "/jobs",
1429 method: "POST",
1430 payload: {
1431 data: {
1432 attributes: {
1433 category: provider.category,
1434 providerTypeName: provider.name,
1435 rallyConfiguration: {},
1436 //we need to strip invalid utf8 characters from the
1437 //buffer before we encode it or the sdvi backend dies
1438 providerData: Buffer.from(preset.code, isBinary && "binary" || "utf8").toString("base64"),
1439 dynamicPresetData
1440 },
1441 type: "jobs",
1442 relationships: {
1443 movie: {
1444 data: {
1445 id: this.id,
1446 type: "movies"
1447 }
1448 }
1449 }
1450 }
1451 }
1452 });
1453 write(" Waiting for finish...");
1454
1455 for (;;) {
1456 res = await lib.makeAPIRequest({
1457 env,
1458 path_full: evalInfo.data.links.self
1459 });
1460 write(".");
1461
1462 if (res.data.attributes.state == "Complete") {
1463 write(chalk`{green Done}...\n`);
1464 break;
1465 }
1466
1467 await sleep(300);
1468 }
1469
1470 return;
1471 }
1472
1473 async startEvaluate(presetid, dynamicPresetData) {
1474 // Fire and forget.
1475 let data = await lib.makeAPIRequest({
1476 env: this.remote,
1477 path: "/jobs",
1478 method: "POST",
1479 payload: {
1480 data: {
1481 type: "jobs",
1482 attributes: {
1483 dynamicPresetData
1484 },
1485 relationships: {
1486 movie: {
1487 data: {
1488 id: this.id,
1489 type: "movies"
1490 }
1491 },
1492 preset: {
1493 data: {
1494 id: presetid,
1495 type: "presets"
1496 }
1497 }
1498 }
1499 }
1500 }
1501 });
1502 return data;
1503 }
1504
1505 async rename(newName) {
1506 let req = await lib.makeAPIRequest({
1507 env: this.remote,
1508 path: `/assets/${this.id}`,
1509 method: "PATCH",
1510 payload: {
1511 data: {
1512 attributes: {
1513 name: newName
1514 },
1515 type: "assets"
1516 }
1517 }
1518 });
1519 this.name = newName;
1520 return req;
1521 }
1522
1523 async migrate(targetEnv) {
1524 configObject.globalProgress = false;
1525 log(`Creating paired file in ${targetEnv}`); //Fetch metadata in parallel, we await it later
1526
1527 let _mdPromise = this.getMetadata();
1528
1529 let targetAsset = await Asset.getByName(targetEnv, this.name);
1530
1531 if (targetAsset) {
1532 log(`Asset already exists ${targetAsset.chalkPrint()}`); //if(configObject.script) process.exit(10);
1533 } else {
1534 targetAsset = await Asset.createNew(this.name, targetEnv);
1535 log(`Asset created ${targetAsset.chalkPrint()}`);
1536 } //wait for metadata to be ready before patching
1537
1538
1539 await _mdPromise;
1540 log("Adding asset metadata");
1541 await targetAsset.patchMetadata(this.md);
1542 let fileCreations = [];
1543
1544 for (let file of await this.getFiles()) {
1545 //Check for any valid copy-able instances
1546 for (let inst of file.instancesList) {
1547 //We need to skip internal files
1548 if (inst.storageLocationName === "Rally Platform Bucket") continue;
1549 log(`Adding file: ${file.chalkPrint()}`);
1550 fileCreations.push(targetAsset.addFileInstance(file, inst));
1551 }
1552 }
1553
1554 await Promise.all(fileCreations);
1555 }
1556
1557 async addFileInstance(file, inst, tagList = []) {
1558 let newInst = {
1559 uri: File.rslURL(inst),
1560 name: inst.name,
1561 size: inst.size,
1562 lastModified: inst.lastModified,
1563 storageLocationName: inst.storageLocationName
1564 };
1565 let request = lib.makeAPIRequest({
1566 env: this.remote,
1567 path: `/files`,
1568 method: "POST",
1569 payload: {
1570 data: {
1571 type: "files",
1572 attributes: {
1573 label: file.label,
1574 tagList,
1575 instances: {
1576 "1": newInst
1577 }
1578 },
1579 relationships: {
1580 asset: {
1581 data: {
1582 id: this.id,
1583 type: "assets"
1584 }
1585 }
1586 }
1587 }
1588 }
1589 });
1590
1591 try {
1592 let fileData = await request;
1593 let newFile = new File({
1594 data: fileData.data,
1595 remote: this.remote,
1596 parent: this
1597 });
1598 if (configObject.script) console.log(inst.uri, newFile.instancesList[0].uri);
1599 } catch (e) {
1600 log(chalk`{red Failed file: ${file.chalkPrint()}}`);
1601 }
1602 }
1603
1604}
1605
1606defineAssoc(Asset, "id", "data.id");
1607defineAssoc(Asset, "name", "data.attributes.name");
1608defineAssoc(Asset, "remote", "meta.remote");
1609defineAssoc(Asset, "md", "meta.metadata");
1610defineAssoc(Asset, "lite", "meta.lite");
1611Asset.endpoint = "movies";
1612
1613let home;
1614
1615if (os.homedir) {
1616 home = os.homedir();
1617}
1618
1619const colon = /:/g;
1620const siloLike = /(silo\-\w+?)s?\/([^\/]+)\.([\w1234567890]+)$/g;
1621function pathTransform(path) {
1622 if (path.includes(":")) {
1623 //Ignore the first colon in window-like filesystems
1624 path = path.slice(0, 3) + path.slice(3).replace(colon, "--");
1625 }
1626
1627 if (configObject.invertedPath) {
1628 path = path.replace(siloLike, "$2-$1.$3");
1629 }
1630
1631 if (path.includes("\\342\\200\\220")) {
1632 path = path.replace("\\342\\200\\220", "‐");
1633 }
1634
1635 return path;
1636}
1637function readFileSync(path, options) {
1638 return fs__default.readFileSync(pathTransform(path), options);
1639} //Create writefilesync, with ability to create directory if it doesnt exist
1640
1641function writeFileSync(path$1, data, options, dircreated = false) {
1642 path$1 = pathTransform(path$1);
1643
1644 try {
1645 return fs__default.writeFileSync(path$1, data, options);
1646 } catch (e) {
1647 if (dircreated) throw e;
1648 let directory = path.dirname(path$1);
1649
1650 try {
1651 fs__default.statSync(directory);
1652 throw e;
1653 } catch (nodir) {
1654 fs__default.mkdirSync(directory);
1655 return writeFileSync(path$1, data, options, true);
1656 }
1657 }
1658}
1659
1660let exists = {};
1661
1662class Preset extends RallyBase {
1663 constructor({
1664 path: path$1,
1665 remote,
1666 data,
1667 subProject
1668 } = {}) {
1669 // Get full path if possible
1670 if (path$1) {
1671 path$1 = path.resolve(path$1);
1672
1673 if (path.dirname(path$1).includes("silo-metadata")) {
1674 throw new AbortError("Constructing preset from metadata file");
1675 }
1676 }
1677
1678 super(); // Cache by path
1679
1680 if (path$1) {
1681 if (exists[pathTransform(path$1)]) return exists[pathTransform(path$1)];
1682 exists[pathTransform(path$1)] = this;
1683 }
1684
1685 this.meta = {};
1686 this.subproject = subProject;
1687 this.remote = remote;
1688
1689 if (lib.isLocalEnv(this.remote)) {
1690 if (path$1) {
1691 this.path = path$1;
1692 let pathspl = this.path.split(".");
1693 this.ext = pathspl[pathspl.length - 1];
1694
1695 try {
1696 this.code = this.getLocalCode();
1697 } catch (e) {
1698 if (e.code === "ENOENT" && configObject.ignoreMissing) {
1699 this.missing = true;
1700 return undefined;
1701 } else {
1702 log(chalk`{red Node Error} ${e.message}`);
1703 throw new AbortError("Could not load code of local file");
1704 }
1705 }
1706
1707 let name = this.parseFilenameForName() || this.parseCodeForName();
1708
1709 try {
1710 this.data = this.getLocalMetadata();
1711 this.isGeneric = true;
1712 name = this.name;
1713 } catch (e) {
1714 log(chalk`{yellow Warning}: ${path$1} does not have a readable metadata file! Looking for ${this.localmetadatapath}`);
1715 this.data = Preset.newShell(name);
1716 this.isGeneric = false;
1717 }
1718
1719 this.name = name;
1720 } else {
1721 this.data = Preset.newShell();
1722 }
1723 } else {
1724 this.data = data; //this.name = data.attributes.name;
1725 //this.id = data.id;
1726
1727 this.isGeneric = false;
1728 }
1729
1730 this.data.attributes.rallyConfiguration = undefined;
1731 this.data.attributes.systemManaged = undefined;
1732 } //Given a metadata file, get its actualy file
1733
1734
1735 static async fromMetadata(path, subproject) {
1736 let data;
1737
1738 try {
1739 data = JSON.parse(readFileSync(path));
1740 } catch (e) {
1741 if (e.code === "ENOENT" && configObject.ignoreMissing) {
1742 return null;
1743 } else {
1744 throw e;
1745 }
1746 }
1747
1748 let providerType = data.relationships.providerType.data.name;
1749 let provider = await Provider.getByName("DEV", providerType);
1750
1751 if (!provider) {
1752 log(chalk`{red The provider type {green ${providerType}} does not exist}`);
1753 log(chalk`{red Skipping {green ${path}}.}`);
1754 return null;
1755 }
1756
1757 let ext = await provider.getFileExtension();
1758 let name = data.attributes.name;
1759 let realpath = Preset.getLocalPath(name, ext, subproject);
1760 return new Preset({
1761 path: realpath,
1762 subProject: subproject
1763 });
1764 }
1765
1766 static newShell(name = undefined) {
1767 return {
1768 "attributes": {
1769 "providerSettings": {
1770 "PresetName": name
1771 }
1772 },
1773 "relationships": {},
1774 "type": "presets"
1775 };
1776 }
1777
1778 cleanup() {
1779 super.cleanup();
1780 delete this.attributes["createdAt"];
1781 delete this.attributes["updatedAt"];
1782 }
1783
1784 async acclimatize(env) {
1785 if (!this.isGeneric) throw new AbortError("Cannot acclimatize non-generics or shells");
1786 let providers = await Provider.getAll(env);
1787 let ptype = this.relationships["providerType"];
1788 ptype = ptype.data;
1789 let provider = providers.findByName(ptype.name);
1790 ptype.id = provider.id;
1791 }
1792
1793 get test() {
1794 if (!this.code) return [];
1795 const regex = /[^-]autotest:\s?([\w\d_\-. \/]+)[\r\s\n]*?/gm;
1796 let match;
1797 let matches = [];
1798
1799 while (match = regex.exec(this.code)) {
1800 matches.push(match[1]);
1801 }
1802
1803 return matches;
1804 }
1805
1806 async runTest(env) {
1807 let remote = await Preset.getByName(env, this.name);
1808
1809 for (let test of this.test) {
1810 log("Tests...");
1811 let asset;
1812
1813 if (test.startsWith("id")) {
1814 let match = /id:\s*(\d+)/g.exec(test);
1815
1816 if (!match) {
1817 log(chalk`{red Could not parse autotest} ${test}.`);
1818 throw new AbortError("Could not properly parse the preset header");
1819 }
1820
1821 asset = await Asset.getById(env, match[1]);
1822 } else {
1823 asset = await Asset.getByName(env, test);
1824 }
1825
1826 if (!asset) {
1827 log(chalk`{yellow No movie found}, skipping test.`);
1828 continue;
1829 }
1830
1831 log(chalk`Starting job {green ${this.name}} on ${asset.chalkPrint(false)}... `);
1832 await asset.startEvaluate(remote.id);
1833 }
1834 }
1835
1836 async resolve() {
1837 if (this.isGeneric) return;
1838 let proType = await this.resolveField(Provider, "providerType");
1839 this.ext = await proType.getFileExtension();
1840 this.isGeneric = true;
1841 return {
1842 proType
1843 };
1844 }
1845
1846 async saveLocal() {
1847 await this.saveLocalMetadata();
1848 await this.saveLocalFile();
1849 }
1850
1851 async saveLocalMetadata() {
1852 if (!this.isGeneric) {
1853 await this.resolve();
1854 this.cleanup();
1855 }
1856
1857 writeFileSync(this.localmetadatapath, JSON.stringify(this.data, null, 4));
1858 }
1859
1860 async saveLocalFile() {
1861 writeFileSync(this.localpath, this.code);
1862 }
1863
1864 async uploadRemote(env) {
1865 await this.uploadCodeToEnv(env, true);
1866 }
1867
1868 async save(env) {
1869 this.saved = true;
1870
1871 if (!this.isGeneric) {
1872 await this.resolve();
1873 }
1874
1875 this.cleanup();
1876
1877 if (lib.isLocalEnv(env)) {
1878 log(chalk`Saving preset {green ${this.name}} to {blue ${lib.envName(env)}}.`);
1879 await this.saveLocal();
1880 } else {
1881 await this.uploadRemote(env);
1882 }
1883 }
1884
1885 async downloadCode() {
1886 if (!this.remote || this.code) return this.code;
1887 let code = await lib.makeAPIRequest({
1888 env: this.remote,
1889 path_full: this.data.links.providerData,
1890 json: false
1891 }); //match header like
1892 // # c: d
1893 // # b
1894 // # a
1895 // ##################
1896
1897 let headerRegex = /(^# .+[\r\n]+)+#+[\r\n]+/gim;
1898 let hasHeader = headerRegex.exec(code);
1899
1900 if (hasHeader) {
1901 this.header = code.substring(0, hasHeader[0].length - 1);
1902 code = code.substring(hasHeader[0].length);
1903 }
1904
1905 return this.code = code;
1906 }
1907
1908 get code() {
1909 if (this._code) return this._code;
1910 }
1911
1912 set code(v) {
1913 this._code = v;
1914 }
1915
1916 chalkPrint(pad = true) {
1917 let id = String("P-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
1918 let sub = "";
1919
1920 if (this.subproject) {
1921 sub = chalk`{yellow ${this.subproject}}`;
1922 }
1923
1924 if (pad) id = id.padStart(10);
1925
1926 if (this.name == undefined) {
1927 return chalk`{green ${id}}: ${sub}{red ${this.path}}`;
1928 } else if (this.meta.proType) {
1929 return chalk`{green ${id}}: ${sub}{red ${this.meta.proType.name}} {blue ${this.name}}`;
1930 } else {
1931 return chalk`{green ${id}}: ${sub}{blue ${this.name}}`;
1932 }
1933 }
1934
1935 parseFilenameForName() {
1936 if (this.path.endsWith(".jinja") || this.path.endsWith(".json")) {
1937 return path.basename(this.path).replace("_", " ").replace("-", " ").replace(".json", "").replace(".jinja", "");
1938 }
1939 }
1940
1941 parseCodeForName() {
1942 const name_regex = /name\s?:\s([\w\d. \/]+)[\r\s\n]*?/;
1943 const match = name_regex.exec(this.code);
1944 if (match) return match[1];
1945 }
1946
1947 findStringsInCode(strings) {
1948 if (!this.code) return [];
1949 return strings.filter(str => {
1950 let regex = new RegExp(str);
1951 return !!this.code.match(regex);
1952 });
1953 }
1954
1955 static getLocalPath(name, ext, subproject) {
1956 return path__default.join(configObject.repodir, subproject || "", "silo-presets", name + "." + ext);
1957 }
1958
1959 get localpath() {
1960 return Preset.getLocalPath(this.name, this.ext, this.subproject);
1961 }
1962
1963 get path() {
1964 if (this._path) return this._path;
1965 }
1966
1967 set path(val) {
1968 this._path = val;
1969 }
1970
1971 get name() {
1972 return this._nameOuter;
1973 }
1974
1975 set name(val) {
1976 if (!this._nameInner) this._nameInner = val;
1977 this._nameOuter = val;
1978 }
1979
1980 set providerType(value) {
1981 this.relationships["providerType"] = {
1982 data: { ...value,
1983 type: "providerTypes"
1984 }
1985 };
1986 }
1987
1988 get localmetadatapath() {
1989 if (this.path) {
1990 return this.path.replace("silo-presets", "silo-metadata").replace(new RegExp(this.ext + "$"), "json");
1991 }
1992
1993 return path__default.join(configObject.repodir, this.subproject || "", "silo-metadata", this.name + ".json");
1994 }
1995
1996 get immutable() {
1997 return this.name.includes("Constant") && !configObject.updateImmutable;
1998 }
1999
2000 async uploadPresetData(env, id) {
2001 var _this$relationships, _this$relationships$p, _this$relationships$p2;
2002
2003 if (this.code.trim() === "NOUPLOAD") {
2004 write(chalk`code skipped {yellow :)}, `);
2005 return;
2006 }
2007
2008 let code = this.code;
2009 let headers = {};
2010 let providerName = (_this$relationships = this.relationships) === null || _this$relationships === void 0 ? void 0 : (_this$relationships$p = _this$relationships.providerType) === null || _this$relationships$p === void 0 ? void 0 : (_this$relationships$p2 = _this$relationships$p.data) === null || _this$relationships$p2 === void 0 ? void 0 : _this$relationships$p2.name;
2011
2012 if (!configObject.skipHeader && (providerName === "SdviEvaluate" || providerName === "SdviEvalPro")) {
2013 write(chalk`generate header, `);
2014 let repodir = configObject.repodir;
2015 let localpath = this.path.replace(repodir, "");
2016 if (localpath.startsWith("/")) localpath = localpath.substring(1);
2017
2018 try {
2019 let {
2020 stdout: headerText
2021 } = await spawn({
2022 noecho: true
2023 }, "sh", [path__default.join(configObject.repodir, `bin/header.sh`), moment(Date.now()).format("ddd YYYY/MM/DD hh:mm:ssa"), localpath]);
2024 code = headerText + code;
2025 write(chalk`header ok, `);
2026 } catch (e) {
2027 write(chalk`missing unix, `);
2028 }
2029 } //binary presets
2030
2031
2032 if (providerName == "Vantage") {
2033 code = code.toString("base64");
2034 headers["Content-Transfer-Encoding"] = "base64";
2035 }
2036
2037 let res = await lib.makeAPIRequest({
2038 env,
2039 path: `/presets/${id}/providerData`,
2040 body: code,
2041 method: "PUT",
2042 fullResponse: true,
2043 timeout: 10000,
2044 headers
2045 });
2046 write(chalk`code up {yellow ${res.statusCode}}, `);
2047 }
2048
2049 async grabMetadata(env) {
2050 let remote = await Preset.getByName(env, this.name);
2051 this.isGeneric = false;
2052
2053 if (!remote) {
2054 throw new AbortError(`No file found on remote ${env} with name ${this.name}`);
2055 }
2056
2057 this.data = remote.data;
2058 this.remote = env;
2059 }
2060
2061 async deleteRemoteVersion(env, id = null) {
2062 if (lib.isLocalEnv(env)) return false;
2063
2064 if (!id) {
2065 let remote = await Preset.getByName(env, this.name);
2066 id = remote.id;
2067 }
2068
2069 return await lib.makeAPIRequest({
2070 env,
2071 path: `/presets/${id}`,
2072 method: "DELETE"
2073 });
2074 }
2075
2076 async delete() {
2077 if (lib.isLocalEnv(this.remote)) return false;
2078 return await this.deleteRemoteVersion(this.remote, this.id);
2079 }
2080
2081 async uploadCodeToEnv(env, includeMetadata, shouldTest = true) {
2082 if (!this.name) {
2083 let match;
2084
2085 if (match = /^(#|["']{3})\s*EPH (\d+)/.exec(this.code.trim())) {
2086 let a = await Asset.getById(env, Number(match[2]));
2087 return a.startEphemeralEvaluateIdeal(this);
2088 } else {
2089 log(chalk`Failed uploading {red ${this.path}}. No name found.`);
2090 return;
2091 }
2092 }
2093
2094 write(chalk`Uploading preset {green ${this.name}} to {green ${env}}: `);
2095
2096 if (this.immutable) {
2097 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
2098 return;
2099 } //First query the api to see if this already exists.
2100
2101
2102 let remote = await Preset.getByName(env, this.name);
2103
2104 if (remote) {
2105 //If it exists we can replace it
2106 write("replace, ");
2107
2108 if (includeMetadata) {
2109 let payload = {
2110 data: {
2111 attributes: this.data.attributes,
2112 type: "presets"
2113 }
2114 };
2115
2116 if (this.relationships.tagNames) {
2117 payload.relationships = {
2118 tagNames: this.relationships.tagNames
2119 };
2120 }
2121
2122 let res = await lib.makeAPIRequest({
2123 env,
2124 path: `/presets/${remote.id}`,
2125 method: "PATCH",
2126 payload,
2127 fullResponse: true
2128 });
2129 write(chalk`metadata {yellow ${res.statusCode}}, `);
2130
2131 if (res.statusCode == 500) {
2132 log(chalk`skipping code upload, did not successfully upload metadata`);
2133 return;
2134 }
2135 }
2136
2137 await this.uploadPresetData(env, remote.id);
2138 } else {
2139 write("create, ");
2140 let metadata = {
2141 data: this.data
2142 };
2143
2144 if (!this.relationships["providerType"]) {
2145 throw new AbortError("Cannot acclimatize shelled presets. (try creating it on the env first)");
2146 }
2147
2148 await this.acclimatize(env);
2149 write("Posting to create preset... ");
2150 let res = await lib.makeAPIRequest({
2151 env,
2152 path: `/presets`,
2153 method: "POST",
2154 payload: metadata,
2155 timeout: 5000
2156 });
2157 let id = res.data.id;
2158 write(chalk`Created id {green ${id}}... Uploading Code... `);
2159 await this.uploadPresetData(env, id);
2160 }
2161
2162 if (this.test[0] && shouldTest) {
2163 await this.runTest(env);
2164 } else {
2165 log("No tests. Done.");
2166 }
2167 }
2168
2169 getLocalMetadata() {
2170 return JSON.parse(readFileSync(this.localmetadatapath, "utf-8"));
2171 }
2172
2173 getLocalCode() {
2174 //todo fixup for binary presets, see uploadPresetData
2175 return readFileSync(this.path, "utf-8");
2176 }
2177
2178 parseHeaderInfo() {
2179 var _$exec$, _$exec$2, _$exec$3, _$exec$4, _$exec$5, _$exec$6, _$exec$7;
2180
2181 if (!this.header) return null;
2182 let abs = {
2183 built: (_$exec$ = /Built On:(.+)/.exec(this.header)[1]) === null || _$exec$ === void 0 ? void 0 : _$exec$.trim(),
2184 author: (_$exec$2 = /Author:(.+)/.exec(this.header)[1]) === null || _$exec$2 === void 0 ? void 0 : _$exec$2.trim(),
2185 build: (_$exec$3 = /Build:(.+)/.exec(this.header)[1]) === null || _$exec$3 === void 0 ? void 0 : _$exec$3.trim(),
2186 version: (_$exec$4 = /Version:(.+)/.exec(this.header)[1]) === null || _$exec$4 === void 0 ? void 0 : _$exec$4.trim(),
2187 branch: (_$exec$5 = /Branch:(.+)/.exec(this.header)[1]) === null || _$exec$5 === void 0 ? void 0 : _$exec$5.trim(),
2188 commit: (_$exec$6 = /Commit:(.+)/.exec(this.header)[1]) === null || _$exec$6 === void 0 ? void 0 : _$exec$6.trim(),
2189 local: (_$exec$7 = /Local File:(.+)/.exec(this.header)[1]) === null || _$exec$7 === void 0 ? void 0 : _$exec$7.trim()
2190 };
2191 let tryFormats = [[true, "ddd MMM DD HH:mm:ss YYYY"], [false, "ddd YYYY/MM/DD LTS"]];
2192
2193 for (let [isUTC, format] of tryFormats) {
2194 let date;
2195
2196 if (isUTC) {
2197 date = moment.utc(abs.built, format);
2198 } else {
2199 date = moment(abs.built, format);
2200 }
2201
2202 if (!date.isValid()) continue;
2203 abs.offset = date.fromNow();
2204 break;
2205 }
2206
2207 return abs;
2208 }
2209
2210 async printRemoteInfo(env) {
2211 let remote = await Preset.getByName(env, this.name);
2212 await remote.downloadCode();
2213 let i = remote.parseHeaderInfo();
2214
2215 if (i) {
2216 log(chalk`
2217 ENV: {red ${env}}, updated {yellow ~${i.offset}}
2218 Built on {blue ${i.built}} by {green ${i.author}}
2219 From ${i.build || "(unknown)"} on ${i.branch} ({yellow ${i.commit}})
2220 `.replace(/^[ \t]+/gim, "").trim());
2221 } else {
2222 log(chalk`No header on {red ${env}}`);
2223 }
2224 }
2225
2226 async getInfo(envs) {
2227 await this.printDepends();
2228
2229 for (let env of envs.split(",")) {
2230 await this.printRemoteInfo(env);
2231 }
2232 }
2233
2234 async printDepends(indent = 0, locals = null, seen = {}) {
2235 let includeRegex = /@include "(.+)"/gim; //let includeRegex = /@include/g;
2236
2237 let includes = [];
2238 let inc;
2239
2240 while (inc = includeRegex.exec(this.code)) {
2241 includes.push(inc[1]);
2242 } //let includes = this.code
2243 //.split("\n")
2244 //.map(x => includeRegex.exec(x))
2245 //.filter(x => x)
2246 //.map(x => x[1]);
2247 //log(includes);
2248
2249
2250 if (!locals) {
2251 locals = new Collection((await loadLocals("silo-presets", Preset)));
2252 }
2253
2254 log(Array(indent + 1).join(" ") + "- " + this.name);
2255
2256 for (let include of includes) {
2257 if (seen[include]) {
2258 log(Array(indent + 1).join(" ") + " - (seen) " + include);
2259 } else {
2260 seen[include] = true;
2261 let file = await locals.findByName(include);
2262
2263 if (file) {
2264 await file.printDepends(indent + 2, locals, seen);
2265 } else {
2266 log(Array(indent + 1).join(" ") + " - (miss) " + include);
2267 }
2268 }
2269 }
2270 }
2271
2272}
2273
2274defineAssoc(Preset, "_nameInner", "data.attributes.providerSettings.PresetName");
2275defineAssoc(Preset, "_nameOuter", "data.attributes.name");
2276defineAssoc(Preset, "id", "data.id");
2277defineAssoc(Preset, "attributes", "data.attributes");
2278defineAssoc(Preset, "relationships", "data.relationships");
2279defineAssoc(Preset, "remote", "meta.remote");
2280defineAssoc(Preset, "_code", "meta.code");
2281defineAssoc(Preset, "_path", "meta.path");
2282defineAssoc(Preset, "isGeneric", "meta.isGeneric");
2283defineAssoc(Preset, "ext", "meta.ext");
2284defineAssoc(Preset, "subproject", "meta.project");
2285defineAssoc(Preset, "metastring", "meta.metastring");
2286Preset.endpoint = "presets";
2287
2288class Notification extends RallyBase {
2289 constructor({
2290 data,
2291 remote
2292 }) {
2293 super();
2294 this.data = data;
2295 this.meta = {};
2296 this.remote = remote;
2297 }
2298
2299 static async getAllPreCollect(notifications) {
2300 return notifications.sort((a, b) => {
2301 return a.attributes.type.localeCompare(b.attributes.type) || a.attributes.name.localeCompare(b.attributes.name);
2302 });
2303 }
2304
2305 chalkPrint(pad = false) {
2306 let id = String("N-" + this.id);
2307 if (pad) id = id.padStart(4);
2308 return chalk`{green ${id}}: {blue ${this.type}} - {green ${this.name}}`;
2309 }
2310
2311}
2312
2313defineAssoc(Notification, "id", "data.id");
2314defineAssoc(Notification, "name", "data.attributes.name");
2315defineAssoc(Notification, "address", "data.attributes.address");
2316defineAssoc(Notification, "type", "data.attributes.type");
2317defineAssoc(Notification, "remote", "meta.remote");
2318Notification.endpoint = "notificationPresets";
2319
2320class Rule extends RallyBase {
2321 constructor({
2322 path: path$1,
2323 data,
2324 remote,
2325 subProject
2326 } = {}) {
2327 super();
2328
2329 if (path$1) {
2330 path$1 = path.resolve(path$1);
2331
2332 try {
2333 let f = readFileSync(path$1, "utf-8");
2334 data = JSON.parse(readFileSync(path$1, "utf-8"));
2335 } catch (e) {
2336 if (e.code === "ENOENT") {
2337 if (configObject.ignoreMissing) {
2338 this.missing = true;
2339 return undefined;
2340 } else {
2341 throw new AbortError("Could not load code of local file");
2342 }
2343 } else {
2344 throw new AbortError(`Unreadable JSON in ${path$1}. ${e}`);
2345 }
2346 }
2347 }
2348
2349 this.meta = {};
2350 this.subproject = subProject;
2351
2352 if (!data) {
2353 data = Rule.newShell();
2354 }
2355
2356 this.data = data;
2357 this.remote = remote;
2358 this.isGeneric = !this.remote;
2359 }
2360
2361 static newShell() {
2362 return {
2363 "attributes": {
2364 "description": "-",
2365 "priority": "PriorityNorm",
2366 "starred": false
2367 },
2368 "relationships": {},
2369 "type": "workflowRules"
2370 };
2371 }
2372
2373 async acclimatize(env) {
2374 this.remote = env;
2375 let preset = await this.resolveField(Preset, "preset", false, "specific");
2376 let pNext = await this.resolveField(Rule, "passNext", false, "specific");
2377 let eNext = await this.resolveField(Rule, "errorNext", false, "specific");
2378 let proType = await this.resolveField(Provider, "providerType", false, "specific");
2379 let dynamicNexts = await this.resolveField(Rule, "dynamicNexts", true, "specific");
2380 let enterNotif = await this.resolveField(Notification, "enterNotifications", true, "specific");
2381 let errorNotif = await this.resolveField(Notification, "errorNotifications", true, "specific");
2382 let passNotif = await this.resolveField(Notification, "passNotifications", true, "specific");
2383 }
2384
2385 async saveA(env) {
2386 if (lib.isLocalEnv(env)) return;
2387 return await this.createIfNotExist(env);
2388 }
2389
2390 async saveB(env) {
2391 if (!this.isGeneric) {
2392 await this.resolve();
2393 }
2394
2395 this.cleanup();
2396
2397 if (lib.isLocalEnv(env)) {
2398 log(chalk`Saving rule {green ${this.name}} to {blue ${lib.envName(env)}}.`);
2399 writeFileSync(this.localpath, JSON.stringify(this.data, null, 4));
2400 } else {
2401 await this.acclimatize(env);
2402 await this.uploadRemote(env);
2403 }
2404 }
2405
2406 get immutable() {
2407 return false;
2408 }
2409
2410 async createIfNotExist(env) {
2411 write(chalk`First pass rule {green ${this.name}} to {green ${env}}: `);
2412
2413 if (this.immutable) {
2414 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
2415 return;
2416 } //First query the api to see if this already exists.
2417
2418
2419 let remote = await Rule.getByName(env, this.name);
2420 this.idMap = this.idMap || {};
2421
2422 if (remote) {
2423 this.idMap[env] = remote.id;
2424 log(chalk`exists ${remote.chalkPrint(false)}`);
2425 return;
2426 } //If it exists we can replace it
2427
2428
2429 write("create, ");
2430 let res = await lib.makeAPIRequest({
2431 env,
2432 path: `/workflowRules`,
2433 method: "POST",
2434 payload: {
2435 data: {
2436 attributes: {
2437 name: this.name
2438 },
2439 type: "workflowRules"
2440 }
2441 }
2442 });
2443 this.idMap = this.idMap || {};
2444 this.idMap[env] = res.data.id;
2445 write("id ");
2446 log(this.idMap[env]);
2447 }
2448
2449 async patchStrip() {
2450 delete this.data.attributes.createdAt;
2451 delete this.data.attributes.starred;
2452 delete this.data.attributes.updatedAt; // TEMP FIX FOR BUG IN SDVI
2453
2454 if (this.relationships.passMetadata && this.relationships.passMetadata[0]) {
2455 log("HAS PASS");
2456 log(this.name);
2457 log("HAS PASS");
2458 }
2459
2460 delete this.relationships.passMetadata;
2461
2462 if (this.relationships.errorMetadata && this.relationships.errorMetadata[0]) {
2463 log("HAS PASS");
2464 log(this.name);
2465 log("HAS PASS");
2466 }
2467
2468 delete this.relationships.errorMetadata; // This is commented out because it was fixed.
2469 //for(let key in this.relationships){
2470 //let relationship = this.relationships[key];
2471 //if(!relationship.data || relationship.data instanceof Array && !relationship.data[0]){
2472 //delete this.relationships[key];
2473 //}
2474 //}
2475 }
2476
2477 async uploadRemote(env) {
2478 write(chalk`Uploading rule {green ${this.name}} to {green ${env}}: `);
2479
2480 if (this.immutable) {
2481 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
2482 return;
2483 }
2484
2485 if (this.idMap[env]) {
2486 this.remote = env;
2487 await this.patchStrip();
2488 this.data.id = this.idMap[env]; //If it exists we can replace it
2489
2490 write("replace, ");
2491 let res = await lib.makeAPIRequest({
2492 env,
2493 path: `/workflowRules/${this.idMap[env]}`,
2494 method: "PATCH",
2495 payload: {
2496 data: this.data
2497 },
2498 fullResponse: true
2499 });
2500 log(chalk`response {yellow ${res.statusCode}}`);
2501
2502 if (res.statusCode !== 200) {
2503 log(res.body);
2504 log(JSON.stringify(this.data, null, 4));
2505 }
2506 } else {
2507 throw Error("Bad idmap!");
2508 }
2509 }
2510
2511 get localpath() {
2512 return path.join(configObject.repodir, this.subproject || "", "silo-rules", this.name + ".json");
2513 }
2514
2515 async resolve() {
2516 let preset = await this.resolveField(Preset, "preset", false); //log(preset);
2517
2518 let pNext = await this.resolveField(Rule, "passNext", false);
2519 let eNext = await this.resolveField(Rule, "errorNext", false);
2520 let proType = await this.resolveField(Provider, "providerType", false); //log("Dynamic nexts")
2521
2522 let dynamicNexts = await this.resolveField(Rule, "dynamicNexts", true); //log(dynamicNexts);
2523
2524 let enterNotif = await this.resolveField(Notification, "enterNotifications", true);
2525 let errorNotif = await this.resolveField(Notification, "errorNotifications", true);
2526 let passNotif = await this.resolveField(Notification, "passNotifications", true); //TODO Unsupported
2527
2528 delete this.relationships["enterMetadata"];
2529 delete this.relationships["errorMetadata"];
2530 this.isGeneric = true;
2531 return {
2532 preset,
2533 proType,
2534 pNext,
2535 eNext,
2536 dynamicNexts,
2537 errorNotif,
2538 enterNotif,
2539 passNotif
2540 };
2541 }
2542
2543 chalkPrint(pad = true) {
2544 let id = String("R-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
2545 let sub = "";
2546
2547 if (this.subproject) {
2548 sub = chalk`{yellow ${this.subproject}}`;
2549 }
2550
2551 if (pad) id = id.padStart(10);
2552
2553 try {
2554 return chalk`{green ${id}}: ${sub}{blue ${this.name}}`;
2555 } catch (e) {
2556 return this.data;
2557 }
2558 }
2559
2560}
2561
2562defineAssoc(Rule, "name", "data.attributes.name");
2563defineAssoc(Rule, "description", "data.attributes.description");
2564defineAssoc(Rule, "id", "data.id");
2565defineAssoc(Rule, "relationships", "data.relationships");
2566defineAssoc(Rule, "isGeneric", "meta.isGeneric");
2567defineAssoc(Rule, "remote", "meta.remote");
2568defineAssoc(Rule, "subproject", "meta.project");
2569defineAssoc(Rule, "idMap", "meta.idMap");
2570Rule.endpoint = "workflowRules";
2571
2572//Move project into silo metadata
2573//move autotest into silo metadata
2574//
2575
2576class SupplyChain {
2577 constructor(startingRule, stopRule) {
2578 if (startingRule) {
2579 this.startingRule = startingRule;
2580 this.stopRule = stopRule;
2581 this.remote = startingRule.remote;
2582 }
2583 }
2584
2585 async downloadPresetCode(objs = this.allPresets) {
2586 log("Downloading code... ");
2587 await lib.keepalive(objs.arr.map(x => () => x.downloadCode()));
2588 }
2589
2590 async calculate() {
2591 log("Getting rules... ");
2592 this.allRules = await Rule.getAll(this.remote);
2593 log(this.allRules.length);
2594 log("Getting presets... ");
2595 this.allPresets = await Preset.getAll(this.remote);
2596 log(this.allPresets.length);
2597 log("Getting providers... ");
2598 this.allProviders = await Provider.getAll(this.remote);
2599 log(this.allProviders.length);
2600 log("Getting notifications... ");
2601 this.allNotifications = await Notification.getAll(this.remote);
2602 log(this.allNotifications.length);
2603
2604 if (!this.startingRule) {
2605 this.rules = this.allRules;
2606 this.presets = this.allPresets;
2607 this.notifications = new Collection([]);
2608 await this.downloadPresetCode();
2609 return;
2610 } else {
2611 await this.downloadPresetCode();
2612 }
2613
2614 log("Done!"); //Now we have everything we need to find a whole supply chain
2615
2616 write("Calculating Supply chain... ");
2617 log(this.startingRule.chalkPrint());
2618 let allRuleNames = this.allRules.arr.map(x => x.name).filter(x => x.length >= 4);
2619 let allPresetNames = this.allPresets.arr.map(x => x.name).filter(x => x.length >= 4);
2620 let allNotifNames = this.allNotifications.arr.map(x => x.name).filter(x => x.length >= 4);
2621 let requiredNotifications = new Set();
2622 let ruleQueue = [this.startingRule];
2623 let presetQueue = [];
2624
2625 for (let currentRule of ruleQueue) {
2626 if (currentRule === this.stopRule) continue;
2627 let {
2628 eNext,
2629 pNext,
2630 preset,
2631 passNotif,
2632 errorNotif,
2633 enterNotif
2634 } = await currentRule.resolve();
2635 passNotif.forEach(n => requiredNotifications.add(n));
2636 enterNotif.forEach(n => requiredNotifications.add(n));
2637 errorNotif.forEach(n => requiredNotifications.add(n));
2638 if (eNext && !ruleQueue.includes(eNext)) ruleQueue.push(eNext);
2639 if (pNext && !ruleQueue.includes(eNext)) ruleQueue.push(pNext);
2640 let neededPresets = preset.findStringsInCode(allPresetNames);
2641 neededPresets = neededPresets.map(x => this.allPresets.findByName(x));
2642 let neededRules = preset.findStringsInCode(allRuleNames);
2643 neededRules = neededRules.map(x => this.allRules.findByName(x));
2644 preset.findStringsInCode(allNotifNames).map(str => this.allNotifications.findByName(str)).forEach(notif => requiredNotifications.add(notif));
2645 neededPresets.push(preset);
2646
2647 for (let p of neededPresets) if (!presetQueue.includes(p)) presetQueue.push(p);
2648
2649 for (let p of neededRules) if (!ruleQueue.includes(p)) ruleQueue.push(p);
2650
2651 if (configObject.verbose) {
2652 write(currentRule.chalkPrint(false));
2653 log(":");
2654 write(" ");
2655 write(preset.chalkPrint(false));
2656 log(":");
2657 write(" Pass Next: ");
2658 if (pNext) write(pNext.chalkPrint(false));else write("None");
2659 log("");
2660 write(" Err Next: ");
2661 if (eNext) write(eNext.chalkPrint(false));else write("None");
2662 log("");
2663 log(" Rules:");
2664
2665 for (let p of neededRules) log(" " + p.chalkPrint(true));
2666
2667 log(" Presets:");
2668
2669 for (let p of neededPresets) log(" " + p.chalkPrint(true));
2670
2671 log("\n");
2672 }
2673 }
2674
2675 log("Done!");
2676 this.rules = new Collection(ruleQueue);
2677 this.presets = new Collection(presetQueue);
2678 requiredNotifications.delete(undefined);
2679 this.notifications = new Collection([...requiredNotifications]);
2680 }
2681
2682 async log() {
2683 if (this.notifications.arr.length > 0) {
2684 log("Required notifications: ");
2685 this.notifications.log();
2686 }
2687
2688 if (this.rules.arr.length > 0) {
2689 write("Required rules: ");
2690 log(this.rules.arr.length);
2691 this.rules.log();
2692 }
2693
2694 if (this.presets.arr.length > 0) {
2695 write("Required presets: ");
2696 log(this.presets.arr.length);
2697 this.presets.log();
2698 }
2699
2700 if (configObject.rawOutput) {
2701 return {
2702 presets: this.presets.arr,
2703 rules: this.rules.arr,
2704 notifications: this.notifications.arr
2705 };
2706 }
2707 }
2708
2709 async deleteTo(env) {
2710 for (let preset of this.presets) {
2711 try {
2712 await preset.deleteRemoteVersion(env);
2713 } catch (e) {
2714 log(e);
2715 }
2716 }
2717 }
2718
2719 async syncTo(env) {
2720 for (let preset of this.presets) {
2721 try {
2722 await preset.save(env);
2723 } catch (e) {
2724 log(e);
2725 }
2726 }
2727
2728 if (this.rules.arr[0]) {
2729 log("Starting create phase for rules");
2730
2731 for (let rule of this.rules) {
2732 try {
2733 await rule.saveA(env);
2734 } catch (e) {
2735 log(e);
2736 }
2737 }
2738
2739 log("OK");
2740 log("Starting link phase for rules");
2741 Rule.removeCache(env);
2742
2743 for (let rule of this.rules) {
2744 try {
2745 await rule.saveB(env);
2746 } catch (e) {
2747 log(e);
2748 }
2749 }
2750 }
2751 }
2752
2753}
2754
2755class User extends RallyBase {
2756 constructor({
2757 data,
2758 remote
2759 }) {
2760 super();
2761 this.data = data;
2762 this.meta = {};
2763 this.remote = remote;
2764 }
2765
2766 chalkPrint(pad = false) {
2767 let id = String("U-" + this.id);
2768 if (pad) id = id.padStart(7);
2769 return chalk`{green ${id}}: {blue ${this.name}}`;
2770 }
2771
2772}
2773
2774defineAssoc(User, "id", "data.id");
2775defineAssoc(User, "name", "data.attributes.name");
2776defineAssoc(User, "email", "data.attributes.email");
2777defineAssoc(User, "remote", "meta.remote");
2778User.endpoint = "users";
2779
2780class Tag extends RallyBase {
2781 constructor({
2782 data,
2783 remote
2784 } = {}) {
2785 super();
2786 this.meta = {};
2787 this.remote = remote;
2788 this.data = data; //this.data.attributes.rallyConfiguration = undefined;
2789 //this.data.attributes.systemManaged = undefined;
2790 }
2791
2792 chalkPrint(pad = true) {
2793 let id = String("T-" + this.remote + "-" + this.id);
2794 if (pad) id = id.padStart(10);
2795 let prefix = this.curated ? "blue +" : "red -";
2796 return chalk`{green ${id}}: {${prefix}${this.name}}`;
2797 }
2798
2799 static async create(env, name, {
2800 notCurated
2801 } = {}) {
2802 return new Tag({
2803 data: await lib.makeAPIRequest({
2804 env,
2805 path: `/${this.endpoint}`,
2806 method: "POST",
2807 payload: {
2808 data: {
2809 attributes: {
2810 name,
2811 curated: notCurated ? false : true
2812 },
2813 type: "tagNames"
2814 }
2815 }
2816 }),
2817 remote: env
2818 });
2819 }
2820
2821}
2822
2823defineAssoc(Tag, "id", "data.id");
2824defineAssoc(Tag, "attributes", "data.attributes");
2825defineAssoc(Tag, "relationships", "data.relationships");
2826defineAssoc(Tag, "name", "data.attributes.name");
2827defineAssoc(Tag, "curated", "data.attributes.curated");
2828defineAssoc(Tag, "remote", "meta.remote");
2829Tag.endpoint = "tagNames";
2830
2831async function findLineInFile(renderedPreset, lineNumber) {
2832 let trueFileLine = lineNumber;
2833 let linedRenderedPreset = renderedPreset.split("\n").slice(2, -2);
2834 renderedPreset = renderedPreset.split("\n").slice(2, -2).join("\n");
2835 let includeLocation = renderedPreset.split("\n").filter(x => x.includes("@include"));
2836 let endIncludeNumber = -1,
2837 addTabDepth = 2;
2838 let lineBeforeIncludeStatement = '';
2839 let withinInclude = true;
2840
2841 if (lineNumber > linedRenderedPreset.indexOf(includeLocation[includeLocation.length - 1])) {
2842 addTabDepth = 0;
2843 withinInclude = false;
2844 }
2845
2846 for (let index = includeLocation.length - 1; index >= 0; index--) {
2847 let currIncludeIndex = linedRenderedPreset.indexOf(includeLocation[index]);
2848 let tabDepth = includeLocation[index].split(" ").length;
2849
2850 if (lineNumber > currIncludeIndex) {
2851 if (includeLocation[index].split(" ").filter(Boolean)[1] != "ERROR:") {
2852 if (lineBeforeIncludeStatement.split(" ").length == tabDepth && withinInclude) {
2853 trueFileLine = trueFileLine - currIncludeIndex;
2854 break;
2855 } else if (lineBeforeIncludeStatement.split(" ").length + addTabDepth == tabDepth && endIncludeNumber == -1) {
2856 endIncludeNumber = currIncludeIndex;
2857 } else if (lineBeforeIncludeStatement.split(" ").length + addTabDepth == tabDepth) {
2858 trueFileLine = trueFileLine - (endIncludeNumber - currIncludeIndex);
2859 endIncludeNumber = -1;
2860 }
2861 }
2862 } else {
2863 lineBeforeIncludeStatement = includeLocation[index];
2864 }
2865 }
2866
2867 let funcLine = "";
2868
2869 for (let line of linedRenderedPreset.slice(0, lineNumber).reverse()) {
2870 let match = /def (\w+)/.exec(line);
2871
2872 if (match) {
2873 funcLine = match[1];
2874 break;
2875 }
2876 }
2877
2878 let includeFilename;
2879
2880 if (lineBeforeIncludeStatement != "") {
2881 includeFilename = lineBeforeIncludeStatement.slice(1).trim().slice(14, -1);
2882 } else {
2883 includeFilename = null;
2884 }
2885
2886 if (includeLocation.length !== 0) {
2887 trueFileLine -= 1;
2888 lineNumber -= 1;
2889 }
2890
2891 return {
2892 lineNumber: trueFileLine,
2893 includeFilename,
2894 line: linedRenderedPreset[lineNumber],
2895 funcLine
2896 };
2897}
2898function printOutLine(eLine) {
2899 return log(chalk`{blue ${eLine.includeFilename || "Main"}}:{green ${eLine.lineNumber}} in ${eLine.funcLine}
2900${eLine.line}`);
2901}
2902async function getInfo(env, jobid) {
2903 log(env, jobid);
2904 let trace = lib.makeAPIRequest({
2905 env,
2906 path: `/jobs/${jobid}/artifacts/trace`
2907 }).catch(x => null);
2908 let renderedPreset = lib.makeAPIRequest({
2909 env,
2910 path: `/jobs/${jobid}/artifacts/preset`
2911 }).catch(x => null);
2912 let result = lib.makeAPIRequest({
2913 env,
2914 path: `/jobs/${jobid}/artifacts/result`
2915 }).catch(x => null);
2916 let error = lib.makeAPIRequest({
2917 env,
2918 path: `/jobs/${jobid}/artifacts/error`
2919 }).catch(x => null);
2920 let output = lib.makeAPIRequest({
2921 env,
2922 path: `/jobs/${jobid}/artifacts/output`
2923 }).catch(x => null);
2924 [trace, renderedPreset, result, output, error] = await Promise.all([trace, renderedPreset, result, output, error]);
2925 return {
2926 trace,
2927 renderedPreset,
2928 result,
2929 output,
2930 error
2931 };
2932}
2933async function parseTrace(env, jobid) {
2934 let {
2935 trace,
2936 renderedPreset
2937 } = await getInfo(env, jobid);
2938 let lineNumber = -1;
2939 let errorLines = [];
2940 let shouldBreak = 0;
2941
2942 for (let tr of trace.split("\n\n").reverse()) {
2943 errorLines.push(tr);
2944 shouldBreak--;
2945 if (tr.includes("Exception")) shouldBreak = 1;
2946 if (tr.includes("raised")) shouldBreak = 1;
2947 if (!shouldBreak) break;
2948 }
2949
2950 let errorList = [];
2951
2952 for (let errLine of errorLines) {
2953 lineNumber = /^[\w ]+:(\d+):/g.exec(errLine);
2954
2955 if (lineNumber && lineNumber[1]) {
2956 errorList.push((await findLineInFile(renderedPreset, lineNumber[1])));
2957 } else {
2958 errorList.push(errLine);
2959 }
2960 }
2961
2962 return errorList;
2963}
2964const Trace = {
2965 parseTrace,
2966 printOutLine,
2967 getInfo,
2968 findLineInFile
2969};
2970
2971require("source-map-support").install();
2972const rallyFunctions = {
2973 async bestPagintation() {
2974 global.silentAPI = true;
2975
2976 for (let i = 10; i <= 30; i += 5) {
2977 console.time("test with " + i);
2978 let dl = await lib.indexPathFast("DEV", `/workflowRules?page=1p${i}`);
2979 console.timeEnd("test with " + i);
2980 }
2981 },
2982
2983 async uploadPresets(env, presets, createFunc = () => false) {
2984 for (let preset of presets) {
2985 await preset.uploadCodeToEnv(env, createFunc);
2986 }
2987 },
2988
2989 //Dummy test access
2990 async testAccess(env) {
2991 if (lib.isLocalEnv(env)) {
2992 //TODO
2993 return true;
2994 }
2995
2996 let result = await lib.makeAPIRequest({
2997 env,
2998 path: "/providers?page=1p1",
2999 fullResponse: true,
3000 timeout: 1000
3001 });
3002 return result.statusCode;
3003 }
3004
3005};
3006
3007var allIndexBundle = /*#__PURE__*/Object.freeze({
3008 __proto__: null,
3009 rallyFunctions: rallyFunctions,
3010 SupplyChain: SupplyChain,
3011 Preset: Preset,
3012 Rule: Rule,
3013 Provider: Provider,
3014 Notification: Notification,
3015 Asset: Asset,
3016 User: User,
3017 Tag: Tag,
3018 Trace: Trace,
3019 get configFile () { return configFile; },
3020 loadConfig: loadConfig,
3021 loadConfigFromArgs: loadConfigFromArgs,
3022 setConfig: setConfig,
3023 get configObject () { return configObject; },
3024 lib: lib,
3025 AbortError: AbortError,
3026 APIError: APIError,
3027 UnconfiguredEnvError: UnconfiguredEnvError,
3028 ProtectedEnvError: ProtectedEnvError,
3029 FileTooLargeError: FileTooLargeError,
3030 Collection: Collection,
3031 RallyBase: RallyBase,
3032 sleep: sleep
3033});
3034
3035var version = "2.4.1";
3036
3037var baseCode = {
3038 SdviContentMover: `{
3039 "tasks": [
3040 {
3041 "operation": "copy" | "move" | "delete",
3042
3043 "source": {
3044 "optional": true | false,
3045
3046 # must specify either inventory OR externalStorage
3047 "inventory": {
3048 "labels": ["<label>" | "*", ],
3049 "tags": ["<tag>", ],
3050 "storageSet": ["<storage location name>" | "*", ], # only valid for move & delete tasks
3051 "expandCollections": true | false
3052 },
3053 "externalStorage": {
3054 "uri": "<protocol>://<host>/<path>/<file>",
3055 "credentials": {
3056 "key": "<parameter store key>",
3057 "roleArn": "<role to assume to access the parameter store>",
3058 "roleId": "<external ID to be used in role assumption>",
3059 "region": "<AWS region of the parameter store>"
3060 }
3061 }
3062 },
3063
3064 "destination": {
3065 "name": "<path within the storage location>/<filename>",
3066 "overwrite": "never" | "notInAnyAsset" | "notInOtherAsset" | "always",
3067 "storageMetadata": {"<key>": "<value>",...} | "<sourceStorageMetadata>",
3068
3069 # must specify either inventory OR externalStorage
3070 "inventory": {
3071 "storage": "<storage location name>",
3072 "newLabel": "<fileLabel>",
3073 "newTags": ["<tag>", "<tag>"],
3074 },
3075 "externalStorage": {
3076 "uri": "<protocol>://<host>",
3077 "credentials": {
3078 "key": "<parameter store key>",
3079 "roleArn": "<role to assume to access the parameter store>",
3080 "roleId": "<external ID to be used in role assumption>",
3081 "region": "<AWS region of the parameter store>"
3082 }
3083 }
3084 }
3085 },
3086
3087 {
3088 <another task>
3089 },
3090
3091 {
3092 <another task>
3093 },
3094
3095 ...
3096
3097 ]
3098}`,
3099 SdviEvaluate: `'''
3100name: {name}
3101'''
3102
3103# code here`,
3104 SdviEvalPro: `'''
3105name: {name}
3106'''
3107
3108import rally
3109
3110def evalMain(context):
3111 # code here`
3112};
3113
3114var _dec, _dec2, _dec3, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _dec10, _dec11, _dec12, _dec13, _dec14, _dec15, _dec16, _dec17, _dec18, _dec19, _dec20, _dec21, _dec22, _dec23, _dec24, _dec25, _dec26, _dec27, _dec28, _dec29, _dec30, _dec31, _dec32, _dec33, _dec34, _dec35, _dec36, _dec37, _dec38, _dec39, _dec40, _dec41, _dec42, _dec43, _dec44, _dec45, _dec46, _dec47, _dec48, _dec49, _dec50, _dec51, _dec52, _dec53, _dec54, _dec55, _dec56, _dec57, _obj;
3115
3116require("source-map-support").install();
3117let argv = argparse(process.argv.slice(2), {
3118 string: ["file", "env"],
3119 //boolean: ["no-protect"],
3120 boolean: ["anon"],
3121 default: {
3122 protect: true
3123 },
3124 alias: {
3125 f: "file",
3126 e: "env"
3127 }
3128}); //help menu helper
3129
3130function printHelp(help, short) {
3131 let helpText = chalk`
3132{white ${help.name}}: ${help.text}
3133 Usage: ${help.usage || "<unknown>"}
3134`; //Trim newlines
3135
3136 helpText = helpText.substring(1, helpText.length - 1);
3137
3138 if (!short) {
3139 for (let param of help.params || []) {
3140 helpText += chalk`\n {blue ${param.param}}: ${param.desc}`;
3141 }
3142
3143 for (let arg of help.args || []) {
3144 helpText += chalk`\n {blue ${arg.short}}, {blue ${arg.long}}: ${arg.desc}`;
3145 }
3146 }
3147
3148 return helpText;
3149}
3150
3151async function getFilesFromArgs(args) {
3152 let lastArg = args._.shift();
3153
3154 if (args.file) {
3155 let files = args.file;
3156 if (typeof files === "string") files = [files];
3157 return files;
3158 }
3159
3160 if (lastArg == "-") {
3161 log("Reading from stdin");
3162
3163 let getStdin = require("get-stdin");
3164
3165 let stdin = await getStdin();
3166 let files = stdin.split("\n");
3167 if (files[files.length - 1] === "") files.pop();
3168 return files;
3169 } else {
3170 args._.push(lastArg);
3171 }
3172}
3173
3174let presetsub = {
3175 async before(args) {
3176 this.env = args.env;
3177 if (!this.env) throw new AbortError("No env supplied");
3178 this.files = await getFilesFromArgs(args);
3179 },
3180
3181 async $grab(args) {
3182 if (!this.files) {
3183 throw new AbortError("No files provided to grab (use --file argument)");
3184 }
3185
3186 log(chalk`Grabbing {green ${this.files.length}} preset(s) metadata from {green ${this.env}}.`);
3187 let presets = this.files.map(path => new Preset({
3188 path,
3189 remote: false
3190 }));
3191
3192 for (let preset of presets) {
3193 //TODO small refactor
3194 await preset.grabMetadata(this.env);
3195 await preset.saveLocalMetadata();
3196
3197 if (args.full) {
3198 let remo = await Preset.getByName(this.env, preset.name);
3199 await remo.resolve();
3200 await remo.downloadCode();
3201 await remo.saveLocalFile();
3202 }
3203 }
3204 },
3205
3206 async $create(args) {
3207 let provider, name, ext;
3208
3209 if (args.provider) {
3210 provider = {
3211 name: args.provider
3212 };
3213 ext = args.ext;
3214 } else {
3215 provider = await selectProvider((await Provider.getAll(this.env)));
3216 ext = (await provider.getEditorConfig()).fileExt;
3217 }
3218
3219 if (args.name) {
3220 name = args.name;
3221 } else {
3222 name = await askInput("Preset Name", "What is the preset name?");
3223 }
3224
3225 let preset = new Preset({
3226 subProject: configObject.project
3227 });
3228 preset.providerType = {
3229 name: provider.name
3230 };
3231 preset.isGeneric = true;
3232 preset.name = name;
3233 preset.ext = ext;
3234
3235 if (baseCode[provider.name]) {
3236 preset._code = baseCode[provider.name].replace("{name}", name);
3237 } else {
3238 preset._code = " ";
3239 }
3240
3241 preset.saveLocalMetadata();
3242 if (!args["only-metadata"]) preset.saveLocalFile();
3243 },
3244
3245 async $list(args) {
3246 log("Loading...");
3247 let presets = await Preset.getAll(this.env);
3248
3249 if (args.resolve) {
3250 Provider.getAll(this.env);
3251
3252 for (let preset of presets) {
3253 let resolve = await preset.resolve(this.env);
3254
3255 if (args.attach) {
3256 let {
3257 proType
3258 } = resolve;
3259 proType.editorConfig.helpText = "";
3260 preset.meta = { ...preset.meta,
3261 proType
3262 };
3263 }
3264 }
3265 }
3266
3267 if (configObject.rawOutput) return presets;
3268 log(chalk`{yellow ${presets.length}} presets on {green ${this.env}}.`);
3269 presets.arr.sort((a, b) => {
3270 return Number(a.attributes.updatedAt) - Number(b.attributes.updatedAt);
3271 });
3272
3273 for (let preset of presets) {
3274 log(preset.chalkPrint());
3275 }
3276 },
3277
3278 async $upload(args) {
3279 if (!this.files) {
3280 throw new AbortError("No files provided to upload (use --file argument)");
3281 }
3282
3283 log(chalk`Uploading {green ${this.files.length}} preset(s) to {green ${this.env}}.`);
3284 let presets = this.files.map(path => new Preset({
3285 path,
3286 remote: false
3287 }));
3288 await rallyFunctions.uploadPresets(this.env, presets);
3289 },
3290
3291 async $deleteRemote(args) {
3292 let file = this.files[0];
3293
3294 if (!this.files) {
3295 throw new AbortError("No files provided to diff (use --file argument)");
3296 }
3297
3298 let preset = new Preset({
3299 path: file,
3300 remote: false
3301 });
3302
3303 if (!preset.name) {
3304 throw new AbortError(chalk`No preset header found. Cannot get name.`);
3305 }
3306
3307 let preset2 = await Preset.getByName(this.env, preset.name);
3308
3309 if (!preset2) {
3310 throw new AbortError(chalk`No preset found with name {red ${preset.name}} on {blue ${this.env}}`);
3311 }
3312
3313 log(chalk`Deleting ${preset2.chalkPrint(true)}.`);
3314 log((await preset2.delete()));
3315 },
3316
3317 async $diff(args) {
3318 let file = this.files[0];
3319
3320 if (!this.files) {
3321 throw new AbortError("No files provided to diff (use --file argument)");
3322 }
3323
3324 let preset = new Preset({
3325 path: file,
3326 remote: false
3327 });
3328
3329 if (!preset.name) {
3330 throw new AbortError(chalk`No preset header found. Cannot get name.`);
3331 }
3332
3333 let preset2 = await Preset.getByName(this.env, preset.name);
3334
3335 if (!preset2) {
3336 throw new AbortError(chalk`No preset found with name {red ${preset.name}} on {blue ${this.env}}`);
3337 }
3338
3339 await preset2.downloadCode();
3340
3341 let tempfile = require("tempy").file;
3342
3343 let temp = tempfile({
3344 extension: `${this.env}.${preset.ext}`
3345 });
3346 fs.writeFileSync(temp, preset2.code);
3347 let ptr = `${file},${temp}`; //raw output returns "file1" "file2"
3348
3349 if (configObject.rawOutput) {
3350 if (args["only-new"]) return temp;else return ptr;
3351 } //standard diff
3352
3353
3354 argv.command = argv.command || "diff";
3355 await spawn(argv.command, [file, temp], {
3356 stdio: "inherit"
3357 });
3358 },
3359
3360 async $info(args) {
3361 if (!this.files) {
3362 throw new AbortError("No files provided to diff (use --file argument)");
3363 }
3364
3365 let file = this.files[0];
3366 let preset = new Preset({
3367 path: file,
3368 remote: false
3369 });
3370
3371 if (!preset.name) {
3372 throw new AbortError(chalk`No preset header found. Cannot get name.`);
3373 }
3374
3375 await preset.getInfo(args.env);
3376 },
3377
3378 async unknown(arg, args) {
3379 log(chalk`Unknown action {red ${arg}} try '{white rally help preset}'`);
3380 }
3381
3382};
3383let rulesub = {
3384 async before(args) {
3385 this.env = args.env;
3386 if (!this.env) throw new AbortError("No env supplied");
3387 },
3388
3389 async $list(args) {
3390 log("Loading...");
3391 let rules = await Rule.getAll(this.env);
3392 if (configObject.rawOutput) return rules;
3393 log(chalk`{yellow ${rules.length}} rules on {green ${this.env}}.`);
3394 rules.arr.sort((a, b) => {
3395 return Number(a.data.attributes.updatedAt) - Number(b.data.attributes.updatedAt);
3396 });
3397
3398 for (let rule of rules) log(rule.chalkPrint());
3399 },
3400
3401 async $create(args) {
3402 let preset = await selectPreset({
3403 canSelectNone: false
3404 });
3405 let passNext = await selectRule({
3406 purpose: "'On Exit OK'"
3407 });
3408 let errorNext = await selectRule({
3409 purpose: "'On Exit Error'"
3410 });
3411 let name = await askInput("Rule Name", "What is the rule name?");
3412 name = name.replace("@", preset.name);
3413 let desc = await askInput("Description", "Enter a description.");
3414 let dynamicNexts = [];
3415 let next;
3416
3417 while (next = await selectRule({
3418 purpose: "dynamic next"
3419 })) {
3420 let name = await askInput("Key", "Key name for dynamic next");
3421 dynamicNexts.push({
3422 meta: {
3423 transition: name
3424 },
3425 type: "workflowRules",
3426 name: next.name
3427 });
3428 }
3429
3430 let rule = new Rule({
3431 subProject: configObject.project
3432 });
3433 rule.name = name;
3434 rule.description = desc;
3435 rule.relationships.preset = {
3436 data: {
3437 name: preset.name,
3438 type: "presets"
3439 }
3440 };
3441 if (errorNext) rule.relationships.errorNext = {
3442 data: {
3443 name: errorNext.name,
3444 type: "workflowRules"
3445 }
3446 };
3447 if (passNext) rule.relationships.passNext = {
3448 data: {
3449 name: passNext.name,
3450 type: "workflowRules"
3451 }
3452 };
3453
3454 if (dynamicNexts[0]) {
3455 rule.relationships.dynamicNexts = {
3456 data: dynamicNexts
3457 };
3458 }
3459
3460 rule.saveB();
3461 },
3462
3463 async unknown(arg, args) {
3464 log(chalk`Unknown action {red ${arg}} try '{white rally help rule}'`);
3465 }
3466
3467};
3468let jupytersub = {
3469 async before(args) {
3470 this.input = args._.shift() || "main.ipynb";
3471 this.output = args._.shift() || "main.py";
3472 },
3473
3474 async $build(args) {
3475 let cmd = `jupyter nbconvert --to python ${this.input} --TagRemovePreprocessor.remove_cell_tags={\"remove_cell\"} --output ${this.output} --TemplateExporter.exclude_markdown=True --TemplateExporter.exclude_input_prompt=True --TemplateExporter.exclude_output_prompt=True`.split(" ");
3476 log(chalk`Compiling GCR file {green ${this.input}} into {green ${this.output}} using jupyter...`);
3477
3478 try {
3479 let {
3480 timestr
3481 } = await spawn(cmd[0], cmd.slice(1));
3482 log(chalk`Complete in ~{green.bold ${timestr}}.`);
3483 } catch (e) {
3484 if (e.code !== "ENOENT") throw e;
3485 log(chalk`Cannot run the build command. Make sure that you have jupyter notebook installed.\n{green pip install jupyter}`);
3486 return;
3487 }
3488 }
3489
3490};
3491
3492async function categorizeString(str, defaultSubproject = undefined) {
3493 str = str.trim();
3494
3495 if (str.startsWith('"')) {
3496 str = str.slice(1, -1);
3497 }
3498
3499 if (match = /^(\w)-(\w{1,10})-(\d{1,10}):/.exec(str)) {
3500 if (match[1] === "P") {
3501 let ret = await Preset.getById(match[2], match[3]); //TODO modify for subproject a bit
3502
3503 return ret;
3504 } else if (match[1] === "R") {
3505 return await Rule.getById(match[2], match[3]);
3506 } else {
3507 return null;
3508 }
3509 } else if (match = /^([\w \/\\\-_]*)[\/\\]?silo\-(\w+)[\/\\]/.exec(str)) {
3510 try {
3511 switch (match[2]) {
3512 case "presets":
3513 return new Preset({
3514 path: str,
3515 subProject: match[1]
3516 });
3517
3518 case "rules":
3519 return new Rule({
3520 path: str,
3521 subProject: match[1]
3522 });
3523
3524 case "metadata":
3525 return await Preset.fromMetadata(str, match[1]);
3526 }
3527 } catch (e) {
3528 log(e);
3529 }
3530 } else {
3531 return null;
3532 }
3533}
3534
3535let tagsub = {
3536 async before(args) {
3537 this.env = args.env;
3538 if (!this.env) throw new AbortError("No env supplied");
3539 },
3540
3541 async $list(args) {
3542 log("Loading...");
3543 let tags = await Tag.getAll(this.env);
3544 if (configObject.rawOutput) return tags;
3545 log(chalk`{yellow ${tags.length}} tags on {green ${this.env}}.`);
3546 tags.arr.sort((a, b) => {
3547 return Number(a.data.attributes.updatedAt) - Number(b.data.attributes.updatedAt);
3548 });
3549
3550 for (let tag of tags) log(tag.chalkPrint());
3551 },
3552
3553 async $create(args) {
3554 return Tag.create(this.env, args._.shift());
3555 }
3556
3557};
3558let supplysub = {
3559 async before(args) {
3560 this.env = args.env;
3561 if (!this.env) throw new AbortError("No env supplied");
3562 this.files = await getFilesFromArgs(args);
3563 },
3564
3565 //Calculate a supply chain based on a starting rule at the top of the stack
3566 async $calc(args) {
3567 let name = args._.shift();
3568
3569 let stopName = args._.shift();
3570
3571 if (!name) {
3572 throw new AbortError("No starting rule or @ supplied");
3573 }
3574
3575 if (name === "@") {
3576 log(chalk`Silo clone started`);
3577 this.chain = new SupplyChain();
3578 this.chain.remote = args.env;
3579 } else {
3580 let rules = await Rule.getAll(this.env);
3581 let stop, start;
3582 start = rules.findByNameContains(name);
3583 if (stopName) stop = rules.findByNameContains(stopName);
3584
3585 if (!start) {
3586 throw new AbortError(chalk`No starting rule found by name {blue ${name}}`);
3587 }
3588
3589 log(chalk`Analzying supply chain: ${start.chalkPrint(false)} - ${stop ? stop.chalkPrint(false) : "(open)"}`);
3590 this.chain = new SupplyChain(start, stop);
3591 }
3592
3593 await this.chain.calculate();
3594 return await this.postAction(args);
3595 },
3596
3597 async postAction(args) {
3598 //Now that we ahve a supply chain object, do something with it
3599 if (args["to"]) {
3600 this.chain.log();
3601
3602 if (this.chain.presets.arr[0]) {
3603 await this.chain.downloadPresetCode(this.chain.presets);
3604 log("Done");
3605 }
3606
3607 if (Array.isArray(args["to"])) {
3608 for (let to of args["to"]) {
3609 await this.chain.syncTo(to);
3610 }
3611 } else {
3612 await this.chain.syncTo(args["to"]);
3613 }
3614 } else if (args["delete"]) {
3615 if (Array.isArray(args["delete"])) {
3616 for (let to of args["delete"]) {
3617 await this.chain.deleteTo(to);
3618 }
3619 } else {
3620 await this.chain.deleteTo(args["delete"]);
3621 }
3622 } else if (args["diff"]) {
3623 //Very basic diff
3624 let env = args["diff"];
3625 await Promise.all(this.chain.presets.arr.map(obj => obj.downloadCode()));
3626 await Promise.all(this.chain.presets.arr.map(obj => obj.resolve()));
3627 let otherPresets = await Promise.all(this.chain.presets.arr.map(obj => Preset.getByName(env, obj.name)));
3628 otherPresets = new Collection(otherPresets.filter(x => x));
3629 await Promise.all(otherPresets.arr.map(obj => obj.downloadCode()));
3630 await Promise.all(otherPresets.arr.map(obj => obj.resolve()));
3631
3632 const printPresets = (preset, otherPreset) => {
3633 log(preset.chalkPrint(true));
3634
3635 if (otherPreset.name) {
3636 log(otherPreset.chalkPrint(true));
3637 } else {
3638 log(chalk`{red (None)}`);
3639 }
3640 };
3641
3642 for (let preset of this.chain.presets) {
3643 let otherPreset = otherPresets.arr.find(x => x.name === preset.name) || {};
3644 preset.code = preset.code.replace(/[\r\n ]/, "");
3645 otherPreset.code = (otherPreset.code || "").replace(/[\r\n ]/, "");
3646
3647 if (preset.code === otherPreset.code) {
3648 if (!args["ignore-same"]) {
3649 printPresets(preset, otherPreset);
3650 log("Code Same");
3651 }
3652 } else {
3653 printPresets(preset, otherPreset);
3654
3655 if (args["ignore-same"]) {
3656 log("-------");
3657 } else {
3658 log("Code Different");
3659 }
3660 }
3661 }
3662 } else {
3663 return await this.chain.log();
3664 }
3665 },
3666
3667 async $make(args) {
3668 let set = new Set();
3669 let hints = args.hint ? Array.isArray(args.hint) ? args.hint : [args.hint] : []; //TODO modify for better hinting, and add this elsewhere
3670
3671 for (let hint of hints) {
3672 if (hint === "presets-uat") {
3673 log("got hint");
3674 await Preset.getAll("UAT");
3675 }
3676 }
3677
3678 for (let file of this.files) {
3679 set.add((await categorizeString(file)));
3680 }
3681
3682 let files = [...set];
3683 files = files.filter(f => f && !f.missing);
3684 this.chain = new SupplyChain();
3685 this.chain.rules = new Collection(files.filter(f => f instanceof Rule));
3686 this.chain.presets = new Collection(files.filter(f => f instanceof Preset));
3687 this.chain.notifications = new Collection([]);
3688 return await this.postAction(args);
3689 },
3690
3691 async unknown(arg, args) {
3692 log(chalk`Unknown action {red ${arg}} try '{white rally help supply}'`);
3693 }
3694
3695};
3696
3697function subCommand(object) {
3698 object = {
3699 before() {},
3700
3701 after() {},
3702
3703 unknown() {},
3704
3705 ...object
3706 };
3707 return async function (args) {
3708 //Grab the next arg on the stack, find a function tied to it, and run
3709 let arg = args._.shift();
3710
3711 let key = "$" + arg;
3712 let ret;
3713
3714 if (object[key]) {
3715 await object.before(args);
3716 ret = await object[key](args);
3717 await object.after(args);
3718 } else {
3719 if (arg === undefined) arg = "(None)";
3720 object.unknown(arg, args);
3721 }
3722
3723 return ret;
3724 };
3725}
3726
3727let cli = (_dec = helpText(`Display the help menu`), _dec2 = usage(`rally help [subhelp]`), _dec3 = param("subhelp", "The name of the command to see help for"), _dec4 = helpText("Rally tools jupyter interface. Requires jupyter to be installed."), _dec5 = usage("rally jupyter build [in] [out]"), _dec6 = param("in/out", "input and output file for jupyter. By default main.ipyrb and main.py"), _dec7 = helpText(`Preset related actions`), _dec8 = usage(`rally preset [action] --env <enviornment> --file [file1] --file [file2] ...`), _dec9 = param("action", "The action to perform. Can be upload, diff, list, deleteRemote"), _dec10 = arg("-e", "--env", "The enviornment you wish to perform the action on"), _dec11 = arg("-f", "--file", "A file to act on"), _dec12 = arg("~", "--command", "If the action is diff, this is the command to run instead of diff"), _dec13 = helpText(`Rule related actions`), _dec14 = usage(`rally rule [action] --env [enviornment]`), _dec15 = param("action", "The action to perform. Only list is supported right now"), _dec16 = arg("-e", "--env", "The enviornment you wish to perform the action on"), _dec17 = helpText(`supply chain related actions`), _dec18 = usage(`rally supply [action] [identifier] --env [enviornment] [post actions]`), _dec19 = param("action", "The action to perform. Can be calc or make."), _dec20 = param("identifier", "If the action is calc, then this identifier should be the first rule in the chain. If this is make, then supply '-' to read from stdin"), _dec21 = param("post actions", "The action to perform on the created supply chain. See commands below"), _dec22 = arg("-e", "--env", "(calc only) environment to do the calculation on"), _dec23 = arg("~", "--diff", "(post action) Use as `--diff [env]`. List all files with differences on the given env."), _dec24 = arg("~", "--to", "(post action) Use as `--to [env]`. Upload all objects."), _dec25 = arg("~", "--delete", "(post action) Use as `--delete [env]`. The reverse of uploading. Only presets are supported right now."), _dec26 = helpText(`tags stuff`), _dec27 = usage(`rally tags [action]`), _dec28 = param("action", "The action to perform. Can be list or create."), _dec29 = arg("-e", "--env", "The enviornment you wish to perform the action on"), _dec30 = helpText(`print out some trace info`), _dec31 = usage(`rally trace -e [env] [jobid]`), _dec32 = param("jobid", "a job id like b86d7d90-f0a5-4622-8754-486ca8e9ecbd"), _dec33 = arg("-e", "--env", "The enviornment you wish to perform the action on"), _dec34 = helpText(`List all available providers, or find one by name/id`), _dec35 = usage(`rally providers [identifier] --env [env] --raw`), _dec36 = param("identifier", "Either the name or id of the provider"), _dec37 = arg("-e", "--env", "The enviornment you wish to perform the action on"), _dec38 = arg("~", "--raw", "Raw output of command. If [identifier] is given, then print editorConfig too"), _dec39 = helpText(`Change config for rally tools`), _dec40 = usage("rally config [key] --set [value] --raw"), _dec41 = param("key", chalk`Key you want to edit. For example, {green chalk} or {green api.DEV}`), _dec42 = arg("~", "--set", "If this value is given, no interactive prompt will launch and the config option will change."), _dec43 = arg("~", "--raw", "Raw output of json config"), _dec44 = helpText(`create/modify asset`), _dec45 = usage("rally asset [action] [action...]"), _dec46 = param("action", chalk`Options are create, delete, launch, addfile, metadata, show, patchMetadata, and launchEvalute. You can supply multiple actions to chain them`), _dec47 = arg(`-i`, `--id`, chalk`MOVIE_ID of asset to select`), _dec48 = arg(`-n`, `--name`, chalk`MOVIE_NAME of asset. with {white create}, '{white #}' will be replaced with a uuid. Default is '{white TEST_#}'`), _dec49 = arg(`~`, `--anon`, chalk`Supply this if no asset is needed (used to lauch anonymous workflows)`), _dec50 = arg(`-j`, `--job-name`, chalk`Job name to start (used with launch and launchEvalute)`), _dec51 = arg(`~`, `--init-data`, chalk`Init data to use when launching job. can be string, or {white @path/to/file} for a file`), _dec52 = arg(`~`, `--file-label`, chalk`File label (used with addfile)`), _dec53 = arg(`~`, `--file-uri`, chalk`File s3 uri. Can use multiple uri's for the same label (used with addfile)`), _dec54 = arg(`~`, `--metadata`, chalk`Metadata to use with patchMetadata. Can be string, or {white @path/to/file} for a file. Data must contain a top level key Metadata, or Workflow. Metadata will be pached into METADATA. Workflow will be patched into WORKFLOW_METADATA(not currently available)`), _dec55 = arg(`~`, `--priority`, chalk`set the priority of all launched jobs`), _dec56 = arg(`~`, `--new-name`, chalk`set the new name`), _dec57 = arg(`~`, `--target-env`, chalk`migrate to the env (when using migrate)`), (_obj = {
3728 async help(args) {
3729 let arg = args._.shift();
3730
3731 if (arg) {
3732 let help = helpEntries[arg];
3733
3734 if (!help) {
3735 log(chalk`No help found for '{red ${arg}}'`);
3736 } else {
3737 log(printHelp(helpEntries[arg]));
3738 }
3739 } else {
3740 for (let helpArg in helpEntries) {
3741 log(printHelp(helpEntries[helpArg], true));
3742 }
3743 }
3744 },
3745
3746 async jupyter(args) {
3747 return subCommand(jupytersub)(args);
3748 },
3749
3750 //@helpText(`Print input args, for debugging`)
3751 async printArgs(args) {
3752 log(args);
3753 },
3754
3755 async preset(args) {
3756 return subCommand(presetsub)(args);
3757 },
3758
3759 async rule(args) {
3760 return subCommand(rulesub)(args);
3761 },
3762
3763 async supply(args) {
3764 return subCommand(supplysub)(args);
3765 },
3766
3767 async tag(args) {
3768 return subCommand(tagsub)(args);
3769 },
3770
3771 async trace(args) {
3772 let jobId = args._.shift();
3773
3774 if (!jobId) throw new AbortError("No job id");
3775 if (!args.env) throw new AbortError("no env");
3776
3777 let ln = args._.shift();
3778
3779 if (!ln) {
3780 log("is trace");
3781 let traceInfo = await parseTrace(args.env, jobId);
3782
3783 for (let line of traceInfo) {
3784 if (typeof line == "string") {
3785 log(chalk.red(line));
3786 } else {
3787 printOutLine(line);
3788 }
3789 }
3790 } else {
3791 log("is ln");
3792 let {
3793 renderedPreset
3794 } = await getInfo(args.env, jobId);
3795 return findLineInFile(renderedPreset, Number(ln));
3796 }
3797 },
3798
3799 async providers(args) {
3800 let env = args.env;
3801 if (!env) return errorLog("No env supplied.");
3802
3803 let ident = args._.shift();
3804
3805 let providers = await Provider.getAll(env);
3806
3807 if (ident) {
3808 let pro = providers.arr.find(x => x.id == ident || x.name.includes(ident));
3809
3810 if (!pro) {
3811 log(chalk`Couldn't find provider by {green ${ident}}`);
3812 } else {
3813 log(pro.chalkPrint(false));
3814 let econfig = await pro.getEditorConfig();
3815
3816 if (args.raw) {
3817 return pro;
3818 } else {
3819 if (econfig.helpText.length > 100) {
3820 econfig.helpText = "<too long to display>";
3821 }
3822
3823 if (econfig.completions.length > 5) {
3824 econfig.completions = "<too long to display>";
3825 }
3826
3827 log(econfig);
3828 }
3829 }
3830 } else {
3831 if (args.raw) return providers;
3832
3833 for (let pro of providers) log(pro.chalkPrint());
3834 }
3835 },
3836
3837 async config(args) {
3838 let prop = args._.shift();
3839
3840 let propArray = prop && prop.split("."); //if(!await configHelpers.askQuestion(`Would you like to create a new config file in ${configFile}`)) return;
3841
3842 let newConfigObject;
3843
3844 if (!prop) {
3845 if (configObject.rawOutput) return configObject;
3846 log("Creating new config");
3847 newConfigObject = { ...configObject
3848 };
3849
3850 for (let helperName in configHelpers) {
3851 if (helperName.startsWith("$")) {
3852 newConfigObject = { ...newConfigObject,
3853 ...(await configHelpers[helperName](false))
3854 };
3855 }
3856 }
3857 } else {
3858 log(chalk`Editing option {green ${prop}}`);
3859
3860 if (args.set) {
3861 newConfigObject = { ...configObject,
3862 [prop]: args.set
3863 };
3864 } else {
3865 let ident = "$" + propArray[0];
3866
3867 if (configHelpers[ident]) {
3868 newConfigObject = { ...configObject,
3869 ...(await configHelpers[ident](propArray))
3870 };
3871 } else {
3872 log(chalk`No helper for {red ${ident}}`);
3873 return;
3874 }
3875 }
3876 }
3877
3878 newConfigObject.hasConfig = true; //Create readable json and make sure the user is ok with it
3879
3880 let newConfig = JSON.stringify(newConfigObject, null, 4);
3881 log(newConfig); //-y or --set will make this not prompt
3882
3883 if (!args.y && !args.set && !(await askQuestion("Write this config to disk?"))) return;
3884 fs.writeFileSync(configFile, newConfig, {
3885 mode: 0o600
3886 });
3887 log(chalk`Created file {green ${configFile}}.`);
3888 },
3889
3890 async asset(args) {
3891 function uuid(args) {
3892 const digits = 16;
3893 return String(Math.floor(Math.random() * Math.pow(10, digits))).padStart(digits, "0");
3894 }
3895
3896 let name = args.name || `TEST_#`;
3897 let env = args.env;
3898 let asset;
3899
3900 let arg = args._.shift();
3901
3902 if (!arg) {
3903 throw new AbortError(chalk`Missing arguments: see {white 'rally help asset'}`);
3904 }
3905
3906 if (args.anon) {
3907 args._.unshift(arg);
3908 } else if (arg == "create") {
3909 name = name.replace("#", uuid());
3910 asset = await Asset.createNew(name, env);
3911 } else {
3912 args._.unshift(arg);
3913
3914 if (args.id) {
3915 asset = Asset.lite(args.id, env);
3916 } else {
3917 asset = await Asset.getByName(env, args.name);
3918 }
3919 }
3920
3921 if (!asset && !args.anon) {
3922 throw new AbortError("No asset found/created");
3923 }
3924
3925 let launchArg = 0;
3926 let fileArg = 0;
3927
3928 let arrayify = (obj, i) => Array.isArray(obj) ? obj[i] : i == 0 ? obj : undefined;
3929
3930 while (arg = args._.shift()) {
3931 if (arg === "launch") {
3932 let initData = arrayify(args["init-data"], launchArg);
3933
3934 if (initData && initData.startsWith("@")) {
3935 log(chalk`Reading init data from {white ${initData.slice(1)}}`);
3936 initData = fs.readFileSync(initData.slice(1), "utf-8");
3937 }
3938
3939 let jobName = arrayify(args["job-name"], launchArg);
3940 let p = await Rule.getByName(env, jobName);
3941
3942 if (!p) {
3943 throw new AbortError(`Cannot launch job ${jobName}, does not exist (?)`);
3944 } else {
3945 log(chalk`Launching ${p.chalkPrint(false)} on ${asset ? asset.chalkPrint(false) : "(None)"}`);
3946 }
3947
3948 if (asset) {
3949 await asset.startWorkflow(jobName, {
3950 initData,
3951 priority: args.priority
3952 });
3953 } else {
3954 await Asset.startAnonWorkflow(env, jobName, {
3955 initData,
3956 priority: args.priority
3957 });
3958 }
3959
3960 launchArg++;
3961 } else if (arg === "launchEvaluate") {
3962 let initData = arrayify(args["init-data"], launchArg);
3963
3964 if (initData && initData.startsWith("@")) {
3965 log(chalk`Reading init data from {white ${initData.slice(1)}}`);
3966 initData = fs.readFileSync(initData.slice(1), "utf-8");
3967 }
3968
3969 let jobName = arrayify(args["job-name"], launchArg);
3970 let jobData;
3971 let ephemeralEval = false;
3972 let p;
3973
3974 if (jobName.startsWith("@")) {
3975 ephemeralEval = true;
3976 jobData = fs.readFileSync(jobName.slice(1));
3977 } else {
3978 p = await Preset.getByName(env, jobName);
3979
3980 if (!p) {
3981 throw new AbortError(`Cannot launch preset ${jobName}, does not exist (?)`);
3982 } else {
3983 log(chalk`Launching ${p.chalkPrint(false)} on ${asset ? asset.chalkPrint(false) : "(None)"}`);
3984 }
3985 }
3986
3987 if (ephemeralEval) {
3988 await Asset.startEphemeralEvaluateIdeal(env, p.id, initData);
3989 } else {
3990 await asset.startEvaluate(p.id, initData);
3991 }
3992
3993 launchArg++;
3994 } else if (arg === "addfile") {
3995 let label = arrayify(args["file-label"], fileArg);
3996 let uri = arrayify(args["file-uri"], fileArg);
3997
3998 if (label === undefined || !uri) {
3999 throw new AbortError("Number of file-label and file-uri does not match");
4000 }
4001
4002 await asset.addFile(label, uri);
4003 log(chalk`Added file ${label}`);
4004 fileArg++;
4005 } else if (arg === "delete") {
4006 await asset.delete();
4007 } else if (arg === "create") {
4008 throw new AbortError(`Cannot have more than 1 create/get per asset call`);
4009 } else if (arg === "show" || arg == "load") {
4010 if (asset.lite) asset = await Asset.getById(env, asset.id);
4011 if (arg == "show") log(asset);
4012 } else if (arg === "metadata" || arg === "md") {
4013 log((await asset.getMetadata()));
4014 } else if (arg === "migrate") {
4015 await asset.migrate(args["target-env"]);
4016 } else if (arg === "patchMetadata") {
4017 let initData = arrayify(args["metadata"], launchArg);
4018
4019 if (initData && initData.startsWith("@")) {
4020 log(chalk`Reading data from {white ${initData.slice(1)}}`);
4021 initData = fs.readFileSync(initData.slice(1), "utf-8");
4022 }
4023
4024 initData = JSON.parse(initData);
4025 await asset.patchMetadata(initData);
4026 } else if (arg === "rename") {
4027 let newName = args["new-name"];
4028 let oldName = asset.name;
4029 await asset.rename(newName);
4030 log(chalk`Rename: {green ${oldName}} -> {green ${newName}}`);
4031 }
4032 }
4033
4034 if (configObject.rawOutput && !configObject.script) return asset;
4035 },
4036
4037 async checkSegments(args) {
4038 let asset = args._.shift();
4039
4040 let res = await lib.makeAPIRequest({
4041 env: args.env,
4042 path: `/movies/${asset}/metadata/Metadata`
4043 });
4044 let segments = res.data.attributes.metadata.userMetaData.segments.segments;
4045 let r = segments.reduce((lastSegment, val, ind) => {
4046 let curSegment = val.startTime;
4047
4048 if (curSegment < lastSegment) {
4049 log("bad segment " + (ind + 1));
4050 }
4051
4052 return val.endTime;
4053 }, "00:00:00:00");
4054 },
4055
4056 async getJobs(args) {
4057 let req = await lib.indexPathFast({
4058 env: args.env,
4059 path: "/jobs",
4060 qs: {
4061 filter: "presetName=DCTC Add Element US Checkin"
4062 }
4063 });
4064 log(req.map(x => x.relationships.asset.data.id).join("\n"));
4065 },
4066
4067 async listAssets(args, tag) {
4068 let req = await lib.indexPathFast({
4069 env: args.env,
4070 path: "/assets",
4071 qs: {
4072 noRelationships: true,
4073 sort: "id"
4074 },
4075 chunksize: 30
4076 });
4077
4078 for (let asset of req) {
4079 log(asset.id);
4080 }
4081
4082 return req;
4083 },
4084
4085 async listSegments(args) {
4086 let f = JSON.parse(fs.readFileSync(args.file, "utf-8"));
4087
4088 for (let asset of f) {
4089 var _r$data$attributes$me, _r$data$attributes$me2;
4090
4091 let r = await lib.makeAPIRequest({
4092 env: args.env,
4093 path: `/movies/${asset.id}/metadata/Metadata`
4094 });
4095 let segs = (_r$data$attributes$me = r.data.attributes.metadata.userMetaData) === null || _r$data$attributes$me === void 0 ? void 0 : (_r$data$attributes$me2 = _r$data$attributes$me.segments) === null || _r$data$attributes$me2 === void 0 ? void 0 : _r$data$attributes$me2.segments;
4096
4097 if (segs && segs.length > 1) {
4098 log(asset.id);
4099 log(asset.name);
4100 }
4101 }
4102 },
4103
4104 async test4(args) {
4105 let things = await lib.indexPathFast({
4106 env: args.env,
4107 path: "/assets",
4108 qs: {
4109 filter: `createdBefore=${Date.now() - 1000 * 160 * 24 * 60 * 60},createdSince=${Date.now() - 1000 * 170 * 24 * 60 * 60}`
4110 }
4111 });
4112 log(JSON.stringify(things, null, 4));
4113 },
4114
4115 async test5(args) {
4116 let things = await lib.indexPathFast({
4117 env: args.env,
4118 path: "/jobs",
4119 qs: {
4120 filter: `state=Queued,presetName=E2 P4101 - DNE Compliance Edit - US Output Deal - Edit WorkOrder`
4121 }
4122 });
4123 log(JSON.stringify(things, null, 4));
4124 },
4125
4126 async test2(args) {
4127 let wfr = await lib.indexPath({
4128 env: args.env,
4129 path: "/workflowRuleMetadata"
4130 });
4131 log(wfr);
4132
4133 for (let wfrm of wfr) {
4134 try {
4135 wfrm.id = undefined;
4136 wfrm.links = undefined;
4137 log(wfrm);
4138 let req = await lib.makeAPIRequest({
4139 env: "DEV",
4140 path: "/workflowRuleMetadata",
4141 method: "POST",
4142 payload: {
4143 data: wfrm
4144 }
4145 });
4146 } catch (e) {
4147 log("caught");
4148 } //break;
4149
4150 }
4151 },
4152
4153 async test3(args) {
4154 let wfr = await lib.indexPath({
4155 env: args.env,
4156 path: "/workflowRuleMetadata"
4157 });
4158 log(wfr);
4159
4160 for (let wfrm of wfr) {
4161 try {
4162 wfrm.id = undefined;
4163 wfrm.links = undefined;
4164 log(wfrm);
4165 let req = await lib.makeAPIRequest({
4166 env: "DEV",
4167 path: "/workflowRuleMetadata",
4168 method: "POST",
4169 payload: {
4170 data: wfrm
4171 }
4172 });
4173 } catch (e) {
4174 log("caught");
4175 } //break;
4176
4177 }
4178 },
4179
4180 async deleteOmneons(args) {
4181 let id = args._.shift();
4182
4183 let asset;
4184
4185 if (Number(id)) {
4186 asset = await Asset.getById("PROD", Number(id));
4187 } else {
4188 asset = await Asset.getByName("PROD", id);
4189 }
4190
4191 log(asset);
4192 let f = await asset.getFiles();
4193
4194 for (let file of f) {
4195 if (file.label.includes("Omneon")) {
4196 log(`removing ${file.label}`);
4197 await file.delete();
4198 }
4199 }
4200 },
4201
4202 async audit(args) {
4203 let supportedAudits = ["presets", "rule", "other"];
4204 await addAutoCompletePrompt();
4205 let q = await inquirer.prompt([{
4206 type: "autocomplete",
4207 name: "obj",
4208 message: `What audit do you want?`,
4209 source: async (sofar, input) => {
4210 return supportedAudits.filter(x => input ? x.includes(input.toLowerCase()) : true);
4211 }
4212 }]);
4213 let choice = q.obj;
4214 let resourceId = undefined;
4215
4216 let filterFunc = _ => _;
4217
4218 if (choice === "presets") {
4219 let preset = await selectPreset({
4220 canSelectNone: false
4221 });
4222 let remote = await Preset.getByName(args.env, preset.name);
4223 if (!remote) throw new AbortError("Could not find this item on remote env");
4224
4225 filterFunc = ev => ev.resource == "Preset";
4226
4227 resourceId = remote.id;
4228 } else if (choice === "rule") {
4229 let preset = await selectRule({
4230 canSelectNone: false
4231 });
4232 let remote = await Rule.getByName(args.env, preset.name);
4233 if (!remote) throw new AbortError("Could not find this item on remote env");
4234
4235 filterFunc = ev => ev.resource == "Rule";
4236
4237 resourceId = remote.id;
4238 } else {
4239 resourceId = await askInput(null, "What resourceID?");
4240 }
4241
4242 log(chalk`Resource ID on {blue ${args.env}} is {yellow ${resourceId}}`);
4243 log(`Loading audits (this might take a while)`);
4244 const numRows = 100;
4245 let r = await lib.makeAPIRequest({
4246 env: args.env,
4247 path: `/v1.0/audit?perPage=${numRows}&count=${numRows}&filter=%7B%22resourceId%22%3A%22${resourceId}%22%7D&autoload=false&pageNum=1&include=`,
4248 timeout: 180000
4249 });
4250 r.data = r.data.filter(filterFunc);
4251 log("Data recieved, parsing users");
4252
4253 for (let event of r.data) {
4254 var _event$correlation;
4255
4256 let uid = event === null || event === void 0 ? void 0 : (_event$correlation = event.correlation) === null || _event$correlation === void 0 ? void 0 : _event$correlation.userId;
4257 if (!uid) continue;
4258
4259 try {
4260 event.user = await User.getById(args.env, uid);
4261 } catch (e) {
4262 event.user = {
4263 name: "????"
4264 };
4265 }
4266 }
4267
4268 if (args.raw) return r.data;
4269 let evCounter = 0;
4270
4271 for (let event of r.data) {
4272 var _event$user;
4273
4274 let evtime = moment(event.createdAt);
4275 let date = evtime.format("ddd YYYY/MM/DD hh:mm:ssa");
4276 let timedist = evtime.fromNow();
4277 log(chalk`${date} {yellow ${timedist}} {green ${(_event$user = event.user) === null || _event$user === void 0 ? void 0 : _event$user.name}} ${event.event}`);
4278 if (++evCounter >= 30) break;
4279 }
4280 },
4281
4282 async audit2(args) {
4283 const numRows = 1000;
4284 let r = await lib.makeAPIRequest({
4285 env: args.env,
4286 //path: `/v1.0/audit?perPage=${numRows}&count=${numRows}&autoload=false&pageNum=1&include=`,
4287 path: `/v1.0/audit?perPage=${numRows}&count=${numRows}&filter=%7B%22correlation.userId%22%3A%5B%22164%22%5D%7D&autoload=false&pageNum=1&include=`,
4288 timeout: 60000
4289 });
4290
4291 for (let event of r.data) {
4292 log(event.event);
4293 }
4294 },
4295
4296 async findIDs(args) {
4297 let files = await getFilesFromArgs(args);
4298
4299 for (let file of files) {
4300 let preset = await Preset.getByName(args.env, file);
4301 await preset.resolve();
4302 log(`silo-presets/${file}.${preset.ext}`);
4303 }
4304 },
4305
4306 async getAssets(env, name) {
4307 if (!this.callid) this.callid = 0;
4308 this.callid++;
4309 let callid = this.callid;
4310 await sleep(500);
4311 if (callid != this.callid) return this.lastResult || [];
4312 let req = await lib.makeAPIRequest({
4313 env,
4314 path: `/assets`,
4315 qs: name ? {
4316 filter: `nameContains=${name}`
4317 } : undefined
4318 });
4319 this.lastCall = Date.now();
4320 return this.lastResult = req.data;
4321 },
4322
4323 async content(args) {
4324 addAutoCompletePrompt();
4325 let q = await inquirer.prompt([{
4326 type: "autocomplete",
4327 name: "what",
4328 message: `What asset do you want?`,
4329 source: async (sofar, input) => {
4330 let assets = await this.getAssets(args.env, input);
4331 assets = assets.map(x => new Asset({
4332 data: x,
4333 remote: args.env
4334 }));
4335 return assets.map(x => ({
4336 name: x.chalkPrint(true) + ": " + x.data.links.self.replace("/api/v2/assets/", "/content/"),
4337 value: x
4338 }));
4339 }
4340 }]);
4341 },
4342
4343 async ["@"](args) {
4344 args._.unshift("-");
4345
4346 args._.unshift("make");
4347
4348 return this.supply(args);
4349 },
4350
4351 async test(args) {
4352 let asset = await Asset.getByName("UAT", args.name);
4353 log(asset);
4354 },
4355
4356 //Used to test startup and teardown speed.
4357 noop() {
4358 return true;
4359 }
4360
4361}, (_applyDecoratedDescriptor(_obj, "help", [_dec, _dec2, _dec3], Object.getOwnPropertyDescriptor(_obj, "help"), _obj), _applyDecoratedDescriptor(_obj, "jupyter", [_dec4, _dec5, _dec6], Object.getOwnPropertyDescriptor(_obj, "jupyter"), _obj), _applyDecoratedDescriptor(_obj, "preset", [_dec7, _dec8, _dec9, _dec10, _dec11, _dec12], Object.getOwnPropertyDescriptor(_obj, "preset"), _obj), _applyDecoratedDescriptor(_obj, "rule", [_dec13, _dec14, _dec15, _dec16], Object.getOwnPropertyDescriptor(_obj, "rule"), _obj), _applyDecoratedDescriptor(_obj, "supply", [_dec17, _dec18, _dec19, _dec20, _dec21, _dec22, _dec23, _dec24, _dec25], Object.getOwnPropertyDescriptor(_obj, "supply"), _obj), _applyDecoratedDescriptor(_obj, "tag", [_dec26, _dec27, _dec28, _dec29], Object.getOwnPropertyDescriptor(_obj, "tag"), _obj), _applyDecoratedDescriptor(_obj, "trace", [_dec30, _dec31, _dec32, _dec33], Object.getOwnPropertyDescriptor(_obj, "trace"), _obj), _applyDecoratedDescriptor(_obj, "providers", [_dec34, _dec35, _dec36, _dec37, _dec38], Object.getOwnPropertyDescriptor(_obj, "providers"), _obj), _applyDecoratedDescriptor(_obj, "config", [_dec39, _dec40, _dec41, _dec42, _dec43], Object.getOwnPropertyDescriptor(_obj, "config"), _obj), _applyDecoratedDescriptor(_obj, "asset", [_dec44, _dec45, _dec46, _dec47, _dec48, _dec49, _dec50, _dec51, _dec52, _dec53, _dec54, _dec55, _dec56, _dec57], Object.getOwnPropertyDescriptor(_obj, "asset"), _obj)), _obj));
4362
4363async function unknownCommand(cmd) {
4364 log(chalk`Unknown command {red ${cmd}}.`);
4365}
4366
4367async function noCommand() {
4368 write(chalk`
4369Rally Tools {yellow v${version} (alpha)} CLI
4370by John Schmidt <John_Schmidt@discovery.com>
4371`); //Prompt users to setup one time config.
4372
4373 if (!configObject.hasConfig) {
4374 write(chalk`
4375It looks like you haven't setup the config yet. Please run '{green rally config}'.
4376`);
4377 return;
4378 }
4379
4380 let envs = new Set(["LOCAL", "UAT", "DEV", "PROD", "QA", ...Object.keys(configObject.api)]);
4381 let proms = [];
4382
4383 for (let env of envs) {
4384 proms.push({
4385 env,
4386 prom: rallyFunctions.testAccess(env)
4387 });
4388 } //API Access tests
4389
4390
4391 for (let {
4392 env,
4393 prom
4394 } of proms) {
4395 //Test access. Returns HTTP response code
4396 let resultStr;
4397
4398 try {
4399 let result = await prom; //Create a colored display and response
4400
4401 resultStr = chalk`{yellow ${result} <unknown>}`;
4402 if (result === 200) resultStr = chalk`{green 200 OK}`;else if (result === 401) resultStr = chalk`{red 401 No Access}`;else if (result >= 500) resultStr = chalk`{yellow ${result} API Down?}`;else if (result === true) resultStr = chalk`{green OK}`;else if (result === false) resultStr = chalk`{red BAD}`;
4403 } catch (e) {
4404 if (e instanceof UnconfiguredEnvError) {
4405 resultStr = chalk`{yellow Unconfigured}`;
4406 } else if (e instanceof APIError) {
4407 if (!e.response.body) {
4408 resultStr = chalk`{red Timeout (?)}`;
4409 }
4410 } else if (e.name == "RequestError") {
4411 resultStr = chalk`{red Low level error (check internet): ${e.error.errno}}`;
4412 } else {
4413 throw e;
4414 }
4415 }
4416
4417 log(chalk` ${env}: ${resultStr}`);
4418 }
4419}
4420
4421async function $main() {
4422 //Supply --config to load a different config file
4423 if (typeof argv.config === "string") {
4424 loadConfig(argv.config);
4425 } else if (typeof argv.config === "object") {
4426 loadConfigFromArgs(argv);
4427 } else {
4428 loadConfig();
4429 } // First we need to decide if the user wants color or not. If they do want
4430 // color, we need to make sure we use the right mode
4431
4432
4433 chalk.enabled = configObject.hasConfig ? configObject.chalk : true;
4434
4435 if (chalk.level === 0 || !chalk.enabled) {
4436 let force = argv["force-color"];
4437
4438 if (force) {
4439 chalk.enabled = true;
4440
4441 if (force === true && chalk.level === 0) {
4442 chalk.level = 1;
4443 } else if (Number(force)) {
4444 chalk.level = Number(force);
4445 }
4446 }
4447 } //This flag being true allows you to modify UAT and PROD
4448
4449
4450 if (!argv["protect"]) {
4451 configObject.dangerModify = true;
4452 } //This enables raw output for some functions
4453
4454
4455 if (argv["raw"]) {
4456 configObject.rawOutput = true;
4457
4458 global.log = () => {};
4459
4460 global.errorLog = () => {};
4461
4462 global.write = () => {};
4463 }
4464
4465 if (argv["script"]) {
4466 configObject.script = true;
4467 }
4468
4469 if (argv["ignore-missing"]) {
4470 configObject.ignoreMissing = true;
4471 }
4472
4473 if (argv["update-immutable"]) {
4474 configObject.updateImmutable = true;
4475 }
4476
4477 if (argv["skip-header"]) {
4478 configObject.skipHeader = true;
4479 }
4480
4481 configObject.globalProgress = !argv["hide-progress"]; //Default enviornment should normally be from config, but it can be
4482 //overridden by the -e/--env flag
4483
4484 if (configObject.defaultEnv) {
4485 argv.env = argv.env || configObject.defaultEnv;
4486 } //Enable verbose logging in some places.
4487
4488
4489 if (argv["vverbose"]) {
4490 configObject.verbose = argv["vverbose"];
4491 configObject.vverbose = true;
4492 } else if (argv["verbose"]) {
4493 configObject.verbose = argv["verbose"];
4494 } else if (argv["vvverbose"]) {
4495 configObject.verbose = true;
4496 configObject.vverbose = true;
4497 configObject.vvverbose = true;
4498 } //copy argument array to new object to allow modification
4499
4500
4501 argv._old = argv._.slice(); //Take first argument after `node bundle.js`
4502 //If there is no argument, display the default version info and API access.
4503
4504 let func = argv._.shift();
4505
4506 if (func) {
4507 if (!cli[func]) return await unknownCommand(func);
4508
4509 try {
4510 //Call the cli function
4511 let ret = await cli[func](argv);
4512
4513 if (ret) {
4514 write(chalk.white("CLI returned: "));
4515 if (ret instanceof Collection) ret = ret.arr; //Directly use console.log so that --raw works as intended.
4516
4517 if (typeof ret === "object") {
4518 console.log(JSON.stringify(ret, null, 4));
4519 } else {
4520 console.log(ret);
4521 }
4522 }
4523 } catch (e) {
4524 if (e instanceof AbortError) {
4525 log(chalk`{red CLI Aborted}: ${e.message}`);
4526 process.exit(1);
4527 } else {
4528 throw e;
4529 }
4530 }
4531 } else {
4532 await noCommand();
4533 }
4534
4535 process.exit(0);
4536}
4537
4538async function main(...args) {
4539 //Catch all for errors to avoid ugly default node promise catcher
4540 try {
4541 await $main(...args);
4542 } catch (e) {
4543 errorLog(e.stack);
4544 process.exit(1);
4545 }
4546} // If this is an imported module, then we should exec the cli interface.
4547// Oterwise just export everything.
4548
4549
4550if (require.main === module) {
4551 main();
4552} else {
4553 loadConfig();
4554 module.exports = allIndexBundle;
4555}
4556//# sourceMappingURL=bundle.js.map