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