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) {
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 providerData: Buffer.from(preset.code, "binary").toString("base64"),
1437 dynamicPresetData
1438 },
1439 type: "jobs",
1440 relationships: {
1441 movie: {
1442 data: {
1443 id: this.id,
1444 type: "movies"
1445 }
1446 }
1447 }
1448 }
1449 }
1450 });
1451 write(" Waiting for finish...");
1452
1453 for (;;) {
1454 res = await lib.makeAPIRequest({
1455 env,
1456 path_full: evalInfo.data.links.self
1457 });
1458 write(".");
1459
1460 if (res.data.attributes.state == "Complete") {
1461 write(chalk`{green Done}...\n`);
1462 break;
1463 }
1464
1465 await sleep(300);
1466 }
1467
1468 return;
1469 }
1470
1471 async startEvaluate(presetid, dynamicPresetData) {
1472 // Fire and forget.
1473 let data = await lib.makeAPIRequest({
1474 env: this.remote,
1475 path: "/jobs",
1476 method: "POST",
1477 payload: {
1478 data: {
1479 type: "jobs",
1480 attributes: {
1481 dynamicPresetData
1482 },
1483 relationships: {
1484 movie: {
1485 data: {
1486 id: this.id,
1487 type: "movies"
1488 }
1489 },
1490 preset: {
1491 data: {
1492 id: presetid,
1493 type: "presets"
1494 }
1495 }
1496 }
1497 }
1498 }
1499 });
1500 return data;
1501 }
1502
1503 async rename(newName) {
1504 let req = await lib.makeAPIRequest({
1505 env: this.remote,
1506 path: `/assets/${this.id}`,
1507 method: "PATCH",
1508 payload: {
1509 data: {
1510 attributes: {
1511 name: newName
1512 },
1513 type: "assets"
1514 }
1515 }
1516 });
1517 this.name = newName;
1518 return req;
1519 }
1520
1521 async migrate(targetEnv) {
1522 configObject.globalProgress = false;
1523 log(`Creating paired file in ${targetEnv}`); //Fetch metadata in parallel, we await it later
1524
1525 let _mdPromise = this.getMetadata();
1526
1527 let targetAsset = await Asset.getByName(targetEnv, this.name);
1528
1529 if (targetAsset) {
1530 log(`Asset already exists ${targetAsset.chalkPrint()}`); //if(configObject.script) process.exit(10);
1531 } else {
1532 targetAsset = await Asset.createNew(this.name, targetEnv);
1533 log(`Asset created ${targetAsset.chalkPrint()}`);
1534 } //wait for metadata to be ready before patching
1535
1536
1537 await _mdPromise;
1538 log("Adding asset metadata");
1539 await targetAsset.patchMetadata(this.md);
1540 let fileCreations = [];
1541
1542 for (let file of await this.getFiles()) {
1543 //Check for any valid copy-able instances
1544 for (let inst of file.instancesList) {
1545 //We need to skip internal files
1546 if (inst.storageLocationName === "Rally Platform Bucket") continue;
1547 log(`Adding file: ${file.chalkPrint()}`);
1548 fileCreations.push(targetAsset.addFileInstance(file, inst));
1549 }
1550 }
1551
1552 await Promise.all(fileCreations);
1553 }
1554
1555 async addFileInstance(file, inst, tagList = []) {
1556 let newInst = {
1557 uri: File.rslURL(inst),
1558 name: inst.name,
1559 size: inst.size,
1560 lastModified: inst.lastModified,
1561 storageLocationName: inst.storageLocationName
1562 };
1563 let request = lib.makeAPIRequest({
1564 env: this.remote,
1565 path: `/files`,
1566 method: "POST",
1567 payload: {
1568 data: {
1569 type: "files",
1570 attributes: {
1571 label: file.label,
1572 tagList,
1573 instances: {
1574 "1": newInst
1575 }
1576 },
1577 relationships: {
1578 asset: {
1579 data: {
1580 id: this.id,
1581 type: "assets"
1582 }
1583 }
1584 }
1585 }
1586 }
1587 });
1588
1589 try {
1590 let fileData = await request;
1591 let newFile = new File({
1592 data: fileData.data,
1593 remote: this.remote,
1594 parent: this
1595 });
1596 if (configObject.script) console.log(inst.uri, newFile.instancesList[0].uri);
1597 } catch (e) {
1598 log(chalk`{red Failed file: ${file.chalkPrint()}}`);
1599 }
1600 }
1601
1602}
1603
1604defineAssoc(Asset, "id", "data.id");
1605defineAssoc(Asset, "name", "data.attributes.name");
1606defineAssoc(Asset, "remote", "meta.remote");
1607defineAssoc(Asset, "md", "meta.metadata");
1608defineAssoc(Asset, "lite", "meta.lite");
1609Asset.endpoint = "movies";
1610
1611let home;
1612
1613if (os.homedir) {
1614 home = os.homedir();
1615}
1616
1617const colon = /:/g;
1618const siloLike = /(silo\-\w+?)s?\/([^\/]+)\.([\w1234567890]+)$/g;
1619function pathTransform(path) {
1620 if (path.includes(":")) {
1621 //Ignore the first colon in window-like filesystems
1622 path = path.slice(0, 3) + path.slice(3).replace(colon, "--");
1623 }
1624
1625 if (configObject.invertedPath) {
1626 path = path.replace(siloLike, "$2-$1.$3");
1627 }
1628
1629 if (path.includes("\\342\\200\\220")) {
1630 path = path.replace("\\342\\200\\220", "‐");
1631 }
1632
1633 return path;
1634}
1635function readFileSync(path, options) {
1636 return fs__default.readFileSync(pathTransform(path), options);
1637} //Create writefilesync, with ability to create directory if it doesnt exist
1638
1639function writeFileSync(path$1, data, options, dircreated = false) {
1640 path$1 = pathTransform(path$1);
1641
1642 try {
1643 return fs__default.writeFileSync(path$1, data, options);
1644 } catch (e) {
1645 if (dircreated) throw e;
1646 let directory = path.dirname(path$1);
1647
1648 try {
1649 fs__default.statSync(directory);
1650 throw e;
1651 } catch (nodir) {
1652 fs__default.mkdirSync(directory);
1653 return writeFileSync(path$1, data, options, true);
1654 }
1655 }
1656}
1657
1658let exists = {};
1659
1660class Preset extends RallyBase {
1661 constructor({
1662 path: path$1,
1663 remote,
1664 data,
1665 subProject
1666 } = {}) {
1667 // Get full path if possible
1668 if (path$1) {
1669 path$1 = path.resolve(path$1);
1670
1671 if (path.dirname(path$1).includes("silo-metadata")) {
1672 throw new AbortError("Constructing preset from metadata file");
1673 }
1674 }
1675
1676 super(); // Cache by path
1677
1678 if (path$1) {
1679 if (exists[pathTransform(path$1)]) return exists[pathTransform(path$1)];
1680 exists[pathTransform(path$1)] = this;
1681 }
1682
1683 this.meta = {};
1684 this.subproject = subProject;
1685 this.remote = remote;
1686
1687 if (lib.isLocalEnv(this.remote)) {
1688 if (path$1) {
1689 this.path = path$1;
1690 let pathspl = this.path.split(".");
1691 this.ext = pathspl[pathspl.length - 1];
1692
1693 try {
1694 this.code = this.getLocalCode();
1695 } catch (e) {
1696 if (e.code === "ENOENT" && configObject.ignoreMissing) {
1697 this.missing = true;
1698 return undefined;
1699 } else {
1700 log(chalk`{red Node Error} ${e.message}`);
1701 throw new AbortError("Could not load code of local file");
1702 }
1703 }
1704
1705 let name = this.parseFilenameForName() || this.parseCodeForName();
1706
1707 try {
1708 this.data = this.getLocalMetadata();
1709 this.isGeneric = true;
1710 name = this.name;
1711 } catch (e) {
1712 log(chalk`{yellow Warning}: ${path$1} does not have a readable metadata file! Looking for ${this.localmetadatapath}`);
1713 this.data = Preset.newShell(name);
1714 this.isGeneric = false;
1715 }
1716
1717 this.name = name;
1718 } else {
1719 this.data = Preset.newShell();
1720 }
1721 } else {
1722 this.data = data; //this.name = data.attributes.name;
1723 //this.id = data.id;
1724
1725 this.isGeneric = false;
1726 }
1727
1728 this.data.attributes.rallyConfiguration = undefined;
1729 this.data.attributes.systemManaged = undefined;
1730 } //Given a metadata file, get its actualy file
1731
1732
1733 static async fromMetadata(path, subproject) {
1734 let data;
1735
1736 try {
1737 data = JSON.parse(readFileSync(path));
1738 } catch (e) {
1739 if (e.code === "ENOENT" && configObject.ignoreMissing) {
1740 return null;
1741 } else {
1742 throw e;
1743 }
1744 }
1745
1746 let providerType = data.relationships.providerType.data.name;
1747 let provider = await Provider.getByName("DEV", providerType);
1748
1749 if (!provider) {
1750 log(chalk`{red The provider type {green ${providerType}} does not exist}`);
1751 log(chalk`{red Skipping {green ${path}}.}`);
1752 return null;
1753 }
1754
1755 let ext = await provider.getFileExtension();
1756 let name = data.attributes.name;
1757 let realpath = Preset.getLocalPath(name, ext, subproject);
1758 return new Preset({
1759 path: realpath,
1760 subProject: subproject
1761 });
1762 }
1763
1764 static newShell(name = undefined) {
1765 return {
1766 "attributes": {
1767 "providerSettings": {
1768 "PresetName": name
1769 }
1770 },
1771 "relationships": {},
1772 "type": "presets"
1773 };
1774 }
1775
1776 cleanup() {
1777 super.cleanup();
1778 delete this.attributes["createdAt"];
1779 delete this.attributes["updatedAt"];
1780 }
1781
1782 async acclimatize(env) {
1783 if (!this.isGeneric) throw new AbortError("Cannot acclimatize non-generics or shells");
1784 let providers = await Provider.getAll(env);
1785 let ptype = this.relationships["providerType"];
1786 ptype = ptype.data;
1787 let provider = providers.findByName(ptype.name);
1788 ptype.id = provider.id;
1789 }
1790
1791 get test() {
1792 if (!this.code) return [];
1793 const regex = /[^-]autotest:\s?([\w\d_\-. \/]+)[\r\s\n]*?/gm;
1794 let match;
1795 let matches = [];
1796
1797 while (match = regex.exec(this.code)) {
1798 matches.push(match[1]);
1799 }
1800
1801 return matches;
1802 }
1803
1804 async runTest(env) {
1805 let remote = await Preset.getByName(env, this.name);
1806
1807 for (let test of this.test) {
1808 log("Tests...");
1809 let asset;
1810
1811 if (test.startsWith("id")) {
1812 let match = /id:\s*(\d+)/g.exec(test);
1813
1814 if (!match) {
1815 log(chalk`{red Could not parse autotest} ${test}.`);
1816 throw new AbortError("Could not properly parse the preset header");
1817 }
1818
1819 asset = await Asset.getById(env, match[1]);
1820 } else {
1821 asset = await Asset.getByName(env, test);
1822 }
1823
1824 if (!asset) {
1825 log(chalk`{yellow No movie found}, skipping test.`);
1826 continue;
1827 }
1828
1829 log(chalk`Starting job {green ${this.name}} on ${asset.chalkPrint(false)}... `);
1830 await asset.startEvaluate(remote.id);
1831 }
1832 }
1833
1834 async resolve() {
1835 if (this.isGeneric) return;
1836 let proType = await this.resolveField(Provider, "providerType");
1837 this.ext = await proType.getFileExtension();
1838 this.isGeneric = true;
1839 return {
1840 proType
1841 };
1842 }
1843
1844 async saveLocal() {
1845 await this.saveLocalMetadata();
1846 await this.saveLocalFile();
1847 }
1848
1849 async saveLocalMetadata() {
1850 if (!this.isGeneric) {
1851 await this.resolve();
1852 this.cleanup();
1853 }
1854
1855 writeFileSync(this.localmetadatapath, JSON.stringify(this.data, null, 4));
1856 }
1857
1858 async saveLocalFile() {
1859 writeFileSync(this.localpath, this.code);
1860 }
1861
1862 async uploadRemote(env) {
1863 await this.uploadCodeToEnv(env, true);
1864 }
1865
1866 async save(env) {
1867 this.saved = true;
1868
1869 if (!this.isGeneric) {
1870 await this.resolve();
1871 }
1872
1873 this.cleanup();
1874
1875 if (lib.isLocalEnv(env)) {
1876 log(chalk`Saving preset {green ${this.name}} to {blue ${lib.envName(env)}}.`);
1877 await this.saveLocal();
1878 } else {
1879 await this.uploadRemote(env);
1880 }
1881 }
1882
1883 async downloadCode() {
1884 if (!this.remote || this.code) return this.code;
1885 let code = await lib.makeAPIRequest({
1886 env: this.remote,
1887 path_full: this.data.links.providerData,
1888 json: false
1889 }); //match header like
1890 // # c: d
1891 // # b
1892 // # a
1893 // ##################
1894
1895 let headerRegex = /(^# .+[\r\n]+)+#+[\r\n]+/gim;
1896 let hasHeader = headerRegex.exec(code);
1897
1898 if (hasHeader) {
1899 this.header = code.substring(0, hasHeader[0].length - 1);
1900 code = code.substring(hasHeader[0].length);
1901 }
1902
1903 return this.code = code;
1904 }
1905
1906 get code() {
1907 if (this._code) return this._code;
1908 }
1909
1910 set code(v) {
1911 this._code = v;
1912 }
1913
1914 chalkPrint(pad = true) {
1915 let id = String("P-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
1916 let sub = "";
1917
1918 if (this.subproject) {
1919 sub = chalk`{yellow ${this.subproject}}`;
1920 }
1921
1922 if (pad) id = id.padStart(10);
1923
1924 if (this.name == undefined) {
1925 return chalk`{green ${id}}: ${sub}{red ${this.path}}`;
1926 } else if (this.meta.proType) {
1927 return chalk`{green ${id}}: ${sub}{red ${this.meta.proType.name}} {blue ${this.name}}`;
1928 } else {
1929 return chalk`{green ${id}}: ${sub}{blue ${this.name}}`;
1930 }
1931 }
1932
1933 parseFilenameForName() {
1934 if (this.path.endsWith(".jinja") || this.path.endsWith(".json")) {
1935 return path.basename(this.path).replace("_", " ").replace("-", " ").replace(".json", "").replace(".jinja", "");
1936 }
1937 }
1938
1939 parseCodeForName() {
1940 const name_regex = /name\s?:\s([\w\d. \/]+)[\r\s\n]*?/;
1941 const match = name_regex.exec(this.code);
1942 if (match) return match[1];
1943 }
1944
1945 findStringsInCode(strings) {
1946 if (!this.code) return [];
1947 return strings.filter(str => {
1948 let regex = new RegExp(str);
1949 return !!this.code.match(regex);
1950 });
1951 }
1952
1953 static getLocalPath(name, ext, subproject) {
1954 return path__default.join(configObject.repodir, subproject || "", "silo-presets", name + "." + ext);
1955 }
1956
1957 get localpath() {
1958 return Preset.getLocalPath(this.name, this.ext, this.subproject);
1959 }
1960
1961 get path() {
1962 if (this._path) return this._path;
1963 }
1964
1965 set path(val) {
1966 this._path = val;
1967 }
1968
1969 get name() {
1970 return this._nameOuter;
1971 }
1972
1973 set name(val) {
1974 if (!this._nameInner) this._nameInner = val;
1975 this._nameOuter = val;
1976 }
1977
1978 set providerType(value) {
1979 this.relationships["providerType"] = {
1980 data: { ...value,
1981 type: "providerTypes"
1982 }
1983 };
1984 }
1985
1986 get localmetadatapath() {
1987 if (this.path) {
1988 return this.path.replace("silo-presets", "silo-metadata").replace(new RegExp(this.ext + "$"), "json");
1989 }
1990
1991 return path__default.join(configObject.repodir, this.subproject || "", "silo-metadata", this.name + ".json");
1992 }
1993
1994 get immutable() {
1995 return this.name.includes("Constant") && !configObject.updateImmutable;
1996 }
1997
1998 async uploadPresetData(env, id) {
1999 var _this$relationships, _this$relationships$p, _this$relationships$p2;
2000
2001 if (this.code.trim() === "NOUPLOAD") {
2002 write(chalk`code skipped {yellow :)}, `);
2003 return;
2004 }
2005
2006 let code = this.code;
2007 let headers = {};
2008 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;
2009
2010 if (!configObject.skipHeader && (providerName === "SdviEvaluate" || providerName === "SdviEvalPro")) {
2011 write(chalk`generate header, `);
2012 let repodir = configObject.repodir;
2013 let localpath = this.path.replace(repodir, "");
2014 if (localpath.startsWith("/")) localpath = localpath.substring(1);
2015
2016 try {
2017 let {
2018 stdout: headerText
2019 } = await spawn({
2020 noecho: true
2021 }, "sh", [path__default.join(configObject.repodir, `bin/header.sh`), moment(Date.now()).format("ddd YYYY/MM/DD hh:mm:ssa"), localpath]);
2022 code = headerText + code;
2023 write(chalk`header ok, `);
2024 } catch (e) {
2025 write(chalk`missing unix, `);
2026 }
2027 } //binary presets
2028
2029
2030 if (providerName == "Vantage") {
2031 code = code.toString("base64");
2032 headers["Content-Transfer-Encoding"] = "base64";
2033 }
2034
2035 let res = await lib.makeAPIRequest({
2036 env,
2037 path: `/presets/${id}/providerData`,
2038 body: code,
2039 method: "PUT",
2040 fullResponse: true,
2041 timeout: 10000,
2042 headers
2043 });
2044 write(chalk`code up {yellow ${res.statusCode}}, `);
2045 }
2046
2047 async grabMetadata(env) {
2048 let remote = await Preset.getByName(env, this.name);
2049 this.isGeneric = false;
2050
2051 if (!remote) {
2052 throw new AbortError(`No file found on remote ${env} with name ${this.name}`);
2053 }
2054
2055 this.data = remote.data;
2056 this.remote = env;
2057 }
2058
2059 async deleteRemoteVersion(env, id = null) {
2060 if (lib.isLocalEnv(env)) return false;
2061
2062 if (!id) {
2063 let remote = await Preset.getByName(env, this.name);
2064 id = remote.id;
2065 }
2066
2067 return await lib.makeAPIRequest({
2068 env,
2069 path: `/presets/${id}`,
2070 method: "DELETE"
2071 });
2072 }
2073
2074 async delete() {
2075 if (lib.isLocalEnv(this.remote)) return false;
2076 return await this.deleteRemoteVersion(this.remote, this.id);
2077 }
2078
2079 async uploadCodeToEnv(env, includeMetadata, shouldTest = true) {
2080 if (!this.name) {
2081 let match;
2082
2083 if (match = /^(#|["']{3})\s*EPH (\d+)/.exec(this.code.trim())) {
2084 let a = await Asset.getById(env, Number(match[2]));
2085 return a.startEphemeralEvaluateIdeal(this);
2086 } else {
2087 log(chalk`Failed uploading {red ${this.path}}. No name found.`);
2088 return;
2089 }
2090 }
2091
2092 write(chalk`Uploading preset {green ${this.name}} to {green ${env}}: `);
2093
2094 if (this.immutable) {
2095 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
2096 return;
2097 } //First query the api to see if this already exists.
2098
2099
2100 let remote = await Preset.getByName(env, this.name);
2101
2102 if (remote) {
2103 //If it exists we can replace it
2104 write("replace, ");
2105
2106 if (includeMetadata) {
2107 let payload = {
2108 data: {
2109 attributes: this.data.attributes,
2110 type: "presets"
2111 }
2112 };
2113
2114 if (this.relationships.tagNames) {
2115 payload.relationships = {
2116 tagNames: this.relationships.tagNames
2117 };
2118 }
2119
2120 let res = await lib.makeAPIRequest({
2121 env,
2122 path: `/presets/${remote.id}`,
2123 method: "PATCH",
2124 payload,
2125 fullResponse: true
2126 });
2127 write(chalk`metadata {yellow ${res.statusCode}}, `);
2128
2129 if (res.statusCode == 500) {
2130 log(chalk`skipping code upload, did not successfully upload metadata`);
2131 return;
2132 }
2133 }
2134
2135 await this.uploadPresetData(env, remote.id);
2136 } else {
2137 write("create, ");
2138 let metadata = {
2139 data: this.data
2140 };
2141
2142 if (!this.relationships["providerType"]) {
2143 throw new AbortError("Cannot acclimatize shelled presets. (try creating it on the env first)");
2144 }
2145
2146 await this.acclimatize(env);
2147 write("Posting to create preset... ");
2148 let res = await lib.makeAPIRequest({
2149 env,
2150 path: `/presets`,
2151 method: "POST",
2152 payload: metadata,
2153 timeout: 5000
2154 });
2155 let id = res.data.id;
2156 write(chalk`Created id {green ${id}}... Uploading Code... `);
2157 await this.uploadPresetData(env, id);
2158 }
2159
2160 if (this.test[0] && shouldTest) {
2161 await this.runTest(env);
2162 } else {
2163 log("No tests. Done.");
2164 }
2165 }
2166
2167 getLocalMetadata() {
2168 return JSON.parse(readFileSync(this.localmetadatapath, "utf-8"));
2169 }
2170
2171 getLocalCode() {
2172 //todo fixup for binary presets, see uploadPresetData
2173 return readFileSync(this.path, "utf-8");
2174 }
2175
2176 parseHeaderInfo() {
2177 var _$exec$, _$exec$2, _$exec$3, _$exec$4, _$exec$5, _$exec$6, _$exec$7;
2178
2179 if (!this.header) return null;
2180 let abs = {
2181 built: (_$exec$ = /Built On:(.+)/.exec(this.header)[1]) === null || _$exec$ === void 0 ? void 0 : _$exec$.trim(),
2182 author: (_$exec$2 = /Author:(.+)/.exec(this.header)[1]) === null || _$exec$2 === void 0 ? void 0 : _$exec$2.trim(),
2183 build: (_$exec$3 = /Build:(.+)/.exec(this.header)[1]) === null || _$exec$3 === void 0 ? void 0 : _$exec$3.trim(),
2184 version: (_$exec$4 = /Version:(.+)/.exec(this.header)[1]) === null || _$exec$4 === void 0 ? void 0 : _$exec$4.trim(),
2185 branch: (_$exec$5 = /Branch:(.+)/.exec(this.header)[1]) === null || _$exec$5 === void 0 ? void 0 : _$exec$5.trim(),
2186 commit: (_$exec$6 = /Commit:(.+)/.exec(this.header)[1]) === null || _$exec$6 === void 0 ? void 0 : _$exec$6.trim(),
2187 local: (_$exec$7 = /Local File:(.+)/.exec(this.header)[1]) === null || _$exec$7 === void 0 ? void 0 : _$exec$7.trim()
2188 };
2189 let tryFormats = [[true, "ddd MMM DD HH:mm:ss YYYY"], [false, "ddd YYYY/MM/DD LTS"]];
2190
2191 for (let [isUTC, format] of tryFormats) {
2192 let date;
2193
2194 if (isUTC) {
2195 date = moment.utc(abs.built, format);
2196 } else {
2197 date = moment(abs.built, format);
2198 }
2199
2200 if (!date.isValid()) continue;
2201 abs.offset = date.fromNow();
2202 break;
2203 }
2204
2205 return abs;
2206 }
2207
2208 async printRemoteInfo(env) {
2209 let remote = await Preset.getByName(env, this.name);
2210 await remote.downloadCode();
2211 let i = remote.parseHeaderInfo();
2212
2213 if (i) {
2214 log(chalk`
2215 ENV: {red ${env}}, updated {yellow ~${i.offset}}
2216 Built on {blue ${i.built}} by {green ${i.author}}
2217 From ${i.build || "(unknown)"} on ${i.branch} ({yellow ${i.commit}})
2218 `.replace(/^[ \t]+/gim, "").trim());
2219 } else {
2220 log(chalk`No header on {red ${env}}`);
2221 }
2222 }
2223
2224 async getInfo(envs) {
2225 await this.printDepends();
2226
2227 for (let env of envs.split(",")) {
2228 await this.printRemoteInfo(env);
2229 }
2230 }
2231
2232 async printDepends(indent = 0, locals = null, seen = {}) {
2233 let includeRegex = /@include "(.+)"/gim; //let includeRegex = /@include/g;
2234
2235 let includes = [];
2236 let inc;
2237
2238 while (inc = includeRegex.exec(this.code)) {
2239 includes.push(inc[1]);
2240 } //let includes = this.code
2241 //.split("\n")
2242 //.map(x => includeRegex.exec(x))
2243 //.filter(x => x)
2244 //.map(x => x[1]);
2245 //log(includes);
2246
2247
2248 if (!locals) {
2249 locals = new Collection((await loadLocals("silo-presets", Preset)));
2250 }
2251
2252 log(Array(indent + 1).join(" ") + "- " + this.name);
2253
2254 for (let include of includes) {
2255 if (seen[include]) {
2256 log(Array(indent + 1).join(" ") + " - (seen) " + include);
2257 } else {
2258 seen[include] = true;
2259 let file = await locals.findByName(include);
2260
2261 if (file) {
2262 await file.printDepends(indent + 2, locals, seen);
2263 } else {
2264 log(Array(indent + 1).join(" ") + " - (miss) " + include);
2265 }
2266 }
2267 }
2268 }
2269
2270}
2271
2272defineAssoc(Preset, "_nameInner", "data.attributes.providerSettings.PresetName");
2273defineAssoc(Preset, "_nameOuter", "data.attributes.name");
2274defineAssoc(Preset, "id", "data.id");
2275defineAssoc(Preset, "attributes", "data.attributes");
2276defineAssoc(Preset, "relationships", "data.relationships");
2277defineAssoc(Preset, "remote", "meta.remote");
2278defineAssoc(Preset, "_code", "meta.code");
2279defineAssoc(Preset, "_path", "meta.path");
2280defineAssoc(Preset, "isGeneric", "meta.isGeneric");
2281defineAssoc(Preset, "ext", "meta.ext");
2282defineAssoc(Preset, "subproject", "meta.project");
2283defineAssoc(Preset, "metastring", "meta.metastring");
2284Preset.endpoint = "presets";
2285
2286class Notification extends RallyBase {
2287 constructor({
2288 data,
2289 remote
2290 }) {
2291 super();
2292 this.data = data;
2293 this.meta = {};
2294 this.remote = remote;
2295 }
2296
2297 static async getAllPreCollect(notifications) {
2298 return notifications.sort((a, b) => {
2299 return a.attributes.type.localeCompare(b.attributes.type) || a.attributes.name.localeCompare(b.attributes.name);
2300 });
2301 }
2302
2303 chalkPrint(pad = false) {
2304 let id = String("N-" + this.id);
2305 if (pad) id = id.padStart(4);
2306 return chalk`{green ${id}}: {blue ${this.type}} - {green ${this.name}}`;
2307 }
2308
2309}
2310
2311defineAssoc(Notification, "id", "data.id");
2312defineAssoc(Notification, "name", "data.attributes.name");
2313defineAssoc(Notification, "address", "data.attributes.address");
2314defineAssoc(Notification, "type", "data.attributes.type");
2315defineAssoc(Notification, "remote", "meta.remote");
2316Notification.endpoint = "notificationPresets";
2317
2318class Rule extends RallyBase {
2319 constructor({
2320 path: path$1,
2321 data,
2322 remote,
2323 subProject
2324 } = {}) {
2325 super();
2326
2327 if (path$1) {
2328 path$1 = path.resolve(path$1);
2329
2330 try {
2331 let f = readFileSync(path$1, "utf-8");
2332 data = JSON.parse(readFileSync(path$1, "utf-8"));
2333 } catch (e) {
2334 if (e.code === "ENOENT") {
2335 if (configObject.ignoreMissing) {
2336 this.missing = true;
2337 return undefined;
2338 } else {
2339 throw new AbortError("Could not load code of local file");
2340 }
2341 } else {
2342 throw new AbortError(`Unreadable JSON in ${path$1}. ${e}`);
2343 }
2344 }
2345 }
2346
2347 this.meta = {};
2348 this.subproject = subProject;
2349
2350 if (!data) {
2351 data = Rule.newShell();
2352 }
2353
2354 this.data = data;
2355 this.remote = remote;
2356 this.isGeneric = !this.remote;
2357 }
2358
2359 static newShell() {
2360 return {
2361 "attributes": {
2362 "description": "-",
2363 "priority": "PriorityNorm",
2364 "starred": false
2365 },
2366 "relationships": {},
2367 "type": "workflowRules"
2368 };
2369 }
2370
2371 async acclimatize(env) {
2372 this.remote = env;
2373 let preset = await this.resolveField(Preset, "preset", false, "specific");
2374 let pNext = await this.resolveField(Rule, "passNext", false, "specific");
2375 let eNext = await this.resolveField(Rule, "errorNext", false, "specific");
2376 let proType = await this.resolveField(Provider, "providerType", false, "specific");
2377 let dynamicNexts = await this.resolveField(Rule, "dynamicNexts", true, "specific");
2378 let enterNotif = await this.resolveField(Notification, "enterNotifications", true, "specific");
2379 let errorNotif = await this.resolveField(Notification, "errorNotifications", true, "specific");
2380 let passNotif = await this.resolveField(Notification, "passNotifications", true, "specific");
2381 }
2382
2383 async saveA(env) {
2384 if (lib.isLocalEnv(env)) return;
2385 return await this.createIfNotExist(env);
2386 }
2387
2388 async saveB(env) {
2389 if (!this.isGeneric) {
2390 await this.resolve();
2391 }
2392
2393 this.cleanup();
2394
2395 if (lib.isLocalEnv(env)) {
2396 log(chalk`Saving rule {green ${this.name}} to {blue ${lib.envName(env)}}.`);
2397 writeFileSync(this.localpath, JSON.stringify(this.data, null, 4));
2398 } else {
2399 await this.acclimatize(env);
2400 await this.uploadRemote(env);
2401 }
2402 }
2403
2404 get immutable() {
2405 return false;
2406 }
2407
2408 async createIfNotExist(env) {
2409 write(chalk`First pass rule {green ${this.name}} to {green ${env}}: `);
2410
2411 if (this.immutable) {
2412 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
2413 return;
2414 } //First query the api to see if this already exists.
2415
2416
2417 let remote = await Rule.getByName(env, this.name);
2418 this.idMap = this.idMap || {};
2419
2420 if (remote) {
2421 this.idMap[env] = remote.id;
2422 log(chalk`exists ${remote.chalkPrint(false)}`);
2423 return;
2424 } //If it exists we can replace it
2425
2426
2427 write("create, ");
2428 let res = await lib.makeAPIRequest({
2429 env,
2430 path: `/workflowRules`,
2431 method: "POST",
2432 payload: {
2433 data: {
2434 attributes: {
2435 name: this.name
2436 },
2437 type: "workflowRules"
2438 }
2439 }
2440 });
2441 this.idMap = this.idMap || {};
2442 this.idMap[env] = res.data.id;
2443 write("id ");
2444 log(this.idMap[env]);
2445 }
2446
2447 async patchStrip() {
2448 delete this.data.attributes.createdAt;
2449 delete this.data.attributes.starred;
2450 delete this.data.attributes.updatedAt; // TEMP FIX FOR BUG IN SDVI
2451
2452 if (this.relationships.passMetadata && this.relationships.passMetadata[0]) {
2453 log("HAS PASS");
2454 log(this.name);
2455 log("HAS PASS");
2456 }
2457
2458 delete this.relationships.passMetadata;
2459
2460 if (this.relationships.errorMetadata && this.relationships.errorMetadata[0]) {
2461 log("HAS PASS");
2462 log(this.name);
2463 log("HAS PASS");
2464 }
2465
2466 delete this.relationships.errorMetadata; // This is commented out because it was fixed.
2467 //for(let key in this.relationships){
2468 //let relationship = this.relationships[key];
2469 //if(!relationship.data || relationship.data instanceof Array && !relationship.data[0]){
2470 //delete this.relationships[key];
2471 //}
2472 //}
2473 }
2474
2475 async uploadRemote(env) {
2476 write(chalk`Uploading rule {green ${this.name}} to {green ${env}}: `);
2477
2478 if (this.immutable) {
2479 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
2480 return;
2481 }
2482
2483 if (this.idMap[env]) {
2484 this.remote = env;
2485 await this.patchStrip();
2486 this.data.id = this.idMap[env]; //If it exists we can replace it
2487
2488 write("replace, ");
2489 let res = await lib.makeAPIRequest({
2490 env,
2491 path: `/workflowRules/${this.idMap[env]}`,
2492 method: "PATCH",
2493 payload: {
2494 data: this.data
2495 },
2496 fullResponse: true
2497 });
2498 log(chalk`response {yellow ${res.statusCode}}`);
2499
2500 if (res.statusCode !== 200) {
2501 log(res.body);
2502 log(JSON.stringify(this.data, null, 4));
2503 }
2504 } else {
2505 throw Error("Bad idmap!");
2506 }
2507 }
2508
2509 get localpath() {
2510 return path.join(configObject.repodir, this.subproject || "", "silo-rules", this.name + ".json");
2511 }
2512
2513 async resolve() {
2514 let preset = await this.resolveField(Preset, "preset", false); //log(preset);
2515
2516 let pNext = await this.resolveField(Rule, "passNext", false);
2517 let eNext = await this.resolveField(Rule, "errorNext", false);
2518 let proType = await this.resolveField(Provider, "providerType", false); //log("Dynamic nexts")
2519
2520 let dynamicNexts = await this.resolveField(Rule, "dynamicNexts", true); //log(dynamicNexts);
2521
2522 let enterNotif = await this.resolveField(Notification, "enterNotifications", true);
2523 let errorNotif = await this.resolveField(Notification, "errorNotifications", true);
2524 let passNotif = await this.resolveField(Notification, "passNotifications", true); //TODO Unsupported
2525
2526 delete this.relationships["enterMetadata"];
2527 delete this.relationships["errorMetadata"];
2528 this.isGeneric = true;
2529 return {
2530 preset,
2531 proType,
2532 pNext,
2533 eNext,
2534 dynamicNexts,
2535 errorNotif,
2536 enterNotif,
2537 passNotif
2538 };
2539 }
2540
2541 chalkPrint(pad = true) {
2542 let id = String("R-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
2543 let sub = "";
2544
2545 if (this.subproject) {
2546 sub = chalk`{yellow ${this.subproject}}`;
2547 }
2548
2549 if (pad) id = id.padStart(10);
2550
2551 try {
2552 return chalk`{green ${id}}: ${sub}{blue ${this.name}}`;
2553 } catch (e) {
2554 return this.data;
2555 }
2556 }
2557
2558}
2559
2560defineAssoc(Rule, "name", "data.attributes.name");
2561defineAssoc(Rule, "description", "data.attributes.description");
2562defineAssoc(Rule, "id", "data.id");
2563defineAssoc(Rule, "relationships", "data.relationships");
2564defineAssoc(Rule, "isGeneric", "meta.isGeneric");
2565defineAssoc(Rule, "remote", "meta.remote");
2566defineAssoc(Rule, "subproject", "meta.project");
2567defineAssoc(Rule, "idMap", "meta.idMap");
2568Rule.endpoint = "workflowRules";
2569
2570//Move project into silo metadata
2571//move autotest into silo metadata
2572//
2573
2574class SupplyChain {
2575 constructor(startingRule, stopRule) {
2576 if (startingRule) {
2577 this.startingRule = startingRule;
2578 this.stopRule = stopRule;
2579 this.remote = startingRule.remote;
2580 }
2581 }
2582
2583 async downloadPresetCode(objs = this.allPresets) {
2584 log("Downloading code... ");
2585 await lib.keepalive(objs.arr.map(x => () => x.downloadCode()));
2586 }
2587
2588 async calculate() {
2589 log("Getting rules... ");
2590 this.allRules = await Rule.getAll(this.remote);
2591 log(this.allRules.length);
2592 log("Getting presets... ");
2593 this.allPresets = await Preset.getAll(this.remote);
2594 log(this.allPresets.length);
2595 log("Getting providers... ");
2596 this.allProviders = await Provider.getAll(this.remote);
2597 log(this.allProviders.length);
2598 log("Getting notifications... ");
2599 this.allNotifications = await Notification.getAll(this.remote);
2600 log(this.allNotifications.length);
2601
2602 if (!this.startingRule) {
2603 this.rules = this.allRules;
2604 this.presets = this.allPresets;
2605 this.notifications = new Collection([]);
2606 await this.downloadPresetCode();
2607 return;
2608 } else {
2609 await this.downloadPresetCode();
2610 }
2611
2612 log("Done!"); //Now we have everything we need to find a whole supply chain
2613
2614 write("Calculating Supply chain... ");
2615 log(this.startingRule.chalkPrint());
2616 let allRuleNames = this.allRules.arr.map(x => x.name).filter(x => x.length >= 4);
2617 let allPresetNames = this.allPresets.arr.map(x => x.name).filter(x => x.length >= 4);
2618 let allNotifNames = this.allNotifications.arr.map(x => x.name).filter(x => x.length >= 4);
2619 let requiredNotifications = new Set();
2620 let ruleQueue = [this.startingRule];
2621 let presetQueue = [];
2622
2623 for (let currentRule of ruleQueue) {
2624 if (currentRule === this.stopRule) continue;
2625 let {
2626 eNext,
2627 pNext,
2628 preset,
2629 passNotif,
2630 errorNotif,
2631 enterNotif
2632 } = await currentRule.resolve();
2633 passNotif.forEach(n => requiredNotifications.add(n));
2634 enterNotif.forEach(n => requiredNotifications.add(n));
2635 errorNotif.forEach(n => requiredNotifications.add(n));
2636 if (eNext && !ruleQueue.includes(eNext)) ruleQueue.push(eNext);
2637 if (pNext && !ruleQueue.includes(eNext)) ruleQueue.push(pNext);
2638 let neededPresets = preset.findStringsInCode(allPresetNames);
2639 neededPresets = neededPresets.map(x => this.allPresets.findByName(x));
2640 let neededRules = preset.findStringsInCode(allRuleNames);
2641 neededRules = neededRules.map(x => this.allRules.findByName(x));
2642 preset.findStringsInCode(allNotifNames).map(str => this.allNotifications.findByName(str)).forEach(notif => requiredNotifications.add(notif));
2643 neededPresets.push(preset);
2644
2645 for (let p of neededPresets) if (!presetQueue.includes(p)) presetQueue.push(p);
2646
2647 for (let p of neededRules) if (!ruleQueue.includes(p)) ruleQueue.push(p);
2648
2649 if (configObject.verbose) {
2650 write(currentRule.chalkPrint(false));
2651 log(":");
2652 write(" ");
2653 write(preset.chalkPrint(false));
2654 log(":");
2655 write(" Pass Next: ");
2656 if (pNext) write(pNext.chalkPrint(false));else write("None");
2657 log("");
2658 write(" Err Next: ");
2659 if (eNext) write(eNext.chalkPrint(false));else write("None");
2660 log("");
2661 log(" Rules:");
2662
2663 for (let p of neededRules) log(" " + p.chalkPrint(true));
2664
2665 log(" Presets:");
2666
2667 for (let p of neededPresets) log(" " + p.chalkPrint(true));
2668
2669 log("\n");
2670 }
2671 }
2672
2673 log("Done!");
2674 this.rules = new Collection(ruleQueue);
2675 this.presets = new Collection(presetQueue);
2676 requiredNotifications.delete(undefined);
2677 this.notifications = new Collection([...requiredNotifications]);
2678 }
2679
2680 async log() {
2681 if (this.notifications.arr.length > 0) {
2682 log("Required notifications: ");
2683 this.notifications.log();
2684 }
2685
2686 if (this.rules.arr.length > 0) {
2687 write("Required rules: ");
2688 log(this.rules.arr.length);
2689 this.rules.log();
2690 }
2691
2692 if (this.presets.arr.length > 0) {
2693 write("Required presets: ");
2694 log(this.presets.arr.length);
2695 this.presets.log();
2696 }
2697
2698 if (configObject.rawOutput) {
2699 return {
2700 presets: this.presets.arr,
2701 rules: this.rules.arr,
2702 notifications: this.notifications.arr
2703 };
2704 }
2705 }
2706
2707 async deleteTo(env) {
2708 for (let preset of this.presets) {
2709 try {
2710 await preset.deleteRemoteVersion(env);
2711 } catch (e) {
2712 log(e);
2713 }
2714 }
2715 }
2716
2717 async syncTo(env) {
2718 for (let preset of this.presets) {
2719 try {
2720 await preset.save(env);
2721 } catch (e) {
2722 log(e);
2723 }
2724 }
2725
2726 if (this.rules.arr[0]) {
2727 log("Starting create phase for rules");
2728
2729 for (let rule of this.rules) {
2730 try {
2731 await rule.saveA(env);
2732 } catch (e) {
2733 log(e);
2734 }
2735 }
2736
2737 log("OK");
2738 log("Starting link phase for rules");
2739 Rule.removeCache(env);
2740
2741 for (let rule of this.rules) {
2742 try {
2743 await rule.saveB(env);
2744 } catch (e) {
2745 log(e);
2746 }
2747 }
2748 }
2749 }
2750
2751}
2752
2753class User extends RallyBase {
2754 constructor({
2755 data,
2756 remote
2757 }) {
2758 super();
2759 this.data = data;
2760 this.meta = {};
2761 this.remote = remote;
2762 }
2763
2764 chalkPrint(pad = false) {
2765 let id = String("U-" + this.id);
2766 if (pad) id = id.padStart(7);
2767 return chalk`{green ${id}}: {blue ${this.name}}`;
2768 }
2769
2770}
2771
2772defineAssoc(User, "id", "data.id");
2773defineAssoc(User, "name", "data.attributes.name");
2774defineAssoc(User, "email", "data.attributes.email");
2775defineAssoc(User, "remote", "meta.remote");
2776User.endpoint = "users";
2777
2778class Tag extends RallyBase {
2779 constructor({
2780 data,
2781 remote
2782 } = {}) {
2783 super();
2784 this.meta = {};
2785 this.remote = remote;
2786 this.data = data; //this.data.attributes.rallyConfiguration = undefined;
2787 //this.data.attributes.systemManaged = undefined;
2788 }
2789
2790 chalkPrint(pad = true) {
2791 let id = String("T-" + this.remote + "-" + this.id);
2792 if (pad) id = id.padStart(10);
2793 let prefix = this.curated ? "blue +" : "red -";
2794 return chalk`{green ${id}}: {${prefix}${this.name}}`;
2795 }
2796
2797 static async create(env, name, {
2798 notCurated
2799 } = {}) {
2800 return new Tag({
2801 data: await lib.makeAPIRequest({
2802 env,
2803 path: `/${this.endpoint}`,
2804 method: "POST",
2805 payload: {
2806 data: {
2807 attributes: {
2808 name,
2809 curated: notCurated ? false : true
2810 },
2811 type: "tagNames"
2812 }
2813 }
2814 }),
2815 remote: env
2816 });
2817 }
2818
2819}
2820
2821defineAssoc(Tag, "id", "data.id");
2822defineAssoc(Tag, "attributes", "data.attributes");
2823defineAssoc(Tag, "relationships", "data.relationships");
2824defineAssoc(Tag, "name", "data.attributes.name");
2825defineAssoc(Tag, "curated", "data.attributes.curated");
2826defineAssoc(Tag, "remote", "meta.remote");
2827Tag.endpoint = "tagNames";
2828
2829async function findLineInFile(renderedPreset, lineNumber) {
2830 let trueFileLine = lineNumber;
2831 let linedRenderedPreset = renderedPreset.split("\n").slice(2, -2);
2832 renderedPreset = renderedPreset.split("\n").slice(2, -2).join("\n");
2833 let includeLocation = renderedPreset.split("\n").filter(x => x.includes("@include"));
2834 let endIncludeNumber = -1,
2835 addTabDepth = 2;
2836 let lineBeforeIncludeStatement = '';
2837 let withinInclude = true;
2838
2839 if (lineNumber > linedRenderedPreset.indexOf(includeLocation[includeLocation.length - 1])) {
2840 addTabDepth = 0;
2841 withinInclude = false;
2842 }
2843
2844 for (let index = includeLocation.length - 1; index >= 0; index--) {
2845 let currIncludeIndex = linedRenderedPreset.indexOf(includeLocation[index]);
2846 let tabDepth = includeLocation[index].split(" ").length;
2847
2848 if (lineNumber > currIncludeIndex) {
2849 if (includeLocation[index].split(" ").filter(Boolean)[1] != "ERROR:") {
2850 if (lineBeforeIncludeStatement.split(" ").length == tabDepth && withinInclude) {
2851 trueFileLine = trueFileLine - currIncludeIndex;
2852 break;
2853 } else if (lineBeforeIncludeStatement.split(" ").length + addTabDepth == tabDepth && endIncludeNumber == -1) {
2854 endIncludeNumber = currIncludeIndex;
2855 } else if (lineBeforeIncludeStatement.split(" ").length + addTabDepth == tabDepth) {
2856 trueFileLine = trueFileLine - (endIncludeNumber - currIncludeIndex);
2857 endIncludeNumber = -1;
2858 }
2859 }
2860 } else {
2861 lineBeforeIncludeStatement = includeLocation[index];
2862 }
2863 }
2864
2865 let funcLine = "";
2866
2867 for (let line of linedRenderedPreset.slice(0, lineNumber).reverse()) {
2868 let match = /def (\w+)/.exec(line);
2869
2870 if (match) {
2871 funcLine = match[1];
2872 break;
2873 }
2874 }
2875
2876 let includeFilename;
2877
2878 if (lineBeforeIncludeStatement != "") {
2879 includeFilename = lineBeforeIncludeStatement.slice(1).trim().slice(14, -1);
2880 } else {
2881 includeFilename = null;
2882 }
2883
2884 if (includeLocation.length !== 0) {
2885 trueFileLine -= 1;
2886 lineNumber -= 1;
2887 }
2888
2889 return {
2890 lineNumber: trueFileLine,
2891 includeFilename,
2892 line: linedRenderedPreset[lineNumber],
2893 funcLine
2894 };
2895}
2896function printOutLine(eLine) {
2897 return log(chalk`{blue ${eLine.includeFilename || "Main"}}:{green ${eLine.lineNumber}} in ${eLine.funcLine}
2898${eLine.line}`);
2899}
2900async function getInfo(env, jobid) {
2901 log(env, jobid);
2902 let trace = lib.makeAPIRequest({
2903 env,
2904 path: `/jobs/${jobid}/artifacts/trace`
2905 }).catch(x => null);
2906 let renderedPreset = lib.makeAPIRequest({
2907 env,
2908 path: `/jobs/${jobid}/artifacts/preset`
2909 }).catch(x => null);
2910 let result = lib.makeAPIRequest({
2911 env,
2912 path: `/jobs/${jobid}/artifacts/result`
2913 }).catch(x => null);
2914 let error = lib.makeAPIRequest({
2915 env,
2916 path: `/jobs/${jobid}/artifacts/error`
2917 }).catch(x => null);
2918 let output = lib.makeAPIRequest({
2919 env,
2920 path: `/jobs/${jobid}/artifacts/output`
2921 }).catch(x => null);
2922 [trace, renderedPreset, result, output, error] = await Promise.all([trace, renderedPreset, result, output, error]);
2923 return {
2924 trace,
2925 renderedPreset,
2926 result,
2927 output,
2928 error
2929 };
2930}
2931async function parseTrace(env, jobid) {
2932 let {
2933 trace,
2934 renderedPreset
2935 } = await getInfo(env, jobid);
2936 let lineNumber = -1;
2937 let errorLines = [];
2938 let shouldBreak = 0;
2939
2940 for (let tr of trace.split("\n\n").reverse()) {
2941 errorLines.push(tr);
2942 shouldBreak--;
2943 if (tr.includes("Exception")) shouldBreak = 1;
2944 if (tr.includes("raised")) shouldBreak = 1;
2945 if (!shouldBreak) break;
2946 }
2947
2948 let errorList = [];
2949
2950 for (let errLine of errorLines) {
2951 lineNumber = /^[\w ]+:(\d+):/g.exec(errLine);
2952
2953 if (lineNumber && lineNumber[1]) {
2954 errorList.push((await findLineInFile(renderedPreset, lineNumber[1])));
2955 } else {
2956 errorList.push(errLine);
2957 }
2958 }
2959
2960 return errorList;
2961}
2962const Trace = {
2963 parseTrace,
2964 printOutLine,
2965 getInfo,
2966 findLineInFile
2967};
2968
2969require("source-map-support").install();
2970const rallyFunctions = {
2971 async bestPagintation() {
2972 global.silentAPI = true;
2973
2974 for (let i = 10; i <= 30; i += 5) {
2975 console.time("test with " + i);
2976 let dl = await lib.indexPathFast("DEV", `/workflowRules?page=1p${i}`);
2977 console.timeEnd("test with " + i);
2978 }
2979 },
2980
2981 async uploadPresets(env, presets, createFunc = () => false) {
2982 for (let preset of presets) {
2983 await preset.uploadCodeToEnv(env, createFunc);
2984 }
2985 },
2986
2987 //Dummy test access
2988 async testAccess(env) {
2989 if (lib.isLocalEnv(env)) {
2990 //TODO
2991 return true;
2992 }
2993
2994 let result = await lib.makeAPIRequest({
2995 env,
2996 path: "/providers?page=1p1",
2997 fullResponse: true,
2998 timeout: 1000
2999 });
3000 return result.statusCode;
3001 }
3002
3003};
3004
3005var allIndexBundle = /*#__PURE__*/Object.freeze({
3006 __proto__: null,
3007 rallyFunctions: rallyFunctions,
3008 SupplyChain: SupplyChain,
3009 Preset: Preset,
3010 Rule: Rule,
3011 Provider: Provider,
3012 Notification: Notification,
3013 Asset: Asset,
3014 User: User,
3015 Tag: Tag,
3016 Trace: Trace,
3017 get configFile () { return configFile; },
3018 loadConfig: loadConfig,
3019 loadConfigFromArgs: loadConfigFromArgs,
3020 setConfig: setConfig,
3021 get configObject () { return configObject; },
3022 lib: lib,
3023 AbortError: AbortError,
3024 APIError: APIError,
3025 UnconfiguredEnvError: UnconfiguredEnvError,
3026 ProtectedEnvError: ProtectedEnvError,
3027 FileTooLargeError: FileTooLargeError,
3028 Collection: Collection,
3029 RallyBase: RallyBase,
3030 sleep: sleep
3031});
3032
3033var version = "2.4.0";
3034
3035var baseCode = {
3036 SdviContentMover: `{
3037 "tasks": [
3038 {
3039 "operation": "copy" | "move" | "delete",
3040
3041 "source": {
3042 "optional": true | false,
3043
3044 # must specify either inventory OR externalStorage
3045 "inventory": {
3046 "labels": ["<label>" | "*", ],
3047 "tags": ["<tag>", ],
3048 "storageSet": ["<storage location name>" | "*", ], # only valid for move & delete tasks
3049 "expandCollections": true | false
3050 },
3051 "externalStorage": {
3052 "uri": "<protocol>://<host>/<path>/<file>",
3053 "credentials": {
3054 "key": "<parameter store key>",
3055 "roleArn": "<role to assume to access the parameter store>",
3056 "roleId": "<external ID to be used in role assumption>",
3057 "region": "<AWS region of the parameter store>"
3058 }
3059 }
3060 },
3061
3062 "destination": {
3063 "name": "<path within the storage location>/<filename>",
3064 "overwrite": "never" | "notInAnyAsset" | "notInOtherAsset" | "always",
3065 "storageMetadata": {"<key>": "<value>",...} | "<sourceStorageMetadata>",
3066
3067 # must specify either inventory OR externalStorage
3068 "inventory": {
3069 "storage": "<storage location name>",
3070 "newLabel": "<fileLabel>",
3071 "newTags": ["<tag>", "<tag>"],
3072 },
3073 "externalStorage": {
3074 "uri": "<protocol>://<host>",
3075 "credentials": {
3076 "key": "<parameter store key>",
3077 "roleArn": "<role to assume to access the parameter store>",
3078 "roleId": "<external ID to be used in role assumption>",
3079 "region": "<AWS region of the parameter store>"
3080 }
3081 }
3082 }
3083 },
3084
3085 {
3086 <another task>
3087 },
3088
3089 {
3090 <another task>
3091 },
3092
3093 ...
3094
3095 ]
3096}`,
3097 SdviEvaluate: `'''
3098name: {name}
3099'''
3100
3101# code here`,
3102 SdviEvalPro: `'''
3103name: {name}
3104'''
3105
3106import rally
3107
3108def evalMain(context):
3109 # code here`
3110};
3111
3112var _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;
3113
3114require("source-map-support").install();
3115let argv = argparse(process.argv.slice(2), {
3116 string: ["file", "env"],
3117 //boolean: ["no-protect"],
3118 boolean: ["anon"],
3119 default: {
3120 protect: true
3121 },
3122 alias: {
3123 f: "file",
3124 e: "env"
3125 }
3126}); //help menu helper
3127
3128function printHelp(help, short) {
3129 let helpText = chalk`
3130{white ${help.name}}: ${help.text}
3131 Usage: ${help.usage || "<unknown>"}
3132`; //Trim newlines
3133
3134 helpText = helpText.substring(1, helpText.length - 1);
3135
3136 if (!short) {
3137 for (let param of help.params || []) {
3138 helpText += chalk`\n {blue ${param.param}}: ${param.desc}`;
3139 }
3140
3141 for (let arg of help.args || []) {
3142 helpText += chalk`\n {blue ${arg.short}}, {blue ${arg.long}}: ${arg.desc}`;
3143 }
3144 }
3145
3146 return helpText;
3147}
3148
3149async function getFilesFromArgs(args) {
3150 let lastArg = args._.shift();
3151
3152 if (args.file) {
3153 let files = args.file;
3154 if (typeof files === "string") files = [files];
3155 return files;
3156 }
3157
3158 if (lastArg == "-") {
3159 log("Reading from stdin");
3160
3161 let getStdin = require("get-stdin");
3162
3163 let stdin = await getStdin();
3164 let files = stdin.split("\n");
3165 if (files[files.length - 1] === "") files.pop();
3166 return files;
3167 } else {
3168 args._.push(lastArg);
3169 }
3170}
3171
3172let presetsub = {
3173 async before(args) {
3174 this.env = args.env;
3175 if (!this.env) throw new AbortError("No env supplied");
3176 this.files = await getFilesFromArgs(args);
3177 },
3178
3179 async $grab(args) {
3180 if (!this.files) {
3181 throw new AbortError("No files provided to grab (use --file argument)");
3182 }
3183
3184 log(chalk`Grabbing {green ${this.files.length}} preset(s) metadata from {green ${this.env}}.`);
3185 let presets = this.files.map(path => new Preset({
3186 path,
3187 remote: false
3188 }));
3189
3190 for (let preset of presets) {
3191 //TODO small refactor
3192 await preset.grabMetadata(this.env);
3193 await preset.saveLocalMetadata();
3194
3195 if (args.full) {
3196 let remo = await Preset.getByName(this.env, preset.name);
3197 await remo.resolve();
3198 await remo.downloadCode();
3199 await remo.saveLocalFile();
3200 }
3201 }
3202 },
3203
3204 async $create(args) {
3205 let provider, name, ext;
3206
3207 if (args.provider) {
3208 provider = {
3209 name: args.provider
3210 };
3211 ext = args.ext;
3212 } else {
3213 provider = await selectProvider((await Provider.getAll(this.env)));
3214 ext = (await provider.getEditorConfig()).fileExt;
3215 }
3216
3217 if (args.name) {
3218 name = args.name;
3219 } else {
3220 name = await askInput("Preset Name", "What is the preset name?");
3221 }
3222
3223 let preset = new Preset({
3224 subProject: configObject.project
3225 });
3226 preset.providerType = {
3227 name: provider.name
3228 };
3229 preset.isGeneric = true;
3230 preset.name = name;
3231 preset.ext = ext;
3232
3233 if (baseCode[provider.name]) {
3234 preset._code = baseCode[provider.name].replace("{name}", name);
3235 } else {
3236 preset._code = " ";
3237 }
3238
3239 preset.saveLocalMetadata();
3240 if (!args["only-metadata"]) preset.saveLocalFile();
3241 },
3242
3243 async $list(args) {
3244 log("Loading...");
3245 let presets = await Preset.getAll(this.env);
3246
3247 if (args.resolve) {
3248 Provider.getAll(this.env);
3249
3250 for (let preset of presets) {
3251 let resolve = await preset.resolve(this.env);
3252
3253 if (args.attach) {
3254 let {
3255 proType
3256 } = resolve;
3257 proType.editorConfig.helpText = "";
3258 preset.meta = { ...preset.meta,
3259 proType
3260 };
3261 }
3262 }
3263 }
3264
3265 if (configObject.rawOutput) return presets;
3266 log(chalk`{yellow ${presets.length}} presets on {green ${this.env}}.`);
3267 presets.arr.sort((a, b) => {
3268 return Number(a.attributes.updatedAt) - Number(b.attributes.updatedAt);
3269 });
3270
3271 for (let preset of presets) {
3272 log(preset.chalkPrint());
3273 }
3274 },
3275
3276 async $upload(args) {
3277 if (!this.files) {
3278 throw new AbortError("No files provided to upload (use --file argument)");
3279 }
3280
3281 log(chalk`Uploading {green ${this.files.length}} preset(s) to {green ${this.env}}.`);
3282 let presets = this.files.map(path => new Preset({
3283 path,
3284 remote: false
3285 }));
3286 await rallyFunctions.uploadPresets(this.env, presets);
3287 },
3288
3289 async $deleteRemote(args) {
3290 let file = this.files[0];
3291
3292 if (!this.files) {
3293 throw new AbortError("No files provided to diff (use --file argument)");
3294 }
3295
3296 let preset = new Preset({
3297 path: file,
3298 remote: false
3299 });
3300
3301 if (!preset.name) {
3302 throw new AbortError(chalk`No preset header found. Cannot get name.`);
3303 }
3304
3305 let preset2 = await Preset.getByName(this.env, preset.name);
3306
3307 if (!preset2) {
3308 throw new AbortError(chalk`No preset found with name {red ${preset.name}} on {blue ${this.env}}`);
3309 }
3310
3311 log(chalk`Deleting ${preset2.chalkPrint(true)}.`);
3312 log((await preset2.delete()));
3313 },
3314
3315 async $diff(args) {
3316 let file = this.files[0];
3317
3318 if (!this.files) {
3319 throw new AbortError("No files provided to diff (use --file argument)");
3320 }
3321
3322 let preset = new Preset({
3323 path: file,
3324 remote: false
3325 });
3326
3327 if (!preset.name) {
3328 throw new AbortError(chalk`No preset header found. Cannot get name.`);
3329 }
3330
3331 let preset2 = await Preset.getByName(this.env, preset.name);
3332
3333 if (!preset2) {
3334 throw new AbortError(chalk`No preset found with name {red ${preset.name}} on {blue ${this.env}}`);
3335 }
3336
3337 await preset2.downloadCode();
3338
3339 let tempfile = require("tempy").file;
3340
3341 let temp = tempfile({
3342 extension: `${this.env}.${preset.ext}`
3343 });
3344 fs.writeFileSync(temp, preset2.code);
3345 let ptr = `${file},${temp}`; //raw output returns "file1" "file2"
3346
3347 if (configObject.rawOutput) {
3348 if (args["only-new"]) return temp;else return ptr;
3349 } //standard diff
3350
3351
3352 argv.command = argv.command || "diff";
3353 await spawn(argv.command, [file, temp], {
3354 stdio: "inherit"
3355 });
3356 },
3357
3358 async $info(args) {
3359 if (!this.files) {
3360 throw new AbortError("No files provided to diff (use --file argument)");
3361 }
3362
3363 let file = this.files[0];
3364 let preset = new Preset({
3365 path: file,
3366 remote: false
3367 });
3368
3369 if (!preset.name) {
3370 throw new AbortError(chalk`No preset header found. Cannot get name.`);
3371 }
3372
3373 await preset.getInfo(args.env);
3374 },
3375
3376 async unknown(arg, args) {
3377 log(chalk`Unknown action {red ${arg}} try '{white rally help preset}'`);
3378 }
3379
3380};
3381let rulesub = {
3382 async before(args) {
3383 this.env = args.env;
3384 if (!this.env) throw new AbortError("No env supplied");
3385 },
3386
3387 async $list(args) {
3388 log("Loading...");
3389 let rules = await Rule.getAll(this.env);
3390 if (configObject.rawOutput) return rules;
3391 log(chalk`{yellow ${rules.length}} rules on {green ${this.env}}.`);
3392 rules.arr.sort((a, b) => {
3393 return Number(a.data.attributes.updatedAt) - Number(b.data.attributes.updatedAt);
3394 });
3395
3396 for (let rule of rules) log(rule.chalkPrint());
3397 },
3398
3399 async $create(args) {
3400 let preset = await selectPreset({
3401 canSelectNone: false
3402 });
3403 let passNext = await selectRule({
3404 purpose: "'On Exit OK'"
3405 });
3406 let errorNext = await selectRule({
3407 purpose: "'On Exit Error'"
3408 });
3409 let name = await askInput("Rule Name", "What is the rule name?");
3410 name = name.replace("@", preset.name);
3411 let desc = await askInput("Description", "Enter a description.");
3412 let dynamicNexts = [];
3413 let next;
3414
3415 while (next = await selectRule({
3416 purpose: "dynamic next"
3417 })) {
3418 let name = await askInput("Key", "Key name for dynamic next");
3419 dynamicNexts.push({
3420 meta: {
3421 transition: name
3422 },
3423 type: "workflowRules",
3424 name: next.name
3425 });
3426 }
3427
3428 let rule = new Rule({
3429 subProject: configObject.project
3430 });
3431 rule.name = name;
3432 rule.description = desc;
3433 rule.relationships.preset = {
3434 data: {
3435 name: preset.name,
3436 type: "presets"
3437 }
3438 };
3439 if (errorNext) rule.relationships.errorNext = {
3440 data: {
3441 name: errorNext.name,
3442 type: "workflowRules"
3443 }
3444 };
3445 if (passNext) rule.relationships.passNext = {
3446 data: {
3447 name: passNext.name,
3448 type: "workflowRules"
3449 }
3450 };
3451
3452 if (dynamicNexts[0]) {
3453 rule.relationships.dynamicNexts = {
3454 data: dynamicNexts
3455 };
3456 }
3457
3458 rule.saveB();
3459 },
3460
3461 async unknown(arg, args) {
3462 log(chalk`Unknown action {red ${arg}} try '{white rally help rule}'`);
3463 }
3464
3465};
3466let jupytersub = {
3467 async before(args) {
3468 this.input = args._.shift() || "main.ipynb";
3469 this.output = args._.shift() || "main.py";
3470 },
3471
3472 async $build(args) {
3473 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(" ");
3474 log(chalk`Compiling GCR file {green ${this.input}} into {green ${this.output}} using jupyter...`);
3475
3476 try {
3477 let {
3478 timestr
3479 } = await spawn(cmd[0], cmd.slice(1));
3480 log(chalk`Complete in ~{green.bold ${timestr}}.`);
3481 } catch (e) {
3482 if (e.code !== "ENOENT") throw e;
3483 log(chalk`Cannot run the build command. Make sure that you have jupyter notebook installed.\n{green pip install jupyter}`);
3484 return;
3485 }
3486 }
3487
3488};
3489
3490async function categorizeString(str, defaultSubproject = undefined) {
3491 str = str.trim();
3492
3493 if (str.startsWith('"')) {
3494 str = str.slice(1, -1);
3495 }
3496
3497 if (match = /^(\w)-(\w{1,10})-(\d{1,10}):/.exec(str)) {
3498 if (match[1] === "P") {
3499 let ret = await Preset.getById(match[2], match[3]); //TODO modify for subproject a bit
3500
3501 return ret;
3502 } else if (match[1] === "R") {
3503 return await Rule.getById(match[2], match[3]);
3504 } else {
3505 return null;
3506 }
3507 } else if (match = /^([\w \/\\\-_]*)[\/\\]?silo\-(\w+)[\/\\]/.exec(str)) {
3508 try {
3509 switch (match[2]) {
3510 case "presets":
3511 return new Preset({
3512 path: str,
3513 subProject: match[1]
3514 });
3515
3516 case "rules":
3517 return new Rule({
3518 path: str,
3519 subProject: match[1]
3520 });
3521
3522 case "metadata":
3523 return await Preset.fromMetadata(str, match[1]);
3524 }
3525 } catch (e) {
3526 log(e);
3527 }
3528 } else {
3529 return null;
3530 }
3531}
3532
3533let tagsub = {
3534 async before(args) {
3535 this.env = args.env;
3536 if (!this.env) throw new AbortError("No env supplied");
3537 },
3538
3539 async $list(args) {
3540 log("Loading...");
3541 let tags = await Tag.getAll(this.env);
3542 if (configObject.rawOutput) return tags;
3543 log(chalk`{yellow ${tags.length}} tags on {green ${this.env}}.`);
3544 tags.arr.sort((a, b) => {
3545 return Number(a.data.attributes.updatedAt) - Number(b.data.attributes.updatedAt);
3546 });
3547
3548 for (let tag of tags) log(tag.chalkPrint());
3549 },
3550
3551 async $create(args) {
3552 return Tag.create(this.env, args._.shift());
3553 }
3554
3555};
3556let supplysub = {
3557 async before(args) {
3558 this.env = args.env;
3559 if (!this.env) throw new AbortError("No env supplied");
3560 this.files = await getFilesFromArgs(args);
3561 },
3562
3563 //Calculate a supply chain based on a starting rule at the top of the stack
3564 async $calc(args) {
3565 let name = args._.shift();
3566
3567 let stopName = args._.shift();
3568
3569 if (!name) {
3570 throw new AbortError("No starting rule or @ supplied");
3571 }
3572
3573 if (name === "@") {
3574 log(chalk`Silo clone started`);
3575 this.chain = new SupplyChain();
3576 this.chain.remote = args.env;
3577 } else {
3578 let rules = await Rule.getAll(this.env);
3579 let stop, start;
3580 start = rules.findByNameContains(name);
3581 if (stopName) stop = rules.findByNameContains(stopName);
3582
3583 if (!start) {
3584 throw new AbortError(chalk`No starting rule found by name {blue ${name}}`);
3585 }
3586
3587 log(chalk`Analzying supply chain: ${start.chalkPrint(false)} - ${stop ? stop.chalkPrint(false) : "(open)"}`);
3588 this.chain = new SupplyChain(start, stop);
3589 }
3590
3591 await this.chain.calculate();
3592 return await this.postAction(args);
3593 },
3594
3595 async postAction(args) {
3596 //Now that we ahve a supply chain object, do something with it
3597 if (args["to"]) {
3598 this.chain.log();
3599
3600 if (this.chain.presets.arr[0]) {
3601 await this.chain.downloadPresetCode(this.chain.presets);
3602 log("Done");
3603 }
3604
3605 if (Array.isArray(args["to"])) {
3606 for (let to of args["to"]) {
3607 await this.chain.syncTo(to);
3608 }
3609 } else {
3610 await this.chain.syncTo(args["to"]);
3611 }
3612 } else if (args["delete"]) {
3613 if (Array.isArray(args["delete"])) {
3614 for (let to of args["delete"]) {
3615 await this.chain.deleteTo(to);
3616 }
3617 } else {
3618 await this.chain.deleteTo(args["delete"]);
3619 }
3620 } else if (args["diff"]) {
3621 //Very basic diff
3622 let env = args["diff"];
3623 await Promise.all(this.chain.presets.arr.map(obj => obj.downloadCode()));
3624 await Promise.all(this.chain.presets.arr.map(obj => obj.resolve()));
3625 let otherPresets = await Promise.all(this.chain.presets.arr.map(obj => Preset.getByName(env, obj.name)));
3626 otherPresets = new Collection(otherPresets.filter(x => x));
3627 await Promise.all(otherPresets.arr.map(obj => obj.downloadCode()));
3628 await Promise.all(otherPresets.arr.map(obj => obj.resolve()));
3629
3630 const printPresets = (preset, otherPreset) => {
3631 log(preset.chalkPrint(true));
3632
3633 if (otherPreset.name) {
3634 log(otherPreset.chalkPrint(true));
3635 } else {
3636 log(chalk`{red (None)}`);
3637 }
3638 };
3639
3640 for (let preset of this.chain.presets) {
3641 let otherPreset = otherPresets.arr.find(x => x.name === preset.name) || {};
3642 preset.code = preset.code.replace(/[\r\n ]/, "");
3643 otherPreset.code = (otherPreset.code || "").replace(/[\r\n ]/, "");
3644
3645 if (preset.code === otherPreset.code) {
3646 if (!args["ignore-same"]) {
3647 printPresets(preset, otherPreset);
3648 log("Code Same");
3649 }
3650 } else {
3651 printPresets(preset, otherPreset);
3652
3653 if (args["ignore-same"]) {
3654 log("-------");
3655 } else {
3656 log("Code Different");
3657 }
3658 }
3659 }
3660 } else {
3661 return await this.chain.log();
3662 }
3663 },
3664
3665 async $make(args) {
3666 let set = new Set();
3667 let hints = args.hint ? Array.isArray(args.hint) ? args.hint : [args.hint] : []; //TODO modify for better hinting, and add this elsewhere
3668
3669 for (let hint of hints) {
3670 if (hint === "presets-uat") {
3671 log("got hint");
3672 await Preset.getAll("UAT");
3673 }
3674 }
3675
3676 for (let file of this.files) {
3677 set.add((await categorizeString(file)));
3678 }
3679
3680 let files = [...set];
3681 files = files.filter(f => f && !f.missing);
3682 this.chain = new SupplyChain();
3683 this.chain.rules = new Collection(files.filter(f => f instanceof Rule));
3684 this.chain.presets = new Collection(files.filter(f => f instanceof Preset));
3685 this.chain.notifications = new Collection([]);
3686 return await this.postAction(args);
3687 },
3688
3689 async unknown(arg, args) {
3690 log(chalk`Unknown action {red ${arg}} try '{white rally help supply}'`);
3691 }
3692
3693};
3694
3695function subCommand(object) {
3696 object = {
3697 before() {},
3698
3699 after() {},
3700
3701 unknown() {},
3702
3703 ...object
3704 };
3705 return async function (args) {
3706 //Grab the next arg on the stack, find a function tied to it, and run
3707 let arg = args._.shift();
3708
3709 let key = "$" + arg;
3710 let ret;
3711
3712 if (object[key]) {
3713 await object.before(args);
3714 ret = await object[key](args);
3715 await object.after(args);
3716 } else {
3717 if (arg === undefined) arg = "(None)";
3718 object.unknown(arg, args);
3719 }
3720
3721 return ret;
3722 };
3723}
3724
3725let 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 = {
3726 async help(args) {
3727 let arg = args._.shift();
3728
3729 if (arg) {
3730 let help = helpEntries[arg];
3731
3732 if (!help) {
3733 log(chalk`No help found for '{red ${arg}}'`);
3734 } else {
3735 log(printHelp(helpEntries[arg]));
3736 }
3737 } else {
3738 for (let helpArg in helpEntries) {
3739 log(printHelp(helpEntries[helpArg], true));
3740 }
3741 }
3742 },
3743
3744 async jupyter(args) {
3745 return subCommand(jupytersub)(args);
3746 },
3747
3748 //@helpText(`Print input args, for debugging`)
3749 async printArgs(args) {
3750 log(args);
3751 },
3752
3753 async preset(args) {
3754 return subCommand(presetsub)(args);
3755 },
3756
3757 async rule(args) {
3758 return subCommand(rulesub)(args);
3759 },
3760
3761 async supply(args) {
3762 return subCommand(supplysub)(args);
3763 },
3764
3765 async tag(args) {
3766 return subCommand(tagsub)(args);
3767 },
3768
3769 async trace(args) {
3770 let jobId = args._.shift();
3771
3772 if (!jobId) throw new AbortError("No job id");
3773 if (!args.env) throw new AbortError("no env");
3774
3775 let ln = args._.shift();
3776
3777 if (!ln) {
3778 log("is trace");
3779 let traceInfo = await parseTrace(args.env, jobId);
3780
3781 for (let line of traceInfo) {
3782 if (typeof line == "string") {
3783 log(chalk.red(line));
3784 } else {
3785 printOutLine(line);
3786 }
3787 }
3788 } else {
3789 log("is ln");
3790 let {
3791 renderedPreset
3792 } = await getInfo(args.env, jobId);
3793 return findLineInFile(renderedPreset, Number(ln));
3794 }
3795 },
3796
3797 async providers(args) {
3798 let env = args.env;
3799 if (!env) return errorLog("No env supplied.");
3800
3801 let ident = args._.shift();
3802
3803 let providers = await Provider.getAll(env);
3804
3805 if (ident) {
3806 let pro = providers.arr.find(x => x.id == ident || x.name.includes(ident));
3807
3808 if (!pro) {
3809 log(chalk`Couldn't find provider by {green ${ident}}`);
3810 } else {
3811 log(pro.chalkPrint(false));
3812 let econfig = await pro.getEditorConfig();
3813
3814 if (args.raw) {
3815 return pro;
3816 } else {
3817 if (econfig.helpText.length > 100) {
3818 econfig.helpText = "<too long to display>";
3819 }
3820
3821 if (econfig.completions.length > 5) {
3822 econfig.completions = "<too long to display>";
3823 }
3824
3825 log(econfig);
3826 }
3827 }
3828 } else {
3829 if (args.raw) return providers;
3830
3831 for (let pro of providers) log(pro.chalkPrint());
3832 }
3833 },
3834
3835 async config(args) {
3836 let prop = args._.shift();
3837
3838 let propArray = prop && prop.split("."); //if(!await configHelpers.askQuestion(`Would you like to create a new config file in ${configFile}`)) return;
3839
3840 let newConfigObject;
3841
3842 if (!prop) {
3843 if (configObject.rawOutput) return configObject;
3844 log("Creating new config");
3845 newConfigObject = { ...configObject
3846 };
3847
3848 for (let helperName in configHelpers) {
3849 if (helperName.startsWith("$")) {
3850 newConfigObject = { ...newConfigObject,
3851 ...(await configHelpers[helperName](false))
3852 };
3853 }
3854 }
3855 } else {
3856 log(chalk`Editing option {green ${prop}}`);
3857
3858 if (args.set) {
3859 newConfigObject = { ...configObject,
3860 [prop]: args.set
3861 };
3862 } else {
3863 let ident = "$" + propArray[0];
3864
3865 if (configHelpers[ident]) {
3866 newConfigObject = { ...configObject,
3867 ...(await configHelpers[ident](propArray))
3868 };
3869 } else {
3870 log(chalk`No helper for {red ${ident}}`);
3871 return;
3872 }
3873 }
3874 }
3875
3876 newConfigObject.hasConfig = true; //Create readable json and make sure the user is ok with it
3877
3878 let newConfig = JSON.stringify(newConfigObject, null, 4);
3879 log(newConfig); //-y or --set will make this not prompt
3880
3881 if (!args.y && !args.set && !(await askQuestion("Write this config to disk?"))) return;
3882 fs.writeFileSync(configFile, newConfig, {
3883 mode: 0o600
3884 });
3885 log(chalk`Created file {green ${configFile}}.`);
3886 },
3887
3888 async asset(args) {
3889 function uuid(args) {
3890 const digits = 16;
3891 return String(Math.floor(Math.random() * Math.pow(10, digits))).padStart(digits, "0");
3892 }
3893
3894 let name = args.name || `TEST_#`;
3895 let env = args.env;
3896 let asset;
3897
3898 let arg = args._.shift();
3899
3900 if (!arg) {
3901 throw new AbortError(chalk`Missing arguments: see {white 'rally help asset'}`);
3902 }
3903
3904 if (args.anon) {
3905 args._.unshift(arg);
3906 } else if (arg == "create") {
3907 name = name.replace("#", uuid());
3908 asset = await Asset.createNew(name, env);
3909 } else {
3910 args._.unshift(arg);
3911
3912 if (args.id) {
3913 asset = Asset.lite(args.id, env);
3914 } else {
3915 asset = await Asset.getByName(env, args.name);
3916 }
3917 }
3918
3919 if (!asset && !args.anon) {
3920 throw new AbortError("No asset found/created");
3921 }
3922
3923 let launchArg = 0;
3924 let fileArg = 0;
3925
3926 let arrayify = (obj, i) => Array.isArray(obj) ? obj[i] : i == 0 ? obj : undefined;
3927
3928 while (arg = args._.shift()) {
3929 if (arg === "launch") {
3930 let initData = arrayify(args["init-data"], launchArg);
3931
3932 if (initData && initData.startsWith("@")) {
3933 log(chalk`Reading init data from {white ${initData.slice(1)}}`);
3934 initData = fs.readFileSync(initData.slice(1), "utf-8");
3935 }
3936
3937 let jobName = arrayify(args["job-name"], launchArg);
3938 let p = await Rule.getByName(env, jobName);
3939
3940 if (!p) {
3941 throw new AbortError(`Cannot launch job ${jobName}, does not exist (?)`);
3942 } else {
3943 log(chalk`Launching ${p.chalkPrint(false)} on ${asset ? asset.chalkPrint(false) : "(None)"}`);
3944 }
3945
3946 if (asset) {
3947 await asset.startWorkflow(jobName, {
3948 initData,
3949 priority: args.priority
3950 });
3951 } else {
3952 await Asset.startAnonWorkflow(env, jobName, {
3953 initData,
3954 priority: args.priority
3955 });
3956 }
3957
3958 launchArg++;
3959 } else if (arg === "launchEvaluate") {
3960 let initData = arrayify(args["init-data"], launchArg);
3961
3962 if (initData && initData.startsWith("@")) {
3963 log(chalk`Reading init data from {white ${initData.slice(1)}}`);
3964 initData = fs.readFileSync(initData.slice(1), "utf-8");
3965 }
3966
3967 let jobName = arrayify(args["job-name"], launchArg);
3968 let jobData;
3969 let ephemeralEval = false;
3970 let p;
3971
3972 if (jobName.startsWith("@")) {
3973 ephemeralEval = true;
3974 jobData = fs.readFileSync(jobName.slice(1));
3975 } else {
3976 p = await Preset.getByName(env, jobName);
3977
3978 if (!p) {
3979 throw new AbortError(`Cannot launch preset ${jobName}, does not exist (?)`);
3980 } else {
3981 log(chalk`Launching ${p.chalkPrint(false)} on ${asset ? asset.chalkPrint(false) : "(None)"}`);
3982 }
3983 }
3984
3985 if (ephemeralEval) {
3986 await Asset.startEphemeralEvaluateIdeal(env, p.id, initData);
3987 } else {
3988 await asset.startEvaluate(p.id, initData);
3989 }
3990
3991 launchArg++;
3992 } else if (arg === "addfile") {
3993 let label = arrayify(args["file-label"], fileArg);
3994 let uri = arrayify(args["file-uri"], fileArg);
3995
3996 if (label === undefined || !uri) {
3997 throw new AbortError("Number of file-label and file-uri does not match");
3998 }
3999
4000 await asset.addFile(label, uri);
4001 log(chalk`Added file ${label}`);
4002 fileArg++;
4003 } else if (arg === "delete") {
4004 await asset.delete();
4005 } else if (arg === "create") {
4006 throw new AbortError(`Cannot have more than 1 create/get per asset call`);
4007 } else if (arg === "show" || arg == "load") {
4008 if (asset.lite) asset = await Asset.getById(env, asset.id);
4009 if (arg == "show") log(asset);
4010 } else if (arg === "metadata" || arg === "md") {
4011 log((await asset.getMetadata()));
4012 } else if (arg === "migrate") {
4013 await asset.migrate(args["target-env"]);
4014 } else if (arg === "patchMetadata") {
4015 let initData = arrayify(args["metadata"], launchArg);
4016
4017 if (initData && initData.startsWith("@")) {
4018 log(chalk`Reading data from {white ${initData.slice(1)}}`);
4019 initData = fs.readFileSync(initData.slice(1), "utf-8");
4020 }
4021
4022 initData = JSON.parse(initData);
4023 await asset.patchMetadata(initData);
4024 } else if (arg === "rename") {
4025 let newName = args["new-name"];
4026 let oldName = asset.name;
4027 await asset.rename(newName);
4028 log(chalk`Rename: {green ${oldName}} -> {green ${newName}}`);
4029 }
4030 }
4031
4032 if (configObject.rawOutput && !configObject.script) return asset;
4033 },
4034
4035 async checkSegments(args) {
4036 let asset = args._.shift();
4037
4038 let res = await lib.makeAPIRequest({
4039 env: args.env,
4040 path: `/movies/${asset}/metadata/Metadata`
4041 });
4042 let segments = res.data.attributes.metadata.userMetaData.segments.segments;
4043 let r = segments.reduce((lastSegment, val, ind) => {
4044 let curSegment = val.startTime;
4045
4046 if (curSegment < lastSegment) {
4047 log("bad segment " + (ind + 1));
4048 }
4049
4050 return val.endTime;
4051 }, "00:00:00:00");
4052 },
4053
4054 async getJobs(args) {
4055 let req = await lib.indexPathFast({
4056 env: args.env,
4057 path: "/jobs",
4058 qs: {
4059 filter: "presetName=DCTC Add Element US Checkin"
4060 }
4061 });
4062 log(req.map(x => x.relationships.asset.data.id).join("\n"));
4063 },
4064
4065 async listAssets(args, tag) {
4066 let req = await lib.indexPathFast({
4067 env: args.env,
4068 path: "/assets",
4069 qs: {
4070 noRelationships: true,
4071 sort: "id"
4072 },
4073 chunksize: 30
4074 });
4075
4076 for (let asset of req) {
4077 log(asset.id);
4078 }
4079
4080 return req;
4081 },
4082
4083 async listSegments(args) {
4084 let f = JSON.parse(fs.readFileSync(args.file, "utf-8"));
4085
4086 for (let asset of f) {
4087 var _r$data$attributes$me, _r$data$attributes$me2;
4088
4089 let r = await lib.makeAPIRequest({
4090 env: args.env,
4091 path: `/movies/${asset.id}/metadata/Metadata`
4092 });
4093 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;
4094
4095 if (segs && segs.length > 1) {
4096 log(asset.id);
4097 log(asset.name);
4098 }
4099 }
4100 },
4101
4102 async test4(args) {
4103 let things = await lib.indexPathFast({
4104 env: args.env,
4105 path: "/assets",
4106 qs: {
4107 filter: `createdBefore=${Date.now() - 1000 * 160 * 24 * 60 * 60},createdSince=${Date.now() - 1000 * 170 * 24 * 60 * 60}`
4108 }
4109 });
4110 log(JSON.stringify(things, null, 4));
4111 },
4112
4113 async test5(args) {
4114 let things = await lib.indexPathFast({
4115 env: args.env,
4116 path: "/jobs",
4117 qs: {
4118 filter: `state=Queued,presetName=E2 P4101 - DNE Compliance Edit - US Output Deal - Edit WorkOrder`
4119 }
4120 });
4121 log(JSON.stringify(things, null, 4));
4122 },
4123
4124 async test2(args) {
4125 let wfr = await lib.indexPath({
4126 env: args.env,
4127 path: "/workflowRuleMetadata"
4128 });
4129 log(wfr);
4130
4131 for (let wfrm of wfr) {
4132 try {
4133 wfrm.id = undefined;
4134 wfrm.links = undefined;
4135 log(wfrm);
4136 let req = await lib.makeAPIRequest({
4137 env: "DEV",
4138 path: "/workflowRuleMetadata",
4139 method: "POST",
4140 payload: {
4141 data: wfrm
4142 }
4143 });
4144 } catch (e) {
4145 log("caught");
4146 } //break;
4147
4148 }
4149 },
4150
4151 async test3(args) {
4152 let wfr = await lib.indexPath({
4153 env: args.env,
4154 path: "/workflowRuleMetadata"
4155 });
4156 log(wfr);
4157
4158 for (let wfrm of wfr) {
4159 try {
4160 wfrm.id = undefined;
4161 wfrm.links = undefined;
4162 log(wfrm);
4163 let req = await lib.makeAPIRequest({
4164 env: "DEV",
4165 path: "/workflowRuleMetadata",
4166 method: "POST",
4167 payload: {
4168 data: wfrm
4169 }
4170 });
4171 } catch (e) {
4172 log("caught");
4173 } //break;
4174
4175 }
4176 },
4177
4178 async deleteOmneons(args) {
4179 let id = args._.shift();
4180
4181 let asset;
4182
4183 if (Number(id)) {
4184 asset = await Asset.getById("PROD", Number(id));
4185 } else {
4186 asset = await Asset.getByName("PROD", id);
4187 }
4188
4189 log(asset);
4190 let f = await asset.getFiles();
4191
4192 for (let file of f) {
4193 if (file.label.includes("Omneon")) {
4194 log(`removing ${file.label}`);
4195 await file.delete();
4196 }
4197 }
4198 },
4199
4200 async audit(args) {
4201 let supportedAudits = ["presets", "rule", "other"];
4202 await addAutoCompletePrompt();
4203 let q = await inquirer.prompt([{
4204 type: "autocomplete",
4205 name: "obj",
4206 message: `What audit do you want?`,
4207 source: async (sofar, input) => {
4208 return supportedAudits.filter(x => input ? x.includes(input.toLowerCase()) : true);
4209 }
4210 }]);
4211 let choice = q.obj;
4212 let resourceId = undefined;
4213
4214 let filterFunc = _ => _;
4215
4216 if (choice === "presets") {
4217 let preset = await selectPreset({
4218 canSelectNone: false
4219 });
4220 let remote = await Preset.getByName(args.env, preset.name);
4221 if (!remote) throw new AbortError("Could not find this item on remote env");
4222
4223 filterFunc = ev => ev.resource == "Preset";
4224
4225 resourceId = remote.id;
4226 } else if (choice === "rule") {
4227 let preset = await selectRule({
4228 canSelectNone: false
4229 });
4230 let remote = await Rule.getByName(args.env, preset.name);
4231 if (!remote) throw new AbortError("Could not find this item on remote env");
4232
4233 filterFunc = ev => ev.resource == "Rule";
4234
4235 resourceId = remote.id;
4236 } else {
4237 resourceId = await askInput(null, "What resourceID?");
4238 }
4239
4240 log(chalk`Resource ID on {blue ${args.env}} is {yellow ${resourceId}}`);
4241 log(`Loading audits (this might take a while)`);
4242 const numRows = 100;
4243 let r = await lib.makeAPIRequest({
4244 env: args.env,
4245 path: `/v1.0/audit?perPage=${numRows}&count=${numRows}&filter=%7B%22resourceId%22%3A%22${resourceId}%22%7D&autoload=false&pageNum=1&include=`,
4246 timeout: 180000
4247 });
4248 r.data = r.data.filter(filterFunc);
4249 log("Data recieved, parsing users");
4250
4251 for (let event of r.data) {
4252 var _event$correlation;
4253
4254 let uid = event === null || event === void 0 ? void 0 : (_event$correlation = event.correlation) === null || _event$correlation === void 0 ? void 0 : _event$correlation.userId;
4255 if (!uid) continue;
4256
4257 try {
4258 event.user = await User.getById(args.env, uid);
4259 } catch (e) {
4260 event.user = {
4261 name: "????"
4262 };
4263 }
4264 }
4265
4266 if (args.raw) return r.data;
4267 let evCounter = 0;
4268
4269 for (let event of r.data) {
4270 var _event$user;
4271
4272 let evtime = moment(event.createdAt);
4273 let date = evtime.format("ddd YYYY/MM/DD hh:mm:ssa");
4274 let timedist = evtime.fromNow();
4275 log(chalk`${date} {yellow ${timedist}} {green ${(_event$user = event.user) === null || _event$user === void 0 ? void 0 : _event$user.name}} ${event.event}`);
4276 if (++evCounter >= 30) break;
4277 }
4278 },
4279
4280 async audit2(args) {
4281 const numRows = 1000;
4282 let r = await lib.makeAPIRequest({
4283 env: args.env,
4284 //path: `/v1.0/audit?perPage=${numRows}&count=${numRows}&autoload=false&pageNum=1&include=`,
4285 path: `/v1.0/audit?perPage=${numRows}&count=${numRows}&filter=%7B%22correlation.userId%22%3A%5B%22164%22%5D%7D&autoload=false&pageNum=1&include=`,
4286 timeout: 60000
4287 });
4288
4289 for (let event of r.data) {
4290 log(event.event);
4291 }
4292 },
4293
4294 async findIDs(args) {
4295 let files = await getFilesFromArgs(args);
4296
4297 for (let file of files) {
4298 let preset = await Preset.getByName(args.env, file);
4299 await preset.resolve();
4300 log(`silo-presets/${file}.${preset.ext}`);
4301 }
4302 },
4303
4304 async getAssets(env, name) {
4305 if (!this.callid) this.callid = 0;
4306 this.callid++;
4307 let callid = this.callid;
4308 await sleep(500);
4309 if (callid != this.callid) return this.lastResult || [];
4310 let req = await lib.makeAPIRequest({
4311 env,
4312 path: `/assets`,
4313 qs: name ? {
4314 filter: `nameContains=${name}`
4315 } : undefined
4316 });
4317 this.lastCall = Date.now();
4318 return this.lastResult = req.data;
4319 },
4320
4321 async content(args) {
4322 addAutoCompletePrompt();
4323 let q = await inquirer.prompt([{
4324 type: "autocomplete",
4325 name: "what",
4326 message: `What asset do you want?`,
4327 source: async (sofar, input) => {
4328 let assets = await this.getAssets(args.env, input);
4329 assets = assets.map(x => new Asset({
4330 data: x,
4331 remote: args.env
4332 }));
4333 return assets.map(x => ({
4334 name: x.chalkPrint(true) + ": " + x.data.links.self.replace("/api/v2/assets/", "/content/"),
4335 value: x
4336 }));
4337 }
4338 }]);
4339 },
4340
4341 async ["@"](args) {
4342 args._.unshift("-");
4343
4344 args._.unshift("make");
4345
4346 return this.supply(args);
4347 },
4348
4349 async test(args) {
4350 let asset = await Asset.getByName("UAT", args.name);
4351 log(asset);
4352 },
4353
4354 //Used to test startup and teardown speed.
4355 noop() {
4356 return true;
4357 }
4358
4359}, (_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));
4360
4361async function unknownCommand(cmd) {
4362 log(chalk`Unknown command {red ${cmd}}.`);
4363}
4364
4365async function noCommand() {
4366 write(chalk`
4367Rally Tools {yellow v${version} (alpha)} CLI
4368by John Schmidt <John_Schmidt@discovery.com>
4369`); //Prompt users to setup one time config.
4370
4371 if (!configObject.hasConfig) {
4372 write(chalk`
4373It looks like you haven't setup the config yet. Please run '{green rally config}'.
4374`);
4375 return;
4376 }
4377
4378 let envs = new Set(["LOCAL", "UAT", "DEV", "PROD", "QA", ...Object.keys(configObject.api)]);
4379 let proms = [];
4380
4381 for (let env of envs) {
4382 proms.push({
4383 env,
4384 prom: rallyFunctions.testAccess(env)
4385 });
4386 } //API Access tests
4387
4388
4389 for (let {
4390 env,
4391 prom
4392 } of proms) {
4393 //Test access. Returns HTTP response code
4394 let resultStr;
4395
4396 try {
4397 let result = await prom; //Create a colored display and response
4398
4399 resultStr = chalk`{yellow ${result} <unknown>}`;
4400 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}`;
4401 } catch (e) {
4402 if (e instanceof UnconfiguredEnvError) {
4403 resultStr = chalk`{yellow Unconfigured}`;
4404 } else if (e instanceof APIError) {
4405 if (!e.response.body) {
4406 resultStr = chalk`{red Timeout (?)}`;
4407 }
4408 } else if (e.name == "RequestError") {
4409 resultStr = chalk`{red Low level error (check internet): ${e.error.errno}}`;
4410 } else {
4411 throw e;
4412 }
4413 }
4414
4415 log(chalk` ${env}: ${resultStr}`);
4416 }
4417}
4418
4419async function $main() {
4420 //Supply --config to load a different config file
4421 if (typeof argv.config === "string") {
4422 loadConfig(argv.config);
4423 } else if (typeof argv.config === "object") {
4424 loadConfigFromArgs(argv);
4425 } else {
4426 loadConfig();
4427 } // First we need to decide if the user wants color or not. If they do want
4428 // color, we need to make sure we use the right mode
4429
4430
4431 chalk.enabled = configObject.hasConfig ? configObject.chalk : true;
4432
4433 if (chalk.level === 0 || !chalk.enabled) {
4434 let force = argv["force-color"];
4435
4436 if (force) {
4437 chalk.enabled = true;
4438
4439 if (force === true && chalk.level === 0) {
4440 chalk.level = 1;
4441 } else if (Number(force)) {
4442 chalk.level = Number(force);
4443 }
4444 }
4445 } //This flag being true allows you to modify UAT and PROD
4446
4447
4448 if (!argv["protect"]) {
4449 configObject.dangerModify = true;
4450 } //This enables raw output for some functions
4451
4452
4453 if (argv["raw"]) {
4454 configObject.rawOutput = true;
4455
4456 global.log = () => {};
4457
4458 global.errorLog = () => {};
4459
4460 global.write = () => {};
4461 }
4462
4463 if (argv["script"]) {
4464 configObject.script = true;
4465 }
4466
4467 if (argv["ignore-missing"]) {
4468 configObject.ignoreMissing = true;
4469 }
4470
4471 if (argv["update-immutable"]) {
4472 configObject.updateImmutable = true;
4473 }
4474
4475 if (argv["skip-header"]) {
4476 configObject.skipHeader = true;
4477 }
4478
4479 configObject.globalProgress = !argv["hide-progress"]; //Default enviornment should normally be from config, but it can be
4480 //overridden by the -e/--env flag
4481
4482 if (configObject.defaultEnv) {
4483 argv.env = argv.env || configObject.defaultEnv;
4484 } //Enable verbose logging in some places.
4485
4486
4487 if (argv["vverbose"]) {
4488 configObject.verbose = argv["vverbose"];
4489 configObject.vverbose = true;
4490 } else if (argv["verbose"]) {
4491 configObject.verbose = argv["verbose"];
4492 } else if (argv["vvverbose"]) {
4493 configObject.verbose = true;
4494 configObject.vverbose = true;
4495 configObject.vvverbose = true;
4496 } //copy argument array to new object to allow modification
4497
4498
4499 argv._old = argv._.slice(); //Take first argument after `node bundle.js`
4500 //If there is no argument, display the default version info and API access.
4501
4502 let func = argv._.shift();
4503
4504 if (func) {
4505 if (!cli[func]) return await unknownCommand(func);
4506
4507 try {
4508 //Call the cli function
4509 let ret = await cli[func](argv);
4510
4511 if (ret) {
4512 write(chalk.white("CLI returned: "));
4513 if (ret instanceof Collection) ret = ret.arr; //Directly use console.log so that --raw works as intended.
4514
4515 if (typeof ret === "object") {
4516 console.log(JSON.stringify(ret, null, 4));
4517 } else {
4518 console.log(ret);
4519 }
4520 }
4521 } catch (e) {
4522 if (e instanceof AbortError) {
4523 log(chalk`{red CLI Aborted}: ${e.message}`);
4524 process.exit(1);
4525 } else {
4526 throw e;
4527 }
4528 }
4529 } else {
4530 await noCommand();
4531 }
4532
4533 process.exit(0);
4534}
4535
4536async function main(...args) {
4537 //Catch all for errors to avoid ugly default node promise catcher
4538 try {
4539 await $main(...args);
4540 } catch (e) {
4541 errorLog(e.stack);
4542 process.exit(1);
4543 }
4544} // If this is an imported module, then we should exec the cli interface.
4545// Oterwise just export everything.
4546
4547
4548if (require.main === module) {
4549 main();
4550} else {
4551 loadConfig();
4552 module.exports = allIndexBundle;
4553}
4554//# sourceMappingURL=bundle.js.map