@adinsure-ops/ops-cli 5.0.0-rc5 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOGS.md CHANGED
@@ -3,7 +3,8 @@
3
3
 
4
4
  ## 5.0.0-rc5: 2023-09-08
5
5
 
6
- - Added --description flag to the log command
6
+ - Added --description flag to the log command. It accepts the description of the changelog which will be used
7
+ - Added --force flag to the log command. It creates folder structure if missing
7
8
 
8
9
  ## 5.0.0-rc4: 2023-09-05
9
10
 
package/README.md CHANGED
@@ -47,6 +47,7 @@ You can run and debug commands directly from terminal by running `ops [command]`
47
47
  - [`ops npm:publish`](#ops-npmpublish)
48
48
  - [`ops npm:dependency-update`](#ops-npmdependency-update)
49
49
  - [`ops nuget:dependency-update`](#ops-nugetdependency-update)
50
+ - [`ops prepare-content`](#ops-prepare-content-d-destination-s-source)
50
51
  - [`ops sql <type> <module> [flags]`](#ops-sql-type-module-flags)
51
52
  - [`ops translate`](#ops-translate)
52
53
  - [`ops version <type> [flags]`](#ops-version-type)
@@ -544,6 +545,217 @@ EXAMPLES
544
545
  \\
545
546
  > NPM packages node: For npm package to update properly _wsrun-version_ preset needs to be in _scripts_ portion of _package.json_. (example: `"wsrun-version": "yarn wsrun -p @adinsure-tools/* -p @adinsure/* -c"`)
546
547
 
548
+ ## `ops prepare-content -d <destination> -s [source]`
549
+
550
+ creates a directory/file structure from `/source` to `/destination</source>` as defined in the `.opsprepare` file. Actual
551
+ destination path for each file will be specified destination path with appended relative path from working directory where command is executed to specified source directory or current directory (if source is not specified). Be careful about using
552
+ `..` notation in target and source paths as it may lead to unpredictable results due to relative path resolving.
553
+
554
+ > Introduced in version 3.1.0.
555
+
556
+ ```txt
557
+ USAGE
558
+ $ ops prepare-content -d <destination> -s [source]
559
+
560
+
561
+ OPTIONS
562
+ -d, --destination=destination path to target directory
563
+ -s, --source=source optional path to source directory containing .opsprepare file, (default: current directory)
564
+
565
+ EXAMPLES
566
+ $ ops prepare-content -d ./target_directory/
567
+ copies everything defined in .opsprepare
568
+ from current directory
569
+ to ./target_directory
570
+
571
+ $ ops prepare-content -d ./target_directory/ -s ./source_directory
572
+ copies everything defined in ./source_directory/.opsprepare
573
+ from ./source_directory/
574
+ to ./target_directory/source_directory
575
+
576
+ $ ops prepare-content -d C:\git\target_directory
577
+ copies everything defined in .opsprepare
578
+ from current directory
579
+ to C:\git\target_directory
580
+
581
+ $ ops prepare-content -d C:\git\target_directory -s C:\git\subfolder\subfolder
582
+ when executed in c:\git it copies everything defined in C:\git\subfolder\subfolder\.opsprepare
583
+ from C:\git\subfolder\subfolder
584
+ to C:\git\target_directory\subfolder\subfolder
585
+ ```
586
+
587
+ ### .opsprepare dotfile
588
+
589
+ The prepare-content command uses `.opsprepare` dotfile as instruction set for content preparation.
590
+ The syntax resembles that of `.gitignore` but differs in how it functions and doesn't support advanced
591
+ pattern matching.
592
+
593
+ Following operations/transformations are currently supported:
594
+
595
+ - Copy: copies the file/directory from source path to destination path with same name
596
+ - Transform: transform source file to destination file with same name
597
+ - Removal: remove file/directory (based on source path) from destination path
598
+
599
+ Before diving into the actual syntax and supported tokens let's look at an example
600
+ of how using `ops prepare-content` creates a target directory structure with files
601
+ based on the specified `.opsprepare` file residing in source directory.
602
+
603
+ This is the example source directory structure:
604
+
605
+ ```ascii
606
+ src_data
607
+ ├── .opsprepare
608
+ ├── README.tpl
609
+ ├── bin
610
+ │ ├── ops.sh
611
+ │ └── run
612
+ │ └── ops
613
+ ├── dist
614
+ │ └── server
615
+ │ └── main.cs
616
+ ├── src
617
+ │ ├── commands
618
+ │ │ ├── prepare.ts
619
+ │ │ └── version.ts
620
+ │ ├── config.ts
621
+ │ └── main.ts
622
+ └── transform
623
+ └── render-tpl.js
624
+ ```
625
+
626
+ Contents of `.opsprepare` file in the `src_data` directory:
627
+
628
+ ```txt
629
+ # Comment
630
+ src
631
+ dist/server
632
+ !src/commands
633
+ !src/config.ts
634
+ src/config.ts {cp:/src/configuration.ts}
635
+ # Another comment
636
+ !bin/run
637
+ README.md {js:/transform/render-tpl.js}
638
+ README.tpl {cp:/README.tpl.src}
639
+ ```
640
+
641
+ Running `ops -s src_data -d dst_data` with `src_data/.opsprepare` containing above content
642
+ results in:
643
+
644
+ ```ascii
645
+ dst_data
646
+ └── src_data
647
+ ├── README.md
648
+ ├── README.tpl.src
649
+ ├── dist
650
+ │ └── server
651
+ │ └── main.cs
652
+ └── src
653
+ ├── main.ts
654
+ └── src
655
+ └── configuration.ts
656
+ ```
657
+
658
+ What actually happened:
659
+
660
+ 1. First note that all data was copied into `dst_data/src_data` because `.opsprepare` files relative
661
+ location to `ops` execution directiory is `src_data`.
662
+ 2. `src` directory was copied recursively to destination
663
+ 3. `dist/server` directory was copied recursively including its full path path relative to `src_data`
664
+ 4. `src/config.ts` file was transformed (copied) to `src/configuration.ts` in the destination
665
+ 5. `README.md` file was created at the destination as a result of JavaScript based transformation
666
+ 6. `README.tpl` file was transformed (copied) to `README.tpl.src` in the destination
667
+ 7. `src/commands` directory was removed from destination
668
+ 8. `src/config.ts` file was removed from destination
669
+ 9. `bin/run` directory was removed from destination
670
+
671
+ Note that copying and explicit transformations are performed before any removal operations.
672
+
673
+ #### Comments
674
+
675
+ Any line prefixed by `#` is considered a comment and will not result in any file/directory operation.
676
+
677
+ #### Copy operations
678
+
679
+ Any file/directory not prefixed by `!` or `#` having explicit transformation defined in `{...}` after
680
+ its name will be copied to the destination with it's full path relative to `ops` execution directory
681
+ to the target directory.
682
+
683
+ #### Transformation operations
684
+
685
+ Transformation operations are always specified using curly braces following the source file name. Base
686
+ syntax looks like this:
687
+
688
+ ```txt
689
+ {<transformation type>:<transformation parameters>}
690
+ ```
691
+
692
+ Two types of transformation operations are supported:
693
+
694
+ - cp: copy transformation which supports renaming of the source file at the destination
695
+ - js: JavasSript based transformation which results in the file containing string returned by the
696
+ transformation function contained in transformation module.
697
+
698
+ **cp - copy transformation syntax**
699
+
700
+ ```txt
701
+ {cp:<destination path>}
702
+
703
+ Examples:
704
+ {cp:/dst/newfilename}
705
+ {cp:dst/newfilenames}
706
+ ```
707
+
708
+ Copy transformation is supported for both files and directories. Source file or directory will be
709
+ copied to the destination path with its new name. The destination will always be relative to the
710
+ destination path of entire `prepare-content` operation therefore paths in the transformation block
711
+ can start with or without the `/` character but using makes the syntax more readable.
712
+
713
+ **js - JavaScript transformation syntax**
714
+
715
+ ```txt
716
+ {js:<transformation_module_path>}
717
+
718
+ Example:
719
+ {js:/transformations/templateRenderer.js}
720
+ ```
721
+
722
+ JavaScript transformation is supported only for files, not directories.
723
+
724
+ Transformation module path should be relative to `.opsprepare` file but can start with `/` character
725
+ for better readability. It can export multiple functions though only one can be used in single
726
+ transformation specification.
727
+
728
+ Transformation function that is defined in the transformation module will be called with a single
729
+ parameter - absolute path of the source file.
730
+
731
+ Transformation function MUST return a string. This string will become the content of the file on
732
+ destination.
733
+
734
+ Destination file will be created with the same name as source file and content returned by the
735
+ transformation function. The existence of actual source file will not be validated by the
736
+ `prepare-content` operation (warning is shown if it doesn't exist), such validation is responsibility
737
+ of the transformation function if required.
738
+
739
+ Example transformation module `transform.js` with exported function called render:
740
+
741
+ ```js
742
+ export default function render(path) {
743
+ /*
744
+ Some transformation happens in here and it's result which is
745
+ a string is assigned to result variable which is returned.
746
+ */
747
+ return result;
748
+ }
749
+ ```
750
+
751
+ > An important thing to note is that you can only export one default function in a module, so defining multiple functions in one file that call each other is possible,
752
+ > however defining multiple default exports is not and therefore **there can only be one function that will be called** in a module file.
753
+
754
+ #### Removal operations
755
+
756
+ Any file/directory prefixed by `!` will be removed from the destination based on it's full path
757
+ relative from `ops` execution directory to source file/directory path.
758
+
547
759
  ## `ops sql <type> <module> [flags]`
548
760
 
549
761
  creates a new database script
@@ -10,6 +10,7 @@ export default class DownloadStudio extends CommandBase<typeof DownloadStudio> {
10
10
  install: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
11
  skip: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
12
12
  keep: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
13
+ force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
13
14
  };
14
15
  private readonly url;
15
16
  /**
@@ -48,6 +48,9 @@ class DownloadStudio extends CommandBase {
48
48
  return __awaiter(this, void 0, void 0, function* () {
49
49
  const { flags } = yield this.parse(DownloadStudio);
50
50
  const basePath = flags.output;
51
+ if (flags.force && !(yield fs.pathExists(basePath))) {
52
+ yield fs.mkdirp(basePath);
53
+ }
51
54
  const filename = `adinsure-studio-${flags.version}.vsix`;
52
55
  const studioVersion = `adacta-fintech.adinsure-studio@${flags.version}`;
53
56
  const filePath = path.resolve(basePath, filename);
@@ -107,6 +110,7 @@ DownloadStudio.description = 'download AdInsure Studio vsix from azure storage';
107
110
  DownloadStudio.usage = 'download:studio <flags>';
108
111
  DownloadStudio.examples = [
109
112
  `$ ops download:studio -v 15.0.0 -o ~/Downloads`,
113
+ `$ ops download:studio -v 15.0.0 -o ~/Downloads -f`,
110
114
  `$ ops download:studio -v 15.0.0 -i`,
111
115
  `$ ops download:studio -v 15.0.0 -i -s`,
112
116
  `$ ops download:studio -v 15.0.0 -i -k`,
@@ -137,5 +141,11 @@ DownloadStudio.flags = {
137
141
  description: 'only with install. If passed, it will not remove the vsix file.',
138
142
  dependsOn: ['install'],
139
143
  }),
144
+ force: Flags.boolean({
145
+ char: 'f',
146
+ description: 'Create output directory if it does not exist.',
147
+ default: false,
148
+ dependsOn: ['output'],
149
+ }),
140
150
  };
141
151
  export default DownloadStudio;
@@ -11,6 +11,7 @@ export default class Changelog extends CommandBase<typeof Changelog> {
11
11
  type: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
12
  component: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
13
13
  description: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
14
+ force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
14
15
  };
15
16
  run(): Promise<void>;
16
17
  }
@@ -12,6 +12,9 @@ class Changelog extends CommandBase {
12
12
  // Validation of input parameters
13
13
  const changelogFolderBase = yield this.getChangelogsRoot();
14
14
  const possibleComponents = [];
15
+ if (flags.force && !(yield fs.pathExists(changelogFolderBase))) {
16
+ yield fs.mkdirp(changelogFolderBase);
17
+ }
15
18
  for (const file of fs.readdirSync(changelogFolderBase)) {
16
19
  if (fs.statSync(path.resolve(changelogFolderBase, file)).isDirectory()) {
17
20
  possibleComponents.push(file);
@@ -88,6 +91,7 @@ Changelog.usage = 'log <issue> -t [type] -c [options]';
88
91
  Changelog.examples = [
89
92
  `$ ops log LJADIRD-1234 -t feature`,
90
93
  `$ ops log LJADIRD-5678 -t fixed`,
94
+ `$ ops log LJADIRD-5678 -t fixed -d "Fixed my bug"`,
91
95
  `$ ops log LJADIRD-8765 -t breaking -c Platform`,
92
96
  `$ ops log LJADIRD-4321 -t improve -c Configuration`,
93
97
  ];
@@ -113,5 +117,10 @@ Changelog.flags = {
113
117
  description: 'Description of a changelog',
114
118
  default: '',
115
119
  }),
120
+ force: Flags.boolean({
121
+ char: 'f',
122
+ description: 'Create logs directory if it does not exist',
123
+ default: false,
124
+ }),
116
125
  };
117
126
  export default Changelog;
@@ -0,0 +1,28 @@
1
+ import CommandBase from '../command.base.js';
2
+ interface Transformation {
3
+ name: string;
4
+ srcPath: string;
5
+ transform(): void;
6
+ }
7
+ interface DotfileLine {
8
+ prefix: string;
9
+ source: string;
10
+ transformData: string;
11
+ }
12
+ export default class PrepareContent extends CommandBase<typeof PrepareContent> {
13
+ static readonly dotfile = ".opsprepare";
14
+ static description: string;
15
+ static usage: string;
16
+ static examples: string[];
17
+ static flags: {
18
+ destination: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
19
+ source: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
20
+ };
21
+ parseDotfileLine(line: string, sourceDirectory: string): Promise<DotfileLine>;
22
+ readDotfile(sourceDirectory: string): Promise<DotfileLine[]>;
23
+ getTransformation(lineData: DotfileLine, baseSourcedir: string, baseDestinationDir: string): Promise<Transformation>;
24
+ prepareTransformations(linesData: DotfileLine[], baseSourceDir: string, baseDestinationDir: string): Promise<Transformation[]>;
25
+ runTransformations(transformations: Transformation[]): Promise<void>;
26
+ run(): Promise<void>;
27
+ }
28
+ export {};
@@ -0,0 +1,411 @@
1
+ import { __awaiter } from "tslib";
2
+ import { Flags, ux } from '@oclif/core';
3
+ import chalk from 'chalk';
4
+ import fs from 'fs-extra';
5
+ import path from 'path';
6
+ import CommandBase from '../command.base.js';
7
+ class CopyTransformation {
8
+ constructor(srcPath, dstPath) {
9
+ this.name = this.constructor.name;
10
+ this.srcPath = srcPath;
11
+ this.dstPath = dstPath;
12
+ }
13
+ transform() {
14
+ try {
15
+ fs.accessSync(this.srcPath, fs.constants.R_OK);
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ }
18
+ catch (error_) {
19
+ const error = error_.code === 'ENOENT' ? new Error(`${this.name}: source file does not exist <${this.srcPath}>`) : new Error(`${this.name}: ${error_} <${this.srcPath}>`);
20
+ throw error;
21
+ }
22
+ ux.log(`Copying from ${this.srcPath} to ${this.dstPath}`);
23
+ try {
24
+ fs.copySync(this.srcPath, this.dstPath, { overwrite: true });
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ }
27
+ catch (error) {
28
+ throw new Error(`${this.name}: ${error} <${this.srcPath}>`);
29
+ }
30
+ }
31
+ }
32
+ class JavascriptTransformation {
33
+ constructor(srcPath, dstPath, javascriptFile) {
34
+ this.name = this.constructor.name;
35
+ this.srcPath = srcPath;
36
+ this.dstPath = dstPath;
37
+ this.jsFile = javascriptFile;
38
+ }
39
+ importTransformationFunction() {
40
+ return __awaiter(this, void 0, void 0, function* () {
41
+ try {
42
+ const { default: transformationFunction } = yield import(this.jsFile);
43
+ return transformationFunction;
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ }
46
+ catch (error) {
47
+ if (error.code === 'ERR_MODULE_NOT_FOUND') {
48
+ throw new Error(`${this.name}: module file ${this.jsFile} not found <${this.srcPath}>`);
49
+ }
50
+ else if (error instanceof SyntaxError) {
51
+ throw new SyntaxError(`${this.name}: ${error.message} in '${this.jsFile}' <${this.srcPath}>`);
52
+ }
53
+ throw new Error(`${this.name}: ${error} <${this.srcPath}>`);
54
+ }
55
+ });
56
+ }
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ executeTransformationFunction(transformationFunction) {
59
+ return __awaiter(this, void 0, void 0, function* () {
60
+ try {
61
+ const result = yield transformationFunction(this.srcPath);
62
+ if (typeof result !== 'string') {
63
+ throw new TypeError(`${this.name}: transformation functions must return string, the script returned ${typeof (result)} <${this.srcPath}>`);
64
+ }
65
+ return result;
66
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
+ }
68
+ catch (error) {
69
+ if (error instanceof TypeError && error.message.search(`/.*must return.*/`) !== -1) {
70
+ console.log(error.message.search(`/.*must return.*/`));
71
+ throw new Error(`${this.name}: invalid function in ${this.jsFile} <${this.srcPath}>`);
72
+ }
73
+ /*
74
+ Rethrow anything errors that were thrown in the above try block
75
+ */
76
+ if (error.message && error.message.search(`${this.name}`.length > 0)) {
77
+ throw error;
78
+ }
79
+ throw new Error(`${this.name}: ${error} <${this.srcPath}>`);
80
+ }
81
+ });
82
+ }
83
+ transform() {
84
+ return __awaiter(this, void 0, void 0, function* () {
85
+ // Check if source exists, if not, issue a warning
86
+ try {
87
+ fs.accessSync(this.srcPath);
88
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
+ }
90
+ catch (error) {
91
+ if (error.code === 'ENOENT') {
92
+ const errMsg = `${this.name}: source does not exist, relying on transformation to find proper source file <${this.srcPath}>`;
93
+ ux.log(`${chalk.yellow(errMsg)}`);
94
+ }
95
+ else {
96
+ throw new Error(`${this.name}: ${error} <${this.srcPath}>`);
97
+ }
98
+ }
99
+ const msg = `Transforming from ${this.srcPath} to ${this.dstPath} using ${this.jsFile}`;
100
+ ux.log(`${chalk.yellow(msg)}`);
101
+ // eslint-disable-next-line no-useless-catch
102
+ try {
103
+ // Import transformation function
104
+ const transFunction = yield this.importTransformationFunction();
105
+ // Execute transformation function
106
+ const transResult = yield this.executeTransformationFunction(transFunction);
107
+ fs.outputFileSync(this.dstPath, transResult, { encoding: 'utf8', mode: 0o644, flag: 'w' });
108
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
+ }
110
+ catch (error) {
111
+ throw error;
112
+ }
113
+ });
114
+ }
115
+ }
116
+ class RemovalTransformation {
117
+ constructor(srcPath) {
118
+ this.name = this.constructor.name;
119
+ this.srcPath = srcPath;
120
+ }
121
+ transform() {
122
+ try {
123
+ fs.accessSync(this.srcPath, fs.constants.W_OK);
124
+ ux.log(`Removing ${this.srcPath}.`);
125
+ fs.removeSync(this.srcPath);
126
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
+ }
128
+ catch (error) {
129
+ if (error.code === 'ENOENT') {
130
+ const errMsg = `${this.name}: target does not exist, skipping removal <${this.srcPath}>`;
131
+ ux.log(`${chalk.yellow(errMsg)}`);
132
+ }
133
+ else {
134
+ throw new Error(`${this.name}: ${error} <${this.srcPath}>`);
135
+ }
136
+ }
137
+ }
138
+ }
139
+ class PrepareContent extends CommandBase {
140
+ parseDotfileLine(line, sourceDirectory) {
141
+ return __awaiter(this, void 0, void 0, function* () {
142
+ const m = line.match('^([!#])?(.+?)({.*?})?$');
143
+ const dotfileLine = {
144
+ prefix: m[1],
145
+ source: path.join(sourceDirectory, path.normalize(m[2].trim())),
146
+ transformData: m[3],
147
+ };
148
+ /*
149
+ Sanity check on source file name. Should not contain "{", ":", or "}" characters
150
+ */
151
+ const vm = dotfileLine.source.match('[{:}]+');
152
+ if (vm !== null) {
153
+ throw new SyntaxError(`<${PrepareContent.dotfile}> source file name '${dotfileLine.source}' contains invalid character '${vm[0]}'`);
154
+ }
155
+ return dotfileLine;
156
+ });
157
+ }
158
+ readDotfile(sourceDirectory) {
159
+ return __awaiter(this, void 0, void 0, function* () {
160
+ let fileContent;
161
+ try {
162
+ fileContent = fs.readFileSync(path.join(sourceDirectory, PrepareContent.dotfile), 'utf8');
163
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
164
+ }
165
+ catch (error) {
166
+ if (error.code === 'ENOENT') {
167
+ const errMsg = `.opsprepare file not found in ${path.resolve(sourceDirectory)}`;
168
+ throw new Error(errMsg);
169
+ }
170
+ throw new Error(error.message);
171
+ }
172
+ const lines = fileContent.split(/\r?\n/).filter(line => line.length > 0);
173
+ const lineParse = lines.map(line => {
174
+ return this.parseDotfileLine(line.trim(), sourceDirectory);
175
+ });
176
+ const parsedLines = Promise.all(lineParse).then(lines => lines.filter(line => line.prefix !== '#'));
177
+ return parsedLines;
178
+ });
179
+ }
180
+ getTransformation(lineData, baseSourcedir, baseDestinationDir) {
181
+ return __awaiter(this, void 0, void 0, function* () {
182
+ const targetPath = path.join(baseDestinationDir, lineData.source);
183
+ /*
184
+ Anything prefixed with ! is a removal
185
+ */
186
+ if (lineData.prefix === '!') {
187
+ return new RemovalTransformation(targetPath);
188
+ }
189
+ /*
190
+ In case transformation data is present, prepare transformations based on requested plugin.
191
+ */
192
+ if (lineData.transformData) {
193
+ const transformType = lineData.transformData.match('^{(.+?):.*}$')[1];
194
+ switch (transformType) {
195
+ case 'cp': {
196
+ const mPath = lineData.transformData.match('^{cp:([^:]+)}$');
197
+ if (!mPath) {
198
+ throw new SyntaxError(`<${PrepareContent.dotfile}> syntax '${lineData.transformData}' is invalid for 'cp' transformation type`);
199
+ }
200
+ const tPath = path.join(baseDestinationDir, path.dirname(lineData.source), mPath[1]);
201
+ return new CopyTransformation(lineData.source, tPath);
202
+ }
203
+ case 'js': {
204
+ /* TODO: Some sanity checking will be required after regex matching
205
+ to make sure everything was matched and is defined
206
+ */
207
+ // Parse transformation data
208
+ const mData = lineData.transformData.match('^{js:(.+?).js}$');
209
+ if (!mData) {
210
+ throw new SyntaxError(`<${PrepareContent.dotfile}> syntax '${lineData.transformData}' is invalid for 'js' transformation type`);
211
+ }
212
+ const jsFile = 'file://' + path.resolve(path.join(baseSourcedir, mData[1])) + '.js';
213
+ // File path absolute
214
+ const filePath = path.resolve(lineData.source);
215
+ return new JavascriptTransformation(filePath, targetPath, jsFile);
216
+ }
217
+ default: {
218
+ const p = lineData.prefix || '';
219
+ const s = lineData.source;
220
+ const t = lineData.transformData || '';
221
+ this.error(`No valid transformation can be extrapolated from ${p}${s}${t}`);
222
+ }
223
+ }
224
+ }
225
+ /*
226
+ Return default transformation
227
+ */
228
+ return new CopyTransformation(lineData.source, targetPath);
229
+ });
230
+ }
231
+ prepareTransformations(linesData, baseSourceDir, baseDestinationDir) {
232
+ return __awaiter(this, void 0, void 0, function* () {
233
+ const transformations = linesData.map(line => {
234
+ return this.getTransformation(line, baseSourceDir, baseDestinationDir);
235
+ });
236
+ return Promise.all(transformations);
237
+ });
238
+ }
239
+ /*
240
+ This function divides transformations into execution groups based on their class
241
+ and runs transformations for each group
242
+ */
243
+ runTransformations(transformations) {
244
+ return __awaiter(this, void 0, void 0, function* () {
245
+ const copyTransformList = [];
246
+ const modlueTransformList = [];
247
+ const excludeList = [];
248
+ // Sort transformation into two lists
249
+ for (const t of transformations) {
250
+ if (t instanceof CopyTransformation) {
251
+ copyTransformList.push(t);
252
+ }
253
+ else if (t instanceof RemovalTransformation) {
254
+ excludeList.push(t);
255
+ }
256
+ else {
257
+ modlueTransformList.push(t);
258
+ }
259
+ }
260
+ const t = (transformation) => __awaiter(this, void 0, void 0, function* () {
261
+ try {
262
+ transformation.transform();
263
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
264
+ }
265
+ catch (error) {
266
+ throw Promise.reject(error.message).catch(error_ => {
267
+ this.log(`${chalk.redBright('ERROR: ' + error_)}`);
268
+ });
269
+ }
270
+ });
271
+ /* eslint-disable unicorn/no-array-callback-reference */
272
+ const doCopyTransform = copyTransformList.map(t);
273
+ const doModuleTransform = modlueTransformList.map(t);
274
+ const doExclude = excludeList.map(t);
275
+ /* eslint-enable unicorn/no-array-callback-reference */
276
+ /* Copy first, then transform and finally remove files. This should
277
+ ensure that transformations can overwrite previous copies. Removals
278
+ can still erase transformations though. If needed, we can remove
279
+ before transforming.
280
+ */
281
+ yield Promise.all(doCopyTransform);
282
+ yield Promise.all(doModuleTransform);
283
+ yield Promise.all(doExclude);
284
+ });
285
+ }
286
+ /*
287
+ Validates file system on source repository by checking if files or directories exist
288
+ */
289
+ /*
290
+ async validate(arr: string[][], source: string): Promise<boolean> {
291
+ //this.log(`${chalk.blue('Validating if files and directories exist.')}`);
292
+ cli.action.start(`${chalk.blue('Validating if files and directories exist')}`, '', {stdout: true});
293
+ for (let a of arr[0]) {
294
+ let element_path = path.join(source, a);
295
+ if(!(fs.existsSync(path.join(element_path)))) {
296
+ this.log(`${chalk.red('File or directory: ' + path.join(element_path) + ' could not be found')}`);
297
+ return false;
298
+ }
299
+ }
300
+ for (let a of arr[1]) {
301
+ let element_path = path.join(source, a);
302
+ if(!(fs.existsSync(element_path))) {
303
+ this.log(`${chalk.red('File or directory: ' + element_path + ' could not be found')}`);
304
+ return false;
305
+ }
306
+ }
307
+
308
+ //this.log(`${chalk.green('Validation done.')}`);
309
+ cli.action.stop(`${chalk.green('done')}`);
310
+ return true;
311
+ }
312
+ */
313
+ /*
314
+ This function copies elements and removes excluded ones.
315
+ */
316
+ /*
317
+ async copyWorkspace(arr: string[][], dest: string, source: string): Promise<any> {
318
+ if (await this.validate(arr, source)) {
319
+ cli.action.start(`${chalk.blue('Preparing content')}`, '', {stdout: true});
320
+ arr[0].forEach(a => {
321
+ this.log('Copying: ', path.join(source, a));
322
+ fs.copySync(path.join(source, a), path.join(dest, a), {overwrite: true});
323
+ });
324
+ this.log(`${chalk.green('Copying done.')}`);
325
+ this.log(`${chalk.blue('Removing excluded elements.')}`);
326
+ arr[1].forEach(a => {
327
+ let curPath = path.join(dest, a);
328
+ if (fs.pathExistsSync(curPath)) {
329
+ if (fs.lstatSync(curPath).isFile()) {
330
+ try {
331
+ fs.removeSync(curPath);
332
+ this.log('Removing file: ', curPath);
333
+ } catch (err: any) {
334
+ cli.action.stop(`${chalk.red('failed')}`);
335
+ this.error(error.message);
336
+ }
337
+ } else if (fs.lstatSync(curPath).isDirectory()) {
338
+ try {
339
+ fs.removeSync(curPath);
340
+ this.log('Removing dir: ', curPath);
341
+ } catch (err: any) {
342
+ cli.action.stop(`${chalk.red('failed')}`);
343
+ this.error(error.message);
344
+ }
345
+ } else {
346
+ cli.action.stop(`${chalk.red('error')}`);
347
+ this.error('Type not file or directory');
348
+ }
349
+ }
350
+ });
351
+ this.log(`${chalk.green('Removing done.')}`);
352
+ cli.action.stop(`${chalk.green('done')}`);
353
+ } else {
354
+ cli.action.stop(`${chalk.red('error')}`);
355
+ this.error('Content validation failed.');
356
+ }
357
+ }
358
+ */
359
+ run() {
360
+ return __awaiter(this, void 0, void 0, function* () {
361
+ const { flags } = yield this.parse(PrepareContent);
362
+ // read the flags and set paths relative to execution directory
363
+ let source = path.relative(process.cwd(), flags.source);
364
+ source = source || '.';
365
+ console.log(source);
366
+ const dest = flags.destination;
367
+ try {
368
+ const dotfileLines = yield this.readDotfile(source);
369
+ this.log(`${chalk.blue('Content transformation started.')}`);
370
+ const transformations = yield this.prepareTransformations(dotfileLines, source, dest);
371
+ yield this.runTransformations(transformations);
372
+ this.log(`${chalk.green('Content prepared at: ', dest)}`);
373
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
374
+ }
375
+ catch (error) {
376
+ this.log(`${chalk.red(error)}`);
377
+ /*
378
+ In test mode, exit with status 0
379
+ */
380
+ if (process.env.OPS_TEST_MODE === 'true') {
381
+ this.exit(0);
382
+ }
383
+ console.log(error.message);
384
+ this.exit(1);
385
+ }
386
+ });
387
+ }
388
+ }
389
+ PrepareContent.dotfile = '.opsprepare';
390
+ PrepareContent.description = `copies files as described in ${PrepareContent.dotfile} which is at root of source directory`;
391
+ PrepareContent.usage = 'prepare-content -d <destination> [-s <source>]';
392
+ PrepareContent.examples = [
393
+ `$ ops prepare-content -d .\\my_workspace`,
394
+ `$ ops prepare-content -d .\\my_workspace -s .\\my_repository`,
395
+ `$ ops prepare-content -d C:\\git\\my_workspace`,
396
+ `$ ops prepare-content -d C:\\git\\my_workspace -s C:\\git\\my_repository\\my_repository`,
397
+ ];
398
+ PrepareContent.flags = {
399
+ destination: Flags.string({
400
+ char: 'd',
401
+ description: 'where to copy the workspace files',
402
+ required: true,
403
+ }),
404
+ source: Flags.string({
405
+ char: 's',
406
+ description: '[optional] source directory, default: current dir',
407
+ required: false,
408
+ default: '.',
409
+ }),
410
+ };
411
+ export default PrepareContent;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "5.0.0-rc5",
2
+ "version": "5.0.0",
3
3
  "commands": {
4
4
  "doctor": {
5
5
  "id": "doctor",
@@ -88,6 +88,43 @@
88
88
  "args": {},
89
89
  "responsePageContent": "\n<!DOCTYPE html>\n<html>\n <head>\n <title>ops-cli authenticate</title>\n <link rel='icon' href='data:;base64,iVBORw0KGgo='>\n </head>\n <body>\n <p><strong>Your login was successful!</strong></p>\n <p>This page will automatically close after 10 seconds OR you can close it yourself immediately.</p>\n <script type='text/javascript'>\n setTimeout('self.close()', 10000);\n </script>\n </body>\n</html>"
90
90
  },
91
+ "prepare-content": {
92
+ "id": "prepare-content",
93
+ "description": "copies files as described in .opsprepare which is at root of source directory",
94
+ "strict": true,
95
+ "usage": "prepare-content -d <destination> [-s <source>]",
96
+ "pluginName": "@adinsure-ops/ops-cli",
97
+ "pluginAlias": "@adinsure-ops/ops-cli",
98
+ "pluginType": "core",
99
+ "aliases": [],
100
+ "examples": [
101
+ "$ ops prepare-content -d .\\my_workspace",
102
+ "$ ops prepare-content -d .\\my_workspace -s .\\my_repository",
103
+ "$ ops prepare-content -d C:\\git\\my_workspace",
104
+ "$ ops prepare-content -d C:\\git\\my_workspace -s C:\\git\\my_repository\\my_repository"
105
+ ],
106
+ "flags": {
107
+ "destination": {
108
+ "name": "destination",
109
+ "type": "option",
110
+ "char": "d",
111
+ "description": "where to copy the workspace files",
112
+ "required": true,
113
+ "multiple": false
114
+ },
115
+ "source": {
116
+ "name": "source",
117
+ "type": "option",
118
+ "char": "s",
119
+ "description": "[optional] source directory, default: current dir",
120
+ "required": false,
121
+ "multiple": false,
122
+ "default": "."
123
+ }
124
+ },
125
+ "args": {},
126
+ "dotfile": ".opsprepare"
127
+ },
91
128
  "sql": {
92
129
  "id": "sql",
93
130
  "description": "creates a new database script",
@@ -633,6 +670,7 @@
633
670
  "aliases": [],
634
671
  "examples": [
635
672
  "$ ops download:studio -v 15.0.0 -o ~/Downloads",
673
+ "$ ops download:studio -v 15.0.0 -o ~/Downloads -f",
636
674
  "$ ops download:studio -v 15.0.0 -i",
637
675
  "$ ops download:studio -v 15.0.0 -i -s",
638
676
  "$ ops download:studio -v 15.0.0 -i -k"
@@ -680,6 +718,16 @@
680
718
  "dependsOn": [
681
719
  "install"
682
720
  ]
721
+ },
722
+ "force": {
723
+ "name": "force",
724
+ "type": "boolean",
725
+ "char": "f",
726
+ "description": "Create output directory if it does not exist.",
727
+ "allowNo": false,
728
+ "dependsOn": [
729
+ "output"
730
+ ]
683
731
  }
684
732
  },
685
733
  "args": {}
@@ -928,6 +976,7 @@
928
976
  "examples": [
929
977
  "$ ops log LJADIRD-1234 -t feature",
930
978
  "$ ops log LJADIRD-5678 -t fixed",
979
+ "$ ops log LJADIRD-5678 -t fixed -d \"Fixed my bug\"",
931
980
  "$ ops log LJADIRD-8765 -t breaking -c Platform",
932
981
  "$ ops log LJADIRD-4321 -t improve -c Configuration"
933
982
  ],
@@ -960,6 +1009,13 @@
960
1009
  "description": "Description of a changelog",
961
1010
  "multiple": false,
962
1011
  "default": ""
1012
+ },
1013
+ "force": {
1014
+ "name": "force",
1015
+ "type": "boolean",
1016
+ "char": "f",
1017
+ "description": "Create logs directory if it does not exist",
1018
+ "allowNo": false
963
1019
  }
964
1020
  },
965
1021
  "args": {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@adinsure-ops/ops-cli",
3
3
  "description": "Operations CLI for working with AdInsure",
4
- "version": "5.0.0-rc5",
4
+ "version": "5.0.0",
5
5
  "author": "Adacta Digital Service Department",
6
6
  "bin": {
7
7
  "ops": "./bin/run.js"
@@ -64,7 +64,7 @@
64
64
  "mock-spawn": "^0.2.6",
65
65
  "nock": "^13.1.3",
66
66
  "nyc": "^15.1.0",
67
- "oclif": "^3.11.3",
67
+ "oclif": "^3.15.0",
68
68
  "sinon": "^15.1.0",
69
69
  "timekeeper": "^2.2.0",
70
70
  "ts-mockito": "^2.6.1",