@bitblit/ratchet-node-only 4.0.80-alpha

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.
Files changed (41) hide show
  1. package/dist/cjs/build/ratchet-node-only-info.js +18 -0
  2. package/dist/cjs/ci/apply-ci-env-variables-to-files.js +82 -0
  3. package/dist/cjs/ci/ci-run-information-util.js +47 -0
  4. package/dist/cjs/ci/ci-run-information.js +2 -0
  5. package/dist/cjs/cli/abstract-ratchet-cli-handler.js +32 -0
  6. package/dist/cjs/cli/cli-ratchet.js +33 -0
  7. package/dist/cjs/cli/ratchet-cli-handler.js +21 -0
  8. package/dist/cjs/csv/csv-ratchet.js +163 -0
  9. package/dist/cjs/files/files-to-static-class.js +61 -0
  10. package/dist/cjs/index.js +14 -0
  11. package/dist/cjs/third-party/git/git-ratchet.js +74 -0
  12. package/dist/cjs/third-party/slack/publish-ci-release-to-slack.js +64 -0
  13. package/dist/es/build/ratchet-node-only-info.js +14 -0
  14. package/dist/es/ci/apply-ci-env-variables-to-files.js +77 -0
  15. package/dist/es/ci/ci-run-information-util.js +43 -0
  16. package/dist/es/ci/ci-run-information.js +1 -0
  17. package/dist/es/cli/abstract-ratchet-cli-handler.js +28 -0
  18. package/dist/es/cli/cli-ratchet.js +28 -0
  19. package/dist/es/cli/ratchet-cli-handler.js +17 -0
  20. package/dist/es/csv/csv-ratchet.js +158 -0
  21. package/dist/es/files/files-to-static-class.js +56 -0
  22. package/dist/es/index.js +11 -0
  23. package/dist/es/third-party/git/git-ratchet.js +69 -0
  24. package/dist/es/third-party/slack/publish-ci-release-to-slack.js +59 -0
  25. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  26. package/dist/tsconfig.es.tsbuildinfo +1 -0
  27. package/dist/tsconfig.types.tsbuildinfo +1 -0
  28. package/dist/types/build/ratchet-node-only-info.d.ts +5 -0
  29. package/dist/types/ci/apply-ci-env-variables-to-files.d.ts +11 -0
  30. package/dist/types/ci/ci-run-information-util.d.ts +8 -0
  31. package/dist/types/ci/ci-run-information.d.ts +9 -0
  32. package/dist/types/cli/abstract-ratchet-cli-handler.d.ts +6 -0
  33. package/dist/types/cli/cli-ratchet.d.ts +6 -0
  34. package/dist/types/cli/ratchet-cli-handler.d.ts +6 -0
  35. package/dist/types/csv/csv-ratchet.d.ts +27 -0
  36. package/dist/types/files/files-to-static-class.d.ts +8 -0
  37. package/dist/types/index.d.ts +14 -0
  38. package/dist/types/third-party/git/git-ratchet.d.ts +26 -0
  39. package/dist/types/third-party/slack/publish-ci-release-to-slack.d.ts +9 -0
  40. package/includes/cli.js +12 -0
  41. package/package.json +77 -0
