@adinsure-ops/ops-cli 4.0.0-rc6
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 +55 -0
- package/LICENSE +1 -0
- package/README.md +576 -0
- package/bin/run +5 -0
- package/bin/run.cmd +3 -0
- package/lib/command.base.d.ts +17 -0
- package/lib/command.base.jira.d.ts +11 -0
- package/lib/command.base.jira.js +37 -0
- package/lib/command.base.js +73 -0
- package/lib/commands/doctor.d.ts +54 -0
- package/lib/commands/doctor.js +311 -0
- package/lib/commands/download/client.d.ts +18 -0
- package/lib/commands/download/client.js +72 -0
- package/lib/commands/download/identity-server.d.ts +18 -0
- package/lib/commands/download/identity-server.js +72 -0
- package/lib/commands/download/platform.d.ts +18 -0
- package/lib/commands/download/platform.js +72 -0
- package/lib/commands/download/scheduler.d.ts +18 -0
- package/lib/commands/download/scheduler.js +72 -0
- package/lib/commands/download/server.d.ts +18 -0
- package/lib/commands/download/server.js +72 -0
- package/lib/commands/download/studio.d.ts +20 -0
- package/lib/commands/download/studio.js +89 -0
- package/lib/commands/gitlab/next-nightly-tag.d.ts +30 -0
- package/lib/commands/gitlab/next-nightly-tag.js +171 -0
- package/lib/commands/jira/issue.d.ts +17 -0
- package/lib/commands/jira/issue.js +31 -0
- package/lib/commands/jira/labels.d.ts +22 -0
- package/lib/commands/jira/labels.js +70 -0
- package/lib/commands/log/add.d.ts +17 -0
- package/lib/commands/log/add.js +107 -0
- package/lib/commands/log/generate.d.ts +22 -0
- package/lib/commands/log/generate.js +155 -0
- package/lib/commands/log/validate.d.ts +11 -0
- package/lib/commands/log/validate.js +58 -0
- package/lib/commands/login.d.ts +23 -0
- package/lib/commands/login.js +144 -0
- package/lib/commands/npm/check.publish.d.ts +17 -0
- package/lib/commands/npm/check.publish.js +78 -0
- package/lib/commands/npm/publish.d.ts +23 -0
- package/lib/commands/npm/publish.js +163 -0
- package/lib/commands/prepare-content.d.ts +29 -0
- package/lib/commands/prepare-content.js +386 -0
- package/lib/commands/sql.d.ts +24 -0
- package/lib/commands/sql.js +147 -0
- package/lib/commands/translate.d.ts +37 -0
- package/lib/commands/translate.js +149 -0
- package/lib/commands/version.d.ts +48 -0
- package/lib/commands/version.js +267 -0
- package/lib/config.d.ts +7 -0
- package/lib/config.js +9 -0
- package/lib/hooks/init/version-override.d.ts +2 -0
- package/lib/hooks/init/version-override.js +15 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +4 -0
- package/lib/lib/command.d.ts +9 -0
- package/lib/lib/command.js +71 -0
- package/lib/lib/npm.d.ts +15 -0
- package/lib/lib/npm.js +79 -0
- package/lib/lib/npmrc.d.ts +17 -0
- package/lib/lib/npmrc.js +44 -0
- package/lib/security/cache.d.ts +7 -0
- package/lib/security/cache.js +33 -0
- package/lib/security/context.d.ts +58 -0
- package/lib/security/context.js +224 -0
- package/lib/types/changelog.types.d.ts +6 -0
- package/lib/types/changelog.types.js +2 -0
- package/lib/types/npm.types.d.ts +34 -0
- package/lib/types/npm.types.js +2 -0
- package/oclif.manifest.json +1 -0
- package/package.json +118 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const command_1 = require("@oclif/command");
|
|
4
|
+
const chalk_1 = require("chalk");
|
|
5
|
+
const cli_ux_1 = require("cli-ux");
|
|
6
|
+
const command_base_1 = require("../../command.base");
|
|
7
|
+
const npm_1 = require("../../lib/npm");
|
|
8
|
+
class NpmCheckPublish extends command_base_1.default {
|
|
9
|
+
async checkPackagesExists(packages, allPackages, version) {
|
|
10
|
+
let errorCount = 0;
|
|
11
|
+
for (const pkg of packages) {
|
|
12
|
+
cli_ux_1.default.action.start(`${pkg.name}@${version}`, undefined, { stdout: true });
|
|
13
|
+
let matchedPackage = allPackages.value.find(azurePackage => azurePackage.name === pkg.name);
|
|
14
|
+
if (!matchedPackage) {
|
|
15
|
+
cli_ux_1.default.action.stop(`${chalk_1.default.red('package missing')}`);
|
|
16
|
+
errorCount++;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
let matchVersion = matchedPackage.versions.find(azurePackageVersion => azurePackageVersion.version === version);
|
|
20
|
+
if (!matchVersion) {
|
|
21
|
+
cli_ux_1.default.action.stop(`${chalk_1.default.red('version missing')}`);
|
|
22
|
+
errorCount++;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
cli_ux_1.default.action.stop(`${chalk_1.default.green('exists')}`);
|
|
26
|
+
}
|
|
27
|
+
if (errorCount > 0) {
|
|
28
|
+
throw new Error(`${errorCount} packages or versions missing!`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async run() {
|
|
32
|
+
try {
|
|
33
|
+
const { flags } = this.parse(NpmCheckPublish);
|
|
34
|
+
let npmContext = new npm_1.default();
|
|
35
|
+
cli_ux_1.default.action.start('Parsing packages to check', undefined, { stdout: true });
|
|
36
|
+
const packages = await npmContext.packageList(flags.package, flags.exclude, this);
|
|
37
|
+
cli_ux_1.default.action.stop(`${chalk_1.default.green('done')}`);
|
|
38
|
+
cli_ux_1.default.action.start('Getting list of available packages', undefined, { stdout: true });
|
|
39
|
+
const allPackages = JSON.parse(await npmContext.getAllNpmPackages(flags.pat));
|
|
40
|
+
cli_ux_1.default.action.stop(`${chalk_1.default.green('done')}`);
|
|
41
|
+
await this.checkPackagesExists(packages, allPackages, flags.version);
|
|
42
|
+
cli_ux_1.default.action.stop('success');
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
cli_ux_1.default.action.stop('error');
|
|
46
|
+
this.error(`${err.message}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.default = NpmCheckPublish;
|
|
51
|
+
NpmCheckPublish.aliases = ['npm:check-publish'];
|
|
52
|
+
NpmCheckPublish.description = 'check if npm packages was published';
|
|
53
|
+
NpmCheckPublish.usage = 'npm:check-publish [options]';
|
|
54
|
+
NpmCheckPublish.examples = [
|
|
55
|
+
'$ ops npm:check-publish -v 6.0.1',
|
|
56
|
+
'$ ops npm:check-publish -p @adinsure/* -v 6.0.0',
|
|
57
|
+
'$ ops npm:check-publish -p @adinsure/* -x @adisure/demo -v 6.0.0'
|
|
58
|
+
];
|
|
59
|
+
NpmCheckPublish.flags = {
|
|
60
|
+
package: command_1.flags.string({
|
|
61
|
+
char: 'p',
|
|
62
|
+
description: 'package name filter you want to check',
|
|
63
|
+
multiple: true
|
|
64
|
+
}),
|
|
65
|
+
exclude: command_1.flags.string({
|
|
66
|
+
char: 'x',
|
|
67
|
+
description: 'exclude packages from filter',
|
|
68
|
+
multiple: true
|
|
69
|
+
}),
|
|
70
|
+
version: command_1.flags.string({
|
|
71
|
+
char: 'v',
|
|
72
|
+
description: 'version we want to check if published',
|
|
73
|
+
required: true
|
|
74
|
+
}),
|
|
75
|
+
pat: command_1.flags.string({
|
|
76
|
+
description: 'PAT token or AZURE_DEVOPS_EXT_PAT environment variable'
|
|
77
|
+
}),
|
|
78
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { flags } from '@oclif/command';
|
|
2
|
+
import CommandBase from '../../command.base';
|
|
3
|
+
import { AzurePackageArray, NpmPackage } from '../../types/npm.types';
|
|
4
|
+
export default class NpmPublish extends CommandBase {
|
|
5
|
+
static aliases: string[];
|
|
6
|
+
static description: string;
|
|
7
|
+
static usage: string;
|
|
8
|
+
static examples: string[];
|
|
9
|
+
static flags: {
|
|
10
|
+
package: flags.IOptionFlag<string[]>;
|
|
11
|
+
exclude: flags.IOptionFlag<string[]>;
|
|
12
|
+
version: flags.IOptionFlag<string>;
|
|
13
|
+
pat: flags.IOptionFlag<string | undefined>;
|
|
14
|
+
};
|
|
15
|
+
private cp;
|
|
16
|
+
publishPackage(pkg: NpmPackage, cmd?: string[]): Promise<{
|
|
17
|
+
data: string;
|
|
18
|
+
error: string;
|
|
19
|
+
}>;
|
|
20
|
+
checkPackageCanPublish(pkg: NpmPackage, allPackages: AzurePackageArray, version: string): Promise<boolean>;
|
|
21
|
+
publishPackages(packages: NpmPackage[], allPackages: AzurePackageArray, version: string): Promise<void>;
|
|
22
|
+
run(): Promise<void>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const command_1 = require("@oclif/command");
|
|
5
|
+
const chalk_1 = require("chalk");
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const cli_ux_1 = require("cli-ux");
|
|
8
|
+
const command_base_1 = require("../../command.base");
|
|
9
|
+
const npm_1 = require("../../lib/npm");
|
|
10
|
+
class NpmPublish extends command_base_1.default {
|
|
11
|
+
async publishPackage(pkg, cmd = ['yarn', 'publish']) {
|
|
12
|
+
var e_1, _a, e_2, _b;
|
|
13
|
+
let sh;
|
|
14
|
+
let args;
|
|
15
|
+
// cross platform compatibility
|
|
16
|
+
if (process.platform === 'win32') {
|
|
17
|
+
sh = 'cmd';
|
|
18
|
+
args = ['/c'].concat(cmd);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
[sh, ...args] = cmd;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
this.cp = child_process_1.spawn(sh, args, {
|
|
25
|
+
cwd: pkg.path,
|
|
26
|
+
env: Object.assign(Object.assign({}, process.env), process.stdout.isTTY ? { FORCE_COLOR: process.env.FORCE_COLOR || '1' } : {}),
|
|
27
|
+
stdio: 'pipe'
|
|
28
|
+
});
|
|
29
|
+
let data = '';
|
|
30
|
+
try {
|
|
31
|
+
for (var _c = tslib_1.__asyncValues(this.cp.stdout), _d; _d = await _c.next(), !_d.done;) {
|
|
32
|
+
const chunk = _d.value;
|
|
33
|
+
data += chunk;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
37
|
+
finally {
|
|
38
|
+
try {
|
|
39
|
+
if (_d && !_d.done && (_a = _c.return)) await _a.call(_c);
|
|
40
|
+
}
|
|
41
|
+
finally { if (e_1) throw e_1.error; }
|
|
42
|
+
}
|
|
43
|
+
let error = '';
|
|
44
|
+
try {
|
|
45
|
+
for (var _e = tslib_1.__asyncValues(this.cp.stderr), _f; _f = await _e.next(), !_f.done;) {
|
|
46
|
+
const chunk = _f.value;
|
|
47
|
+
error += chunk;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
51
|
+
finally {
|
|
52
|
+
try {
|
|
53
|
+
if (_f && !_f.done && (_b = _e.return)) await _b.call(_e);
|
|
54
|
+
}
|
|
55
|
+
finally { if (e_2) throw e_2.error; }
|
|
56
|
+
}
|
|
57
|
+
const exitCode = await new Promise((resolve, _reject) => {
|
|
58
|
+
this.cp.on('close', resolve);
|
|
59
|
+
});
|
|
60
|
+
if (exitCode) {
|
|
61
|
+
throw new Error(`subprocess error exit ${exitCode}, ${error}`);
|
|
62
|
+
}
|
|
63
|
+
return { data, error };
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
throw new Error(err.message);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async checkPackageCanPublish(pkg, allPackages, version) {
|
|
70
|
+
this.log('');
|
|
71
|
+
cli_ux_1.default.action.start(`${pkg.name}@${pkg.version}`, undefined, { stdout: true });
|
|
72
|
+
let matchedPackage = allPackages.value.find(azurePackage => azurePackage.name === pkg.name);
|
|
73
|
+
if (pkg.version !== version) {
|
|
74
|
+
this.warn(`${pkg.name}@${pkg.version} doesn't match desired publish version!`);
|
|
75
|
+
cli_ux_1.default.action.stop(chalk_1.default.red(`version doesn't match version ${version}`));
|
|
76
|
+
throw new Error(`Version in package.json doesn't match desired published version`);
|
|
77
|
+
}
|
|
78
|
+
if (!matchedPackage) {
|
|
79
|
+
cli_ux_1.default.action.stop(`${chalk_1.default.green('new package')}`);
|
|
80
|
+
cli_ux_1.default.action.start(`publish new package ${pkg.name}@${pkg.version} from ${pkg.jsonPath}`, undefined, { stdout: true });
|
|
81
|
+
await this.publishPackage(pkg);
|
|
82
|
+
cli_ux_1.default.action.stop(`${chalk_1.default.green('done')}`);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
let matchVersion = matchedPackage.versions.find(azurePackageVersion => azurePackageVersion.version === pkg.version);
|
|
86
|
+
if (!matchVersion) {
|
|
87
|
+
cli_ux_1.default.action.stop(`${chalk_1.default.yellow('version missing')}`);
|
|
88
|
+
cli_ux_1.default.action.start(`publish new version ${pkg.name}@${pkg.version} from ${pkg.jsonPath}`, undefined, { stdout: true });
|
|
89
|
+
await this.publishPackage(pkg);
|
|
90
|
+
cli_ux_1.default.action.stop(`${chalk_1.default.green('done')}`);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
this.warn(`${pkg.name}@${pkg.version} already exists`);
|
|
94
|
+
cli_ux_1.default.action.stop(`${chalk_1.default.blue('skipping')}`);
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
async publishPackages(packages, allPackages, version) {
|
|
98
|
+
let errorCount = 0;
|
|
99
|
+
this.log(`publishing ${packages.length} packages:`);
|
|
100
|
+
this.log(packages.map(pkg => pkg.name).toString());
|
|
101
|
+
for (const pkg of packages) {
|
|
102
|
+
try {
|
|
103
|
+
await this.checkPackageCanPublish(pkg, allPackages, version);
|
|
104
|
+
}
|
|
105
|
+
catch (_a) {
|
|
106
|
+
errorCount++;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (errorCount > 0) {
|
|
111
|
+
throw new Error(`${errorCount} with Errors!`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async run() {
|
|
115
|
+
try {
|
|
116
|
+
const { flags } = this.parse(NpmPublish);
|
|
117
|
+
let npmContext = new npm_1.default();
|
|
118
|
+
cli_ux_1.default.action.start('Parsing packages to check', undefined, { stdout: true });
|
|
119
|
+
const packages = await npmContext.packageList(flags.package, flags.exclude, this);
|
|
120
|
+
cli_ux_1.default.action.stop(`${chalk_1.default.green('done')}`);
|
|
121
|
+
cli_ux_1.default.action.start('Getting list of published packages', undefined, { stdout: true });
|
|
122
|
+
const allPackages = JSON.parse(await npmContext.getAllNpmPackages(flags.pat));
|
|
123
|
+
cli_ux_1.default.action.stop(`${chalk_1.default.green('done')}`);
|
|
124
|
+
await this.publishPackages(packages, allPackages, flags.version);
|
|
125
|
+
cli_ux_1.default.action.stop('success');
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
cli_ux_1.default.action.stop('error');
|
|
129
|
+
this.error(`${err.message}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
exports.default = NpmPublish;
|
|
134
|
+
NpmPublish.aliases = ['npm:publish'];
|
|
135
|
+
NpmPublish.description = 'Publish npm packages';
|
|
136
|
+
NpmPublish.usage = 'npm:publish [options]';
|
|
137
|
+
NpmPublish.examples = [
|
|
138
|
+
'$ ops npm:publish -v 15.0.0',
|
|
139
|
+
'$ ops npm:publish -v 15.0.0',
|
|
140
|
+
'$ ops npm:publish -p @adinsure-tooling/* -v 15.0.0',
|
|
141
|
+
'$ ops npm:publish -p @adinsure-tooling/* -x @adisure-tooling/studio-services -v 6.0.0'
|
|
142
|
+
];
|
|
143
|
+
NpmPublish.flags = {
|
|
144
|
+
package: command_1.flags.string({
|
|
145
|
+
char: 'p',
|
|
146
|
+
description: 'package name filter you want to check',
|
|
147
|
+
multiple: true
|
|
148
|
+
}),
|
|
149
|
+
exclude: command_1.flags.string({
|
|
150
|
+
char: 'x',
|
|
151
|
+
description: 'exclude packages from filter',
|
|
152
|
+
multiple: true
|
|
153
|
+
}),
|
|
154
|
+
version: command_1.flags.string({
|
|
155
|
+
char: 'v',
|
|
156
|
+
description: 'version we want to check if published',
|
|
157
|
+
required: true
|
|
158
|
+
}),
|
|
159
|
+
pat: command_1.flags.string({
|
|
160
|
+
description: 'PAT token or AZURE_DEVOPS_EXT_PAT environment variable',
|
|
161
|
+
env: 'AZURE_DEVOPS_EXT_PAT'
|
|
162
|
+
}),
|
|
163
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { flags } from '@oclif/command';
|
|
2
|
+
import CommandBase from '../command.base';
|
|
3
|
+
interface Transformation {
|
|
4
|
+
name: string;
|
|
5
|
+
srcPath: string;
|
|
6
|
+
transform(): void;
|
|
7
|
+
}
|
|
8
|
+
interface DotfileLine {
|
|
9
|
+
prefix: string;
|
|
10
|
+
source: string;
|
|
11
|
+
transformData: string;
|
|
12
|
+
}
|
|
13
|
+
export default class PrepareContent extends CommandBase {
|
|
14
|
+
static readonly dotfile = ".opsprepare";
|
|
15
|
+
static description: string;
|
|
16
|
+
static usage: string;
|
|
17
|
+
static examples: string[];
|
|
18
|
+
static flags: {
|
|
19
|
+
destination: flags.IOptionFlag<string>;
|
|
20
|
+
source: flags.IOptionFlag<string>;
|
|
21
|
+
};
|
|
22
|
+
parseDotfileLine(line: string, sourceDirectory: string): Promise<DotfileLine>;
|
|
23
|
+
readDotfile(sourceDirectory: string): Promise<DotfileLine[]>;
|
|
24
|
+
getTransformation(lineData: DotfileLine, baseSourcedir: string, baseDestinationDir: string): Promise<Transformation>;
|
|
25
|
+
prepareTransformations(linesData: DotfileLine[], baseSourceDir: string, baseDestinationDir: string): Promise<Transformation[]>;
|
|
26
|
+
runTransformations(transformations: Transformation[]): Promise<void>;
|
|
27
|
+
run(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const command_1 = require("@oclif/command");
|
|
4
|
+
const chalk_1 = require("chalk");
|
|
5
|
+
const cli_ux_1 = require("cli-ux");
|
|
6
|
+
const fs = require("fs-extra");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const command_base_1 = require("../command.base");
|
|
9
|
+
class CopyTransformation {
|
|
10
|
+
constructor(srcPath, dstPath) {
|
|
11
|
+
this.name = this.constructor.name;
|
|
12
|
+
this.srcPath = srcPath;
|
|
13
|
+
this.dstPath = dstPath;
|
|
14
|
+
}
|
|
15
|
+
transform() {
|
|
16
|
+
try {
|
|
17
|
+
fs.accessSync(this.srcPath, fs.constants.R_OK);
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
if (err.code === 'ENOENT') {
|
|
21
|
+
throw new Error(`${this.name}: source file does not exist <${this.srcPath}>`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
throw new Error(`${this.name}: ${err} <${this.srcPath}>`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
cli_ux_1.default.log(`Copying from ${this.srcPath} to ${this.dstPath}`);
|
|
28
|
+
try {
|
|
29
|
+
fs.copySync(this.srcPath, this.dstPath, { overwrite: true });
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
throw new Error(`${this.name}: ${err} <${this.srcPath}>`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class JavascriptTransformation {
|
|
37
|
+
constructor(srcPath, dstPath, javascriptFile, functionName) {
|
|
38
|
+
this.name = this.constructor.name;
|
|
39
|
+
this.srcPath = srcPath;
|
|
40
|
+
this.dstPath = dstPath;
|
|
41
|
+
this.jsFile = path.resolve(javascriptFile);
|
|
42
|
+
this.functionName = functionName;
|
|
43
|
+
}
|
|
44
|
+
importTransformationModule() {
|
|
45
|
+
try {
|
|
46
|
+
return require(this.jsFile);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
if (err.code === 'MODULE_NOT_FOUND') {
|
|
50
|
+
throw new Error(`${this.name}: module file ${this.jsFile} not found <${this.srcPath}>`);
|
|
51
|
+
}
|
|
52
|
+
else if (err instanceof SyntaxError) {
|
|
53
|
+
throw new Error(`${this.name}: ${err} in \'${this.jsFile}\' <${this.srcPath}>`);
|
|
54
|
+
}
|
|
55
|
+
throw new Error(`${this.name}: ${err} <${this.srcPath}>`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
executeTransformationFunction(transformationModule) {
|
|
59
|
+
try {
|
|
60
|
+
let result = transformationModule[this.functionName](this.srcPath);
|
|
61
|
+
if (typeof (result) !== 'string') {
|
|
62
|
+
throw new Error(`${this.name}: transformation functions must return string, ${this.functionName} returned ${typeof (result)} <${this.srcPath}>`);
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
if (err instanceof TypeError) {
|
|
68
|
+
throw new Error(`${this.name}: invalid function \'${this.functionName}\' in ${this.jsFile} <${this.srcPath}>`);
|
|
69
|
+
}
|
|
70
|
+
/*
|
|
71
|
+
Rethrow anything errors that were thrown in the above try block
|
|
72
|
+
*/
|
|
73
|
+
if (err.message && err.message.search(`${this.name}`.length > 0)) {
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
76
|
+
throw new Error(`${this.name}: ${err} <${this.srcPath}>`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
transform() {
|
|
80
|
+
// Check if source exists, if not, issue a warning
|
|
81
|
+
try {
|
|
82
|
+
fs.accessSync(this.srcPath);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
if (err.code === 'ENOENT') {
|
|
86
|
+
let errMsg = `${this.name}: source does not exist, relying on transformation to find proper source file <${this.srcPath}>`;
|
|
87
|
+
cli_ux_1.default.log(`${chalk_1.default.yellow(errMsg)}`);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
throw new Error(`${this.name}: ${err} <${this.srcPath}>`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
cli_ux_1.default.log(`Transforming from ${this.srcPath} to ${this.dstPath} using ${this.jsFile} function ${this.functionName}`);
|
|
94
|
+
// Import transformation module
|
|
95
|
+
let transModule = this.importTransformationModule();
|
|
96
|
+
// Execute transformation function
|
|
97
|
+
let transResult = this.executeTransformationFunction(transModule);
|
|
98
|
+
// Write output to destination file
|
|
99
|
+
try {
|
|
100
|
+
fs.outputFileSync(this.dstPath, transResult, { encoding: 'utf-8', mode: 0o644, flag: 'w' });
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
throw new Error(`${this.name}: ${err} <${this.srcPath}>`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
class RemovalTransformation {
|
|
108
|
+
constructor(srcPath) {
|
|
109
|
+
this.name = this.constructor.name;
|
|
110
|
+
this.srcPath = srcPath;
|
|
111
|
+
}
|
|
112
|
+
transform() {
|
|
113
|
+
try {
|
|
114
|
+
fs.accessSync(this.srcPath, fs.constants.W_OK);
|
|
115
|
+
cli_ux_1.default.log(`Removing ${this.srcPath}.`);
|
|
116
|
+
fs.removeSync(this.srcPath);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
if (err.code === 'ENOENT') {
|
|
120
|
+
let errMsg = `${this.name}: target does not exist, skipping removal <${this.srcPath}>`;
|
|
121
|
+
cli_ux_1.default.log(`${chalk_1.default.yellow(errMsg)}`);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
throw new Error(`${this.name}: ${err} <${this.srcPath}>`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
class PrepareContent extends command_base_1.default {
|
|
130
|
+
async parseDotfileLine(line, sourceDirectory) {
|
|
131
|
+
const re = RegExp('^([!#]{1})?(.+?)({.*?})?$');
|
|
132
|
+
let m = line.match(re);
|
|
133
|
+
let dotfileLine = {
|
|
134
|
+
prefix: m[1],
|
|
135
|
+
source: path.join(sourceDirectory, path.normalize(m[2].trim())),
|
|
136
|
+
transformData: m[3]
|
|
137
|
+
};
|
|
138
|
+
/*
|
|
139
|
+
Sanity check on source file name. Should not contain "{", ":", or "}" characters
|
|
140
|
+
*/
|
|
141
|
+
const valRe = RegExp('[{:}]+');
|
|
142
|
+
let vm = dotfileLine.source.match(valRe);
|
|
143
|
+
if (vm !== null) {
|
|
144
|
+
throw new SyntaxError(`<${PrepareContent.dotfile}> source file name '${dotfileLine.source}' contains invalid character '${vm[0]}'`);
|
|
145
|
+
}
|
|
146
|
+
return dotfileLine;
|
|
147
|
+
}
|
|
148
|
+
async readDotfile(sourceDirectory) {
|
|
149
|
+
let file_content;
|
|
150
|
+
try {
|
|
151
|
+
file_content = fs.readFileSync(path.join(sourceDirectory, PrepareContent.dotfile), 'utf-8');
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
if (err.code === 'ENOENT') {
|
|
155
|
+
let errMsg = `.opsprepare file not found in ${path.resolve(sourceDirectory)}`;
|
|
156
|
+
throw new Error(errMsg);
|
|
157
|
+
}
|
|
158
|
+
throw new Error(err.message);
|
|
159
|
+
}
|
|
160
|
+
let lines = file_content.split(/\r?\n/).filter(line => line.length !== 0);
|
|
161
|
+
const lineParse = lines.map(line => {
|
|
162
|
+
return this.parseDotfileLine(line.trim(), sourceDirectory);
|
|
163
|
+
});
|
|
164
|
+
const parsedLines = Promise.all(lineParse).then(lines => lines.filter(line => line.prefix !== '#'));
|
|
165
|
+
return parsedLines;
|
|
166
|
+
}
|
|
167
|
+
async getTransformation(lineData, baseSourcedir, baseDestinationDir) {
|
|
168
|
+
const targetPath = path.join(baseDestinationDir, lineData.source);
|
|
169
|
+
/*
|
|
170
|
+
Anything prefixed with ! is a removal
|
|
171
|
+
*/
|
|
172
|
+
if (lineData.prefix === '!') {
|
|
173
|
+
return new RemovalTransformation(targetPath);
|
|
174
|
+
}
|
|
175
|
+
/*
|
|
176
|
+
In case transformation data is present, prepare transformations based on requested plugin.
|
|
177
|
+
*/
|
|
178
|
+
if (lineData.transformData) {
|
|
179
|
+
const re = RegExp('^{(.+?):.*}$');
|
|
180
|
+
const transformType = lineData.transformData.match(re)[1];
|
|
181
|
+
switch (transformType) {
|
|
182
|
+
case 'cp': {
|
|
183
|
+
const rePath = RegExp('^{cp:([^:]+)}$');
|
|
184
|
+
const mPath = lineData.transformData.match(rePath);
|
|
185
|
+
if (!mPath) {
|
|
186
|
+
throw new SyntaxError(`<${PrepareContent.dotfile}> syntax '${lineData.transformData}' is invalid for 'cp' transformation type`);
|
|
187
|
+
}
|
|
188
|
+
const tPath = path.join(baseDestinationDir, path.dirname(lineData.source), mPath[1]);
|
|
189
|
+
return new CopyTransformation(lineData.source, tPath);
|
|
190
|
+
}
|
|
191
|
+
case 'js': {
|
|
192
|
+
/* TODO: Some sanity checking will be required after regex matching
|
|
193
|
+
to make sure everything was matched and is defined
|
|
194
|
+
*/
|
|
195
|
+
// Parse transformation data
|
|
196
|
+
const reData = RegExp('^{js:(.+?):(.+)}$');
|
|
197
|
+
const mData = lineData.transformData.match(reData);
|
|
198
|
+
if (!mData) {
|
|
199
|
+
throw new SyntaxError(`<${PrepareContent.dotfile}> syntax '${lineData.transformData}' is invalid for 'js' transformation type`);
|
|
200
|
+
}
|
|
201
|
+
const jsFile = path.join(baseSourcedir, mData[1]);
|
|
202
|
+
const jsFunc = mData[2];
|
|
203
|
+
// File path absolute. Should it be relative to the transformation function?
|
|
204
|
+
const filePath = path.resolve(lineData.source);
|
|
205
|
+
return new JavascriptTransformation(filePath, targetPath, jsFile, jsFunc);
|
|
206
|
+
}
|
|
207
|
+
default: {
|
|
208
|
+
const p = lineData.prefix ? lineData.prefix : '';
|
|
209
|
+
const s = lineData.source;
|
|
210
|
+
const t = lineData.transformData ? lineData.transformData : '';
|
|
211
|
+
this.error(`No valid transformation can be extrapolated from ${p}${s}${t}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/*
|
|
216
|
+
Return default transformation
|
|
217
|
+
*/
|
|
218
|
+
return new CopyTransformation(lineData.source, targetPath);
|
|
219
|
+
}
|
|
220
|
+
async prepareTransformations(linesData, baseSourceDir, baseDestinationDir) {
|
|
221
|
+
const transformations = linesData.map(line => {
|
|
222
|
+
return this.getTransformation(line, baseSourceDir, baseDestinationDir);
|
|
223
|
+
});
|
|
224
|
+
return Promise.all(transformations);
|
|
225
|
+
}
|
|
226
|
+
/*
|
|
227
|
+
This function divides transformations into execution groups based on their class
|
|
228
|
+
and runs transformations for each group
|
|
229
|
+
*/
|
|
230
|
+
async runTransformations(transformations) {
|
|
231
|
+
let copyTransformList = [];
|
|
232
|
+
let modlueTransformList = [];
|
|
233
|
+
let excludeList = [];
|
|
234
|
+
// Sort transformation into two lists
|
|
235
|
+
transformations.forEach(t => {
|
|
236
|
+
if (t instanceof CopyTransformation) {
|
|
237
|
+
copyTransformList.push(t);
|
|
238
|
+
}
|
|
239
|
+
else if (t instanceof RemovalTransformation) {
|
|
240
|
+
excludeList.push(t);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
modlueTransformList.push(t);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
const t = async (transformation) => {
|
|
247
|
+
try {
|
|
248
|
+
transformation.transform();
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
return Promise.reject(err.message);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
const doCopyTransform = copyTransformList.map(t);
|
|
255
|
+
const doModuleTransform = modlueTransformList.map(t);
|
|
256
|
+
const doExclude = excludeList.map(t);
|
|
257
|
+
/* Copy first, then transform and finally remove files. This should
|
|
258
|
+
ensure that transformations can overwrite previous copies. Removals
|
|
259
|
+
can still erase transformations though. If needed, we can remove
|
|
260
|
+
before transforming.
|
|
261
|
+
*/
|
|
262
|
+
await Promise.all(doCopyTransform);
|
|
263
|
+
await Promise.all(doModuleTransform);
|
|
264
|
+
await Promise.all(doExclude);
|
|
265
|
+
}
|
|
266
|
+
/*
|
|
267
|
+
Validates file system on source repository by checking if files or directories exist
|
|
268
|
+
*/
|
|
269
|
+
/*
|
|
270
|
+
async validate(arr: string[][], source: string): Promise<boolean> {
|
|
271
|
+
//this.log(`${chalk.blue('Validating if files and directories exist.')}`);
|
|
272
|
+
cli.action.start(`${chalk.blue('Validating if files and directories exist')}`, '', {stdout: true});
|
|
273
|
+
for (let a of arr[0]) {
|
|
274
|
+
let element_path = path.join(source, a);
|
|
275
|
+
if(!(fs.existsSync(path.join(element_path)))) {
|
|
276
|
+
this.log(`${chalk.red('File or directory: ' + path.join(element_path) + ' could not be found')}`);
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
for (let a of arr[1]) {
|
|
281
|
+
let element_path = path.join(source, a);
|
|
282
|
+
if(!(fs.existsSync(element_path))) {
|
|
283
|
+
this.log(`${chalk.red('File or directory: ' + element_path + ' could not be found')}`);
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
//this.log(`${chalk.green('Validation done.')}`);
|
|
289
|
+
cli.action.stop(`${chalk.green('done')}`);
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
*/
|
|
293
|
+
/*
|
|
294
|
+
This function copies elements and removes excluded ones.
|
|
295
|
+
*/
|
|
296
|
+
/*
|
|
297
|
+
async copyWorkspace(arr: string[][], dest: string, source: string): Promise<any> {
|
|
298
|
+
if (await this.validate(arr, source)) {
|
|
299
|
+
cli.action.start(`${chalk.blue('Preparing content')}`, '', {stdout: true});
|
|
300
|
+
arr[0].forEach(a => {
|
|
301
|
+
this.log('Copying: ', path.join(source, a));
|
|
302
|
+
fs.copySync(path.join(source, a), path.join(dest, a), {overwrite: true});
|
|
303
|
+
});
|
|
304
|
+
this.log(`${chalk.green('Copying done.')}`);
|
|
305
|
+
this.log(`${chalk.blue('Removing excluded elements.')}`);
|
|
306
|
+
arr[1].forEach(a => {
|
|
307
|
+
let curPath = path.join(dest, a);
|
|
308
|
+
if (fs.pathExistsSync(curPath)) {
|
|
309
|
+
if (fs.lstatSync(curPath).isFile()) {
|
|
310
|
+
try {
|
|
311
|
+
fs.removeSync(curPath);
|
|
312
|
+
this.log('Removing file: ', curPath);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
cli.action.stop(`${chalk.red('failed')}`);
|
|
315
|
+
this.error(error.message);
|
|
316
|
+
}
|
|
317
|
+
} else if (fs.lstatSync(curPath).isDirectory()) {
|
|
318
|
+
try {
|
|
319
|
+
fs.removeSync(curPath);
|
|
320
|
+
this.log('Removing dir: ', curPath);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
cli.action.stop(`${chalk.red('failed')}`);
|
|
323
|
+
this.error(error.message);
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
cli.action.stop(`${chalk.red('error')}`);
|
|
327
|
+
this.error('Type not file or directory');
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
this.log(`${chalk.green('Removing done.')}`);
|
|
332
|
+
cli.action.stop(`${chalk.green('done')}`);
|
|
333
|
+
} else {
|
|
334
|
+
cli.action.stop(`${chalk.red('error')}`);
|
|
335
|
+
this.error('Content validation failed.');
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
*/
|
|
339
|
+
async run() {
|
|
340
|
+
const { flags } = this.parse(PrepareContent);
|
|
341
|
+
// read the flags and set paths relative to execution directory
|
|
342
|
+
let source = path.relative(process.cwd(), flags.source);
|
|
343
|
+
source = source ? source : '.';
|
|
344
|
+
let dest = flags.destination;
|
|
345
|
+
try {
|
|
346
|
+
let dotfileLines = await this.readDotfile(source);
|
|
347
|
+
this.log(`${chalk_1.default.blue('Content transformation started.')}`);
|
|
348
|
+
let transformations = await this.prepareTransformations(dotfileLines, source, dest);
|
|
349
|
+
await this.runTransformations(transformations);
|
|
350
|
+
this.log(`${chalk_1.default.green('Content prepared at: ', dest)}`);
|
|
351
|
+
}
|
|
352
|
+
catch (err) {
|
|
353
|
+
this.log(`${chalk_1.default.red(err)}`);
|
|
354
|
+
/*
|
|
355
|
+
In test mode, exit with status 0
|
|
356
|
+
*/
|
|
357
|
+
if (process.env.OPS_TEST_MODE === 'true') {
|
|
358
|
+
this.exit(0);
|
|
359
|
+
}
|
|
360
|
+
this.exit(1);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
exports.default = PrepareContent;
|
|
365
|
+
PrepareContent.dotfile = '.opsprepare';
|
|
366
|
+
PrepareContent.description = `copies files as described in ${PrepareContent.dotfile} which is at root of source directory`;
|
|
367
|
+
PrepareContent.usage = 'prepare-content -d <destination> [-s <source>]';
|
|
368
|
+
PrepareContent.examples = [
|
|
369
|
+
`$ ops prepare-content -d .\\my_workspace`,
|
|
370
|
+
`$ ops prepare-content -d .\\my_workspace -s .\\my_repository`,
|
|
371
|
+
`$ ops prepare-content -d C:\\git\\my_workspace`,
|
|
372
|
+
`$ ops prepare-content -d C:\\git\\my_workspace -s C:\\git\\my_repository\\my_repository`
|
|
373
|
+
];
|
|
374
|
+
PrepareContent.flags = {
|
|
375
|
+
destination: command_1.flags.string({
|
|
376
|
+
char: 'd',
|
|
377
|
+
description: 'where to copy the workspace files',
|
|
378
|
+
required: true
|
|
379
|
+
}),
|
|
380
|
+
source: command_1.flags.string({
|
|
381
|
+
char: 's',
|
|
382
|
+
description: '[optional] source directory, default: current dir',
|
|
383
|
+
required: false,
|
|
384
|
+
default: '.'
|
|
385
|
+
})
|
|
386
|
+
};
|