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