@@ -0,0 +1,77 @@
1
+ import fs from 'fs';
2
+ import { ErrorRatchet, Logger, RequireRatchet, StringRatchet } from '@bitblit/ratchet-common';
3
+ import { CiRunInformationUtil } from './ci-run-information-util';
4
+ export class ApplyCiEnvVariablesToFiles {
5
+ static async process(fileNames, cfg, buildFinder = 'LOCAL-SNAPSHOT', branchFinder = 'LOCAL-BRANCH', hashFinder = 'LOCAL-HASH', tagFinder = 'LOCAL-TAG', timeFinder = 'LOCAL-TIME') {
6
+ RequireRatchet.notNullOrUndefined(cfg, 'cfg');
7
+ RequireRatchet.notNullOrUndefined(cfg.buildNumber, 'cfg.buildNumber');
8
+ RequireRatchet.notNullOrUndefined(cfg.localTime, 'cfg.localTime');
9
+ if (!fileNames) {
10
+ throw new Error('fileNames must be defined');
11
+ }
12
+ if (fileNames.length === 0) {
13
+ Logger.warn('Warning - no files supplied to process');
14
+ }
15
+ if (!cfg.buildNumber) {
16
+ ErrorRatchet.throwFormattedErr('%s env var not set - apparently not in a CI environment', cfg.buildNumber);
17
+ }
18
+ Logger.info('Processing files %j with run info %j', cfg);
19
+ let foundCount = 0;
20
+ fileNames.forEach((f) => {
21
+ if (!fs.existsSync(f)) {
22
+ Logger.error('Could not find file %s to process, continuing', f);
23
+ }
24
+ else {
25
+ try {
26
+ let contents = fs.readFileSync(f).toString();
27
+ contents = contents.split(buildFinder).join(cfg.buildNumber);
28
+ contents = contents.split(branchFinder).join(cfg.branch || '');
29
+ contents = contents.split(hashFinder).join(cfg.commitHash || '');
30
+ contents = contents.split(tagFinder).join(cfg.tag || '');
31
+ contents = contents.split(timeFinder).join(cfg.localTime || '');
32
+ fs.writeFileSync(f, contents);
33
+ foundCount++;
34
+ }
35
+ catch (err) {
36
+ Logger.error('Error processing %s , continuing: %s', f, err, err);
37
+ }
38
+ }
39
+ });
40
+ return foundCount;
41
+ }
42
+ static extractFileNames() {
43
+ let rval = [];
44
+ if (process && process.argv && process.argv.length > 3) {
45
+ rval = process.argv.slice(3);
46
+ }
47
+ return rval;
48
+ }
49
+ static extractVariableConfig(inName) {
50
+ let rval = null;
51
+ const name = StringRatchet.trimToEmpty(inName).toLowerCase();
52
+ switch (name) {
53
+ case 'circleci':
54
+ rval = CiRunInformationUtil.createDefaultCircleCiRunInformation();
55
+ break;
56
+ case 'github':
57
+ rval = CiRunInformationUtil.createDefaultGithubActionsRunInformation();
58
+ break;
59
+ case 'test':
60
+ rval = CiRunInformationUtil.createTestingCiRunInformation();
61
+ break;
62
+ default:
63
+ ErrorRatchet.throwFormattedErr('Unrecognized env var config type : %s', name);
64
+ }
65
+ Logger.info('Using variable config : %j', rval);
66
+ return rval;
67
+ }
68
+ static async runFromCliArgs(args) {
69
+ if (args.length > 1) {
70
+ return ApplyCiEnvVariablesToFiles.process(args.slice(1), ApplyCiEnvVariablesToFiles.extractVariableConfig(args[0]));
71
+ }
72
+ else {
73
+ Logger.infoP('Usage : apply-ci-env-variables-to-files {file1} {file2} ...');
74
+ return -1;
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,43 @@
1
+ import { DateTime } from 'luxon';
2
+ import { GlobalRatchet } from '@bitblit/ratchet-common';
3
+ export class CiRunInformationUtil {
4
+ static createTestingCiRunInformation(timezone = CiRunInformationUtil.DEFAULT_TIME_ZONE) {
5
+ const now = new Date().toISOString();
6
+ const rval = {
7
+ buildNumber: 'Test_buildNumberVar_' + now,
8
+ localTime: DateTime.local().setZone(timezone).toFormat(CiRunInformationUtil.DEFAULT_TIME_FORMAT),
9
+ branch: 'Test_branchVar_' + now,
10
+ tag: 'Test_tagVar_' + now,
11
+ commitHash: 'Test_hashVar_' + now,
12
+ userName: 'Test_userNameVar_' + now,
13
+ projectName: 'Test_projectNameVar_' + now,
14
+ };
15
+ return rval;
16
+ }
17
+ static createDefaultCircleCiRunInformation(timezone = CiRunInformationUtil.DEFAULT_TIME_ZONE) {
18
+ const rval = {
19
+ buildNumber: GlobalRatchet.fetchGlobalVar('CIRCLE_BUILD_NUM'),
20
+ branch: GlobalRatchet.fetchGlobalVar('CIRCLE_BRANCH'),
21
+ tag: GlobalRatchet.fetchGlobalVar('CIRCLE_TAG'),
22
+ commitHash: GlobalRatchet.fetchGlobalVar('CIRCLE_SHA1'),
23
+ localTime: DateTime.local().setZone(timezone).toFormat(CiRunInformationUtil.DEFAULT_TIME_FORMAT),
24
+ userName: GlobalRatchet.fetchGlobalVar('CIRCLE_USERNAME'),
25
+ projectName: GlobalRatchet.fetchGlobalVar('CIRCLE_PROJECT_REPONAME'),
26
+ };
27
+ return rval;
28
+ }
29
+ static createDefaultGithubActionsRunInformation(timezone = CiRunInformationUtil.DEFAULT_TIME_ZONE) {
30
+ const rval = {
31
+ buildNumber: GlobalRatchet.fetchGlobalVar('GITHUB_RUN_NUMBER'),
32
+ branch: GlobalRatchet.fetchGlobalVar('GITHUB_REF_NAME'),
33
+ tag: GlobalRatchet.fetchGlobalVar('GITHUB_REF_NAME'),
34
+ commitHash: GlobalRatchet.fetchGlobalVar('GITHUB_SHA'),
35
+ localTime: DateTime.local().setZone(timezone).toFormat(CiRunInformationUtil.DEFAULT_TIME_FORMAT),
36
+ userName: GlobalRatchet.fetchGlobalVar('GITHUB_ACTOR'),
37
+ projectName: GlobalRatchet.fetchGlobalVar('GITHUB_REPOSITORY'),
38
+ };
39
+ return rval;
40
+ }
41
+ }
42
+ CiRunInformationUtil.DEFAULT_TIME_FORMAT = 'yyyy-MM-dd HH:mm:ss a z';
43
+ CiRunInformationUtil.DEFAULT_TIME_ZONE = 'America/Los_Angeles';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import { CliRatchet } from './cli-ratchet';
2
+ export class AbstractRatchetCliHandler {
3
+ async findAndExecuteHandler() {
4
+ let handler = null;
5
+ if (CliRatchet.argsAfterCommand(['version'])) {
6
+ console.log('Version : ' + JSON.stringify(this.fetchVersionInfo()));
7
+ }
8
+ else {
9
+ const handlerMap = this.fetchHandlerMap();
10
+ const keys = Object.keys(handlerMap);
11
+ let remainArgs = null;
12
+ for (let i = 0; i < keys.length && !handler; i++) {
13
+ remainArgs = CliRatchet.argsAfterCommand([keys[i], keys[i] + '.js']);
14
+ if (remainArgs) {
15
+ handler = handlerMap[keys[i]];
16
+ }
17
+ }
18
+ if (handler) {
19
+ console.debug('Running command with args ' + JSON.stringify(remainArgs));
20
+ await handler(remainArgs);
21
+ }
22
+ else {
23
+ console.log('Unrecognized command : ', process.argv);
24
+ console.log('Valid commands are : ', Object.keys(handlerMap));
25
+ }
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,28 @@
1
+ export class CliRatchet {
2
+ static isCalledFromCLI(filenames) {
3
+ let rval = false;
4
+ for (let i = 0; filenames && i < filenames.length && !rval; i++) {
5
+ rval = CliRatchet.indexOfCommandArgument(filenames[i]) > -1;
6
+ }
7
+ return rval;
8
+ }
9
+ static argsAfterCommand(filenames) {
10
+ let rval = null;
11
+ if (process?.argv?.length && filenames?.length) {
12
+ let idx = null;
13
+ for (let i = 0; i < filenames.length && idx === null; i++) {
14
+ idx = CliRatchet.indexOfCommandArgument(filenames[i]);
15
+ }
16
+ rval = idx !== null ? process.argv.slice(idx + 1, process.argv.length) : null;
17
+ }
18
+ return rval;
19
+ }
20
+ static isCalledFromCLISingle(filename) {
21
+ return CliRatchet.isCalledFromCLI([filename]);
22
+ }
23
+ static indexOfCommandArgument(filename) {
24
+ const contFileName = process.argv.map((arg) => arg.indexOf(filename) !== -1);
25
+ const idx = contFileName.indexOf(true);
26
+ return idx === -1 ? null : idx;
27
+ }
28
+ }
@@ -0,0 +1,17 @@
1
+ import { RatchetNodeOnlyInfo } from '../build/ratchet-node-only-info';
2
+ import { ApplyCiEnvVariablesToFiles } from '../ci/apply-ci-env-variables-to-files';
3
+ import { FilesToStaticClass } from '../files/files-to-static-class';
4
+ import { PublishCiReleaseToSlack } from '../third-party/slack/publish-ci-release-to-slack';
5
+ import { AbstractRatchetCliHandler } from './abstract-ratchet-cli-handler';
6
+ export class RatchetCliHandler extends AbstractRatchetCliHandler {
7
+ fetchHandlerMap() {
8
+ return {
9
+ 'apply-ci-env-variables-to-files': ApplyCiEnvVariablesToFiles.runFromCliArgs,
10
+ 'files-to-static-class': FilesToStaticClass.runFromCliArgs,
11
+ 'publish-ci-release-to-slack': PublishCiReleaseToSlack.runFromCliArgs,
12
+ };
13
+ }
14
+ fetchVersionInfo() {
15
+ return RatchetNodeOnlyInfo.buildInformation();
16
+ }
17
+ }
@@ -0,0 +1,158 @@
1
+ import fs from 'fs';
2
+ import { parse } from 'csv-parse';
3
+ import { Logger } from '@bitblit/ratchet-common';
4
+ import { stringify } from 'csv-stringify';
5
+ import { RequireRatchet } from '@bitblit/ratchet-common';
6
+ import { MapRatchet } from '@bitblit/ratchet-common';
7
+ import { Readable } from 'stream';
8
+ export class CsvRatchet {
9
+ static defaultParseOptions() {
10
+ const rval = {
11
+ delimiter: ',',
12
+ columns: true,
13
+ };
14
+ return rval;
15
+ }
16
+ static defaultStringifyOptions() {
17
+ const rval = {
18
+ header: true,
19
+ };
20
+ return rval;
21
+ }
22
+ static async stringParse(input, pf, opts = CsvRatchet.defaultParseOptions()) {
23
+ return CsvRatchet.streamParse(Readable.from(input), pf, opts);
24
+ }
25
+ static async streamParse(readStream, pf, opts = CsvRatchet.defaultParseOptions()) {
26
+ return new Promise((res, rej) => {
27
+ const rval = [];
28
+ const p = parse(opts);
29
+ p.on('readable', () => {
30
+ let record = p.read();
31
+ while (record) {
32
+ const newVal = pf(record);
33
+ if (newVal) {
34
+ rval.push(newVal);
35
+ }
36
+ else {
37
+ }
38
+ record = p.read();
39
+ }
40
+ });
41
+ p.on('error', (err) => {
42
+ rej(err);
43
+ });
44
+ p.on('end', () => {
45
+ res(rval);
46
+ });
47
+ readStream.pipe(p);
48
+ });
49
+ }
50
+ static async fileParse(filename, pf) {
51
+ const readStream = fs.createReadStream(filename);
52
+ return CsvRatchet.streamParse(readStream, pf);
53
+ }
54
+ static async generateCsvData(objectsToConvert, opts = CsvRatchet.defaultStringifyOptions()) {
55
+ Logger.silly('Converting %d items into csv file', objectsToConvert.length);
56
+ const genProm = new Promise((res, rej) => {
57
+ stringify(objectsToConvert, opts, function (err, data) {
58
+ if (err) {
59
+ rej(err);
60
+ }
61
+ else {
62
+ res(data);
63
+ }
64
+ });
65
+ });
66
+ return genProm;
67
+ }
68
+ static async generateComparison(file1, file2, keyField) {
69
+ RequireRatchet.notNullOrUndefined(file1, 'file1');
70
+ RequireRatchet.notNullOrUndefined(file2, 'file2');
71
+ RequireRatchet.notNullOrUndefined(keyField, 'keyField');
72
+ Logger.info('Created csv compare with files %s and %s keyed on %s', file1, file2, keyField);
73
+ let file1Parsed = await this.streamParse(fs.createReadStream(file1), (f) => {
74
+ f;
75
+ });
76
+ file1Parsed = file1Parsed.map((m) => {
77
+ const next = {};
78
+ Object.keys(m).forEach((k) => {
79
+ next[k.trim()] = m[k];
80
+ });
81
+ return next;
82
+ });
83
+ const file1Mapped = MapRatchet.mapByUniqueProperty(file1Parsed, keyField);
84
+ let file2Parsed = await this.streamParse(fs.createReadStream(file2), (f) => {
85
+ f;
86
+ });
87
+ file2Parsed = file2Parsed.map((m) => {
88
+ const next = {};
89
+ Object.keys(m).forEach((k) => {
90
+ next[k.trim()] = m[k];
91
+ });
92
+ return next;
93
+ });
94
+ const file2Mapped = MapRatchet.mapByUniqueProperty(file2Parsed, keyField);
95
+ const f1Only = [];
96
+ const f2Only = [];
97
+ const both = [];
98
+ Array.from(file1Mapped.keys()).forEach((f1k) => {
99
+ if (file2Mapped.has(f1k)) {
100
+ both.push(f1k);
101
+ }
102
+ else {
103
+ f1Only.push(f1k);
104
+ }
105
+ });
106
+ Array.from(file2Mapped.keys()).forEach((f1k) => {
107
+ if (!file1Mapped.has(f1k)) {
108
+ f2Only.push(f1k);
109
+ }
110
+ });
111
+ const rval = {
112
+ file1Data: file1Mapped,
113
+ file2Data: file2Mapped,
114
+ file1OnlyKeys: f1Only,
115
+ file2OnlyKeys: f2Only,
116
+ bothFilesKeys: both,
117
+ };
118
+ return rval;
119
+ }
120
+ static async streamObjectsToCsv(srcSubject, output, inOpts) {
121
+ RequireRatchet.notNullOrUndefined(srcSubject, 'srcSubject');
122
+ RequireRatchet.notNullOrUndefined(output, 'output');
123
+ const opts = inOpts || CsvRatchet.defaultStringifyOptions();
124
+ Logger.silly('Running pipe to csv output : %j', opts);
125
+ let count = 0;
126
+ const genProm = new Promise((res, rej) => {
127
+ const stringifier = stringify(opts);
128
+ stringifier.on('error', (err) => {
129
+ if (sub) {
130
+ sub.unsubscribe();
131
+ }
132
+ rej(err);
133
+ });
134
+ stringifier.on('finish', () => {
135
+ if (sub) {
136
+ sub.unsubscribe();
137
+ }
138
+ res(count);
139
+ });
140
+ stringifier.pipe(output);
141
+ const sub = srcSubject.subscribe((next) => {
142
+ Logger.debug('Adding %j to csv', next);
143
+ count++;
144
+ stringifier.write(next);
145
+ }, (err) => {
146
+ Logger.error('Error generating : %s', err);
147
+ rej(err);
148
+ }, () => {
149
+ Logger.debug('Finished');
150
+ stringifier.end();
151
+ });
152
+ });
153
+ return genProm;
154
+ }
155
+ static defaultParseFunction(row) {
156
+ return row;
157
+ }
158
+ }
@@ -0,0 +1,56 @@
1
+ import { Logger } from '@bitblit/ratchet-common';
2
+ import fs from 'fs';
3
+ import { CliRatchet } from '../cli/cli-ratchet';
4
+ export class FilesToStaticClass {
5
+ static async process(fileNames, outClassName, outFileName = null) {
6
+ if (!fileNames) {
7
+ throw new Error('fileNames must be defined');
8
+ }
9
+ if (!outClassName) {
10
+ throw new Error('outClassName must be defined');
11
+ }
12
+ if (fileNames.length === 0) {
13
+ Logger.warn('Warning - no files supplied to process');
14
+ }
15
+ Logger.info('Generating class %s from files %j (output file: %s)', outClassName, fileNames, outFileName);
16
+ let rval = '/** \n';
17
+ rval += '* Holder for the constants to be used by consumers \n';
18
+ rval += '* Moves it into code so that it can survive a trip through WebPack \n';
19
+ rval += '*/ \n\n';
20
+ rval += 'export class ' + outClassName + ' { \n';
21
+ rval += ' public static readonly VALUES:Record<string, string> = { \n';
22
+ for (let i = 0; i < fileNames.length; i++) {
23
+ let contents = 'NOT-FOUND';
24
+ if (fs.existsSync(fileNames[i])) {
25
+ const trimmed = fileNames[i].substring(fileNames[i].lastIndexOf('/') + 1);
26
+ contents = fs.readFileSync(fileNames[i]).toString();
27
+ rval += i > 0 ? ',' : '';
28
+ rval += '"' + trimmed + '":' + JSON.stringify(contents) + '\n';
29
+ }
30
+ else {
31
+ Logger.warn('Could not find file %s', fileNames[i]);
32
+ }
33
+ }
34
+ rval += '}; \n';
35
+ rval += '}';
36
+ if (!!outFileName) {
37
+ Logger.info('Writing to %s', outFileName);
38
+ fs.writeFileSync(outFileName, rval);
39
+ }
40
+ return rval;
41
+ }
42
+ static async runFromCliArgs(args) {
43
+ if (args.length < 4) {
44
+ Logger.infoP('Usage: ratchet-files-to-static-class {outFileName} {outClassName} {file0} ... {fileN}');
45
+ return null;
46
+ }
47
+ else {
48
+ const idx = CliRatchet.indexOfCommandArgument('files-to-static-class');
49
+ const outFileName = process.argv[idx + 1];
50
+ const outClassName = process.argv[idx + 2];
51
+ const files = process.argv.slice(idx + 3);
52
+ Logger.info('Running FilesToStaticClass from command line arguments Target: %s TargetClass: %s InFiles: %j', outFileName, outClassName, files);
53
+ return FilesToStaticClass.process(files, outClassName, outFileName);
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,11 @@
1
+ export * from './build/ratchet-node-only-info';
2
+ export * from './ci/apply-ci-env-variables-to-files';
3
+ export * from './ci/ci-run-information-util';
4
+ export * from './ci/ci-run-information';
5
+ export * from './cli/abstract-ratchet-cli-handler';
6
+ export * from './cli/cli-ratchet';
7
+ export * from './cli/ratchet-cli-handler';
8
+ export * from './csv/csv-ratchet';
9
+ export * from './files/files-to-static-class';
10
+ export * from './third-party/git/git-ratchet';
11
+ export * from './third-party/slack/publish-ci-release-to-slack';
@@ -0,0 +1,69 @@
1
+ import process from 'child_process';
2
+ import { Logger } from '@bitblit/ratchet-common';
3
+ export class GitRatchet {
4
+ static async executeCommand(command, options) {
5
+ let dst = __dirname;
6
+ if (!!options && !!options.dst) {
7
+ dst = options.dst;
8
+ }
9
+ return new Promise((res, rej) => {
10
+ process.exec(command, { cwd: dst }, (err, stdout, stderr) => {
11
+ if (stdout === '') {
12
+ rej('this does not look like a git repo');
13
+ }
14
+ if (!!stderr) {
15
+ rej(stderr);
16
+ }
17
+ res(stdout);
18
+ });
19
+ });
20
+ }
21
+ static getCommandString(splitChar) {
22
+ return ('git log -1 --pretty=format:"' +
23
+ GitRatchet.PRETTY_FORMAT.join(splitChar) +
24
+ '"' +
25
+ ' && git rev-parse --abbrev-ref HEAD' +
26
+ ' && git tag --contains HEAD');
27
+ }
28
+ static async getLastCommitSwallowException(options = {}) {
29
+ let rval = null;
30
+ try {
31
+ rval = await this.getLastCommit(options);
32
+ }
33
+ catch (err) {
34
+ Logger.warn('Failed to fetch git data : %s', err, err);
35
+ }
36
+ return rval;
37
+ }
38
+ static async getLastCommit(options = {}) {
39
+ const command = GitRatchet.getCommandString(GitRatchet.SPLIT_CHARACTER);
40
+ const res = await GitRatchet.executeCommand(command, options);
41
+ const a = res.split(GitRatchet.SPLIT_CHARACTER);
42
+ const branchAndTags = a[a.length - 1].split('\n').filter((n) => n);
43
+ const branch = branchAndTags[0];
44
+ const tags = branchAndTags.slice(1);
45
+ const rval = {
46
+ shortHash: a[0],
47
+ hash: a[1],
48
+ subject: a[2],
49
+ sanitizedSubject: a[3],
50
+ body: a[4],
51
+ authoredOn: a[5],
52
+ committedOn: a[6],
53
+ author: {
54
+ name: a[7],
55
+ email: a[8],
56
+ },
57
+ committer: {
58
+ name: a[9],
59
+ email: a[10],
60
+ },
61
+ notes: a[11],
62
+ branch,
63
+ tags,
64
+ };
65
+ return rval;
66
+ }
67
+ }
68
+ GitRatchet.SPLIT_CHARACTER = '<##>';
69
+ GitRatchet.PRETTY_FORMAT = ['%h', '%H', '%s', '%f', '%b', '%at', '%ct', '%an', '%ae', '%cn', '%ce', '%N', ''];
@@ -0,0 +1,59 @@
1
+ import { DateTime } from 'luxon';
2
+ import { GlobalRatchet, Logger } from '@bitblit/ratchet-common';
3
+ import fetch from 'cross-fetch';
4
+ import util from 'util';
5
+ import { GitRatchet } from '../git/git-ratchet';
6
+ export class PublishCiReleaseToSlack {
7
+ static async process(slackHookUrl, timezone = 'America/Los_Angeles') {
8
+ if (!slackHookUrl) {
9
+ throw new Error('slackHookUrl must be defined');
10
+ }
11
+ const buildNum = GlobalRatchet.fetchGlobalVar('CIRCLE_BUILD_NUM');
12
+ const userName = GlobalRatchet.fetchGlobalVar('CIRCLE_USERNAME');
13
+ const projectName = GlobalRatchet.fetchGlobalVar('CIRCLE_PROJECT_REPONAME');
14
+ const branch = GlobalRatchet.fetchGlobalVar('CIRCLE_BRANCH') || '';
15
+ const tag = GlobalRatchet.fetchGlobalVar('CIRCLE_TAG') || '';
16
+ const sha1 = GlobalRatchet.fetchGlobalVar('CIRCLE_SHA1') || '';
17
+ const localTime = DateTime.local().setZone(timezone).toFormat('MMMM Do yyyy, h:mm:ss a z');
18
+ const gitData = await GitRatchet.getLastCommitSwallowException();
19
+ if (!buildNum || !userName || !projectName) {
20
+ throw new Error('CIRCLE_BUILD_NUM, CIRCLE_USERNAME, CIRCLE_PROJECT_REPONAME env vars not set - apparently not in a CircleCI environment');
21
+ }
22
+ Logger.info('Sending slack notification %j with build %s, branch %s, tag %s, sha %s, time: %s', buildNum, branch, tag, sha1, localTime);
23
+ let message = util.format('%s performed release %s on %s at %s', userName, tag + ' ' + branch, projectName, localTime);
24
+ if (!!gitData && !!gitData.subject) {
25
+ message += '\n\n' + gitData.subject;
26
+ }
27
+ const response = await fetch(slackHookUrl, {
28
+ method: 'POST',
29
+ mode: 'cors',
30
+ cache: 'no-cache',
31
+ headers: {
32
+ 'Content-Type': 'application/json',
33
+ },
34
+ redirect: 'follow',
35
+ body: JSON.stringify({ text: message }),
36
+ });
37
+ const bodyOut = await response.text();
38
+ Logger.info('Slack returned : %s', bodyOut);
39
+ return bodyOut;
40
+ }
41
+ static extractHookUrl() {
42
+ let rval = null;
43
+ if (process && process.argv && process.argv.length > 2) {
44
+ rval = process.argv[2];
45
+ }
46
+ return rval;
47
+ }
48
+ static async runFromCliArgs(args) {
49
+ Logger.info('Running PublishCiReleaseToSlack from command line arguments');
50
+ const hook = PublishCiReleaseToSlack.extractHookUrl();
51
+ if (!!hook) {
52
+ return PublishCiReleaseToSlack.process(hook);
53
+ }
54
+ else {
55
+ Logger.infoP('Usage : ratchet-publish-circle-ci-release-to-slack {hookUrl} ...');
56
+ return null;
57
+ }
58
+ }
59
+ }