@api3/commons 0.12.0 → 0.13.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/README.md +1 -0
- package/dist/node-index.d.ts +1 -0
- package/dist/node-index.d.ts.map +1 -1
- package/dist/node-index.js +1 -0
- package/dist/node-index.js.map +1 -1
- package/dist/release-scripts/index.d.ts +2 -0
- package/dist/release-scripts/index.d.ts.map +1 -0
- package/dist/release-scripts/index.js +18 -0
- package/dist/release-scripts/index.js.map +1 -0
- package/dist/release-scripts/tag-and-release.d.ts +2 -0
- package/dist/release-scripts/tag-and-release.d.ts.map +1 -0
- package/dist/release-scripts/tag-and-release.js +88 -0
- package/dist/release-scripts/tag-and-release.js.map +1 -0
- package/package.json +10 -6
- package/src/node-index.ts +1 -0
- package/src/release-scripts/README.md +85 -0
- package/src/release-scripts/index.ts +1 -0
- package/src/release-scripts/tag-and-release.ts +93 -0
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ Read the documentation and sources of each module how to use it in the project.
|
|
|
25
25
|
- [http](./src/http/README.md)
|
|
26
26
|
- [logger](./src/logger/README.md)
|
|
27
27
|
- [processing](./src/processing/README.md)
|
|
28
|
+
- [release-scripts](./src/release-scripts/README.md)
|
|
28
29
|
- [run-in-loop](./src/run-in-loop/README.md)
|
|
29
30
|
- [utils](./src/utils/README.md)
|
|
30
31
|
|
package/dist/node-index.d.ts
CHANGED
package/dist/node-index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-index.d.ts","sourceRoot":"","sources":["../src/node-index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"node-index.d.ts","sourceRoot":"","sources":["../src/node-index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC"}
|
package/dist/node-index.js
CHANGED
|
@@ -19,4 +19,5 @@ __exportStar(require("./logger"), exports);
|
|
|
19
19
|
__exportStar(require("./processing"), exports);
|
|
20
20
|
__exportStar(require("./config-parsing"), exports);
|
|
21
21
|
__exportStar(require("./config-hash"), exports);
|
|
22
|
+
__exportStar(require("./release-scripts"), exports);
|
|
22
23
|
//# sourceMappingURL=node-index.js.map
|
package/dist/node-index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-index.js","sourceRoot":"","sources":["../src/node-index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oDAAkC;AAClC,2CAAyB;AACzB,+CAA6B;AAC7B,mDAAiC;AACjC,gDAA8B"}
|
|
1
|
+
{"version":3,"file":"node-index.js","sourceRoot":"","sources":["../src/node-index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oDAAkC;AAClC,2CAAyB;AACzB,+CAA6B;AAC7B,mDAAiC;AACjC,gDAA8B;AAC9B,oDAAkC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/release-scripts/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./tag-and-release"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/release-scripts/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oDAAkC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tag-and-release.d.ts","sourceRoot":"","sources":["../../src/release-scripts/tag-and-release.ts"],"names":[],"mappings":"AA4DA,eAAO,MAAM,aAAa,SAAgB,MAAM,WAAU,MAAM,kBAgC/D,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ########################################################################################
|
|
3
|
+
// This script uses the version defined in the root package.json to determine if a
|
|
4
|
+
// new Git tag and Github release should be created. The Github release notes are
|
|
5
|
+
// generated automatically using the commit information
|
|
6
|
+
//
|
|
7
|
+
// The following secrets are required:
|
|
8
|
+
//
|
|
9
|
+
// 1. GH_ACCESS_TOKEN - A "fine-grained personal access token" generated through the
|
|
10
|
+
// Github UI. It seems like these tokens are scoped to a user, rather than an
|
|
11
|
+
// organisation.
|
|
12
|
+
//
|
|
13
|
+
// The following minimum permissions are required:
|
|
14
|
+
// Read - access to metadata
|
|
15
|
+
// Read & write - access to actions and code
|
|
16
|
+
// #######################################################################################
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.tagAndRelease = void 0;
|
|
19
|
+
const node_child_process_1 = require("node:child_process");
|
|
20
|
+
const node_fs_1 = require("node:fs");
|
|
21
|
+
const node_path_1 = require("node:path");
|
|
22
|
+
const promise_utils_1 = require("@api3/promise-utils");
|
|
23
|
+
const rest_1 = require("@octokit/rest");
|
|
24
|
+
const execSyncWithErrorHandling = (command) => {
|
|
25
|
+
// eslint-disable-next-line functional/no-try-statements
|
|
26
|
+
try {
|
|
27
|
+
return (0, node_child_process_1.execSync)(command).toString();
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
console.info(error.message);
|
|
31
|
+
console.info('STDOUT', error.stdout.toString());
|
|
32
|
+
console.info('STDERR', error.stderr.toString());
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const createGithubRelease = async (repo, tagName) => {
|
|
37
|
+
if (!process.env.GH_ACCESS_TOKEN) {
|
|
38
|
+
console.info(`GH_ACCESS_TOKEN not set. Skipping release creation`);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
// Ensure the GH_ACCESS_TOKEN secret is set on Github and has the relevant permissions
|
|
42
|
+
const octokit = new rest_1.Octokit({ auth: process.env.GH_ACCESS_TOKEN });
|
|
43
|
+
const createRelease = async () => octokit.rest.repos.createRelease({
|
|
44
|
+
owner: 'api3dao',
|
|
45
|
+
repo,
|
|
46
|
+
tag_name: tagName, // eslint-disable-line camelcase
|
|
47
|
+
generate_release_notes: true, // eslint-disable-line camelcase
|
|
48
|
+
});
|
|
49
|
+
console.info(`Creating Github release...`);
|
|
50
|
+
const goRes = await (0, promise_utils_1.go)(createRelease, { totalTimeoutMs: 15_000 });
|
|
51
|
+
if (!goRes.success) {
|
|
52
|
+
// We don't want to fail CI if the release fails to create. This can be done manually through Github's UI
|
|
53
|
+
console.info(`Unable to create Github release`);
|
|
54
|
+
console.info(goRes.error.message);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return goRes.data;
|
|
58
|
+
};
|
|
59
|
+
const tagAndRelease = async (repo, branch = 'main') => {
|
|
60
|
+
console.info('Ensuring working directory is clean...');
|
|
61
|
+
const gitStatus = execSyncWithErrorHandling('git status --porcelain');
|
|
62
|
+
if (gitStatus !== '')
|
|
63
|
+
throw new Error('Working directory is not clean');
|
|
64
|
+
console.info(`Ensuring we are on the ${branch} branch...`);
|
|
65
|
+
const currentBranch = execSyncWithErrorHandling('git branch --show-current');
|
|
66
|
+
if (currentBranch !== `${branch}\n`)
|
|
67
|
+
throw new Error(`Not on the ${branch} branch`);
|
|
68
|
+
console.info('Ensuring we are up to date with the remote...');
|
|
69
|
+
execSyncWithErrorHandling('git fetch');
|
|
70
|
+
const gitDiff = execSyncWithErrorHandling(`git diff origin/${branch}`);
|
|
71
|
+
if (gitDiff !== '')
|
|
72
|
+
throw new Error('Not up to date with the remote');
|
|
73
|
+
const packageJson = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, '../../package.json'), 'utf8'));
|
|
74
|
+
const { version } = packageJson;
|
|
75
|
+
console.info(`Version set to ${version}...`);
|
|
76
|
+
const gitTag = execSyncWithErrorHandling(`git tag -l '*v${version}*'`);
|
|
77
|
+
if (gitTag !== '')
|
|
78
|
+
throw new Error(`git tag v${version} already exists`);
|
|
79
|
+
console.info('Creating new annotated git tag...');
|
|
80
|
+
execSyncWithErrorHandling(`git tag -a v${version} -m "v${version}"`);
|
|
81
|
+
console.info('Pushing git tag...');
|
|
82
|
+
// NOTE: in order to push, a valid access token is expected as GH_ACCESS_TOKEN
|
|
83
|
+
execSyncWithErrorHandling(`git push origin v${version} --no-verify`);
|
|
84
|
+
await createGithubRelease(repo, `v${version}`);
|
|
85
|
+
console.info(`Done!`);
|
|
86
|
+
};
|
|
87
|
+
exports.tagAndRelease = tagAndRelease;
|
|
88
|
+
//# sourceMappingURL=tag-and-release.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tag-and-release.js","sourceRoot":"","sources":["../../src/release-scripts/tag-and-release.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@api3/commons",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -26,11 +26,13 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@api3/ois": "^2.3.2",
|
|
28
28
|
"@api3/promise-utils": "^0.4.0",
|
|
29
|
-
"
|
|
29
|
+
"@octokit/rest": "^20.1.1",
|
|
30
|
+
"@octokit/types": "^13.5.0",
|
|
31
|
+
"axios": "^1.7.7",
|
|
30
32
|
"dotenv": "^16.4.5",
|
|
31
33
|
"ethers": "^5.7.2",
|
|
32
34
|
"lodash": "^4.17.21",
|
|
33
|
-
"winston": "^3.
|
|
35
|
+
"winston": "^3.14.2",
|
|
34
36
|
"winston-console-format": "^1.0.8",
|
|
35
37
|
"zod": "^3.23.8"
|
|
36
38
|
},
|
|
@@ -38,12 +40,13 @@
|
|
|
38
40
|
"@api3/eslint-plugin-commons": "^2.0.1",
|
|
39
41
|
"@types/jest": "^29.5.12",
|
|
40
42
|
"@types/lodash": "^4.17.7",
|
|
41
|
-
"@types/node": "^20.
|
|
43
|
+
"@types/node": "^20.16.3",
|
|
42
44
|
"eslint": "^8.57.0",
|
|
43
|
-
"husky": "^9.1.
|
|
45
|
+
"husky": "^9.1.5",
|
|
44
46
|
"jest": "^29.7.0",
|
|
45
47
|
"prettier": "^3.3.3",
|
|
46
|
-
"ts-jest": "^29.2.
|
|
48
|
+
"ts-jest": "^29.2.5",
|
|
49
|
+
"ts-node": "^10.9.2",
|
|
47
50
|
"typescript": "^5.5.4"
|
|
48
51
|
},
|
|
49
52
|
"scripts": {
|
|
@@ -53,6 +56,7 @@
|
|
|
53
56
|
"eslint:fix": "pnpm run eslint:check --fix",
|
|
54
57
|
"prettier:check": "prettier --check \"./**/*.{js,ts,md,json}\"",
|
|
55
58
|
"prettier:fix": "prettier --write \"./**/*.{js,ts,md,json}\"",
|
|
59
|
+
"release:tag": "ts-node scripts/tag-and-release.ts",
|
|
56
60
|
"test": "jest",
|
|
57
61
|
"tsc": "tsc --project ."
|
|
58
62
|
}
|
package/src/node-index.ts
CHANGED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Release Scripts
|
|
2
|
+
|
|
3
|
+
The following scripts are exposed for use:
|
|
4
|
+
|
|
5
|
+
## tagAndRelease
|
|
6
|
+
|
|
7
|
+
This script creates a Git tag and Github release for a given version. It is expected to be run as part of CI and only
|
|
8
|
+
once for the given version defined in `package.json`.
|
|
9
|
+
|
|
10
|
+
Git tags are created with the following naming scheme: `v1.2.3`. i.e. a `v` is prepended to the version defined in
|
|
11
|
+
`package.json`.
|
|
12
|
+
|
|
13
|
+
### Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
// The following environment variable is expected. See the script itself for more details
|
|
17
|
+
//
|
|
18
|
+
// GH_ACCESS_TOKEN - created through the Github UI with relevant permissions to the repo. See the tag-and-release source for more information
|
|
19
|
+
|
|
20
|
+
import { tagAndRelease } from '@api3/commons';
|
|
21
|
+
|
|
22
|
+
await tagAndRelease('my-repo-name'); // defaults to using the 'main' branch
|
|
23
|
+
await tagAndRelease('my-repo-name', 'release-branch');
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
It is advised to create a script that imports and uses the `tagAndRelease` function, setup a script in `package.json`
|
|
27
|
+
and then call that in CI.
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// scripts/tag-and-release.ts
|
|
31
|
+
import { tagAndRelease } from '@api3/commons';
|
|
32
|
+
|
|
33
|
+
const main = async () => {
|
|
34
|
+
await tagAndRelease('my-repo-name');
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
main()
|
|
38
|
+
.then(() => process.exit(0))
|
|
39
|
+
.catch((error: unknown) => {
|
|
40
|
+
console.info(error);
|
|
41
|
+
process.exitCode = 1;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// package.json
|
|
45
|
+
{
|
|
46
|
+
"scripts": {
|
|
47
|
+
"release:tag": "ts-node scripts/tag-and-release.ts",
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
It's also recommended to setup a step in CI that checks if the Git tag already exists before executing.
|
|
53
|
+
|
|
54
|
+
```yml
|
|
55
|
+
# NOTE: irrelevant names and steps have been omitted such cloning, installing dependencies etc.
|
|
56
|
+
tag-and-release:
|
|
57
|
+
# Only tag and release on pushes to main (or the release branch)
|
|
58
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
59
|
+
steps:
|
|
60
|
+
- name: Clone repo
|
|
61
|
+
- name: Install pnpm
|
|
62
|
+
- name: Setup Node
|
|
63
|
+
- name: Install Dependencies
|
|
64
|
+
# Configure the Git user
|
|
65
|
+
- name: Configure Git credentials
|
|
66
|
+
run: |
|
|
67
|
+
git config --global user.name '${{ secrets.GH_USER_NAME }}'
|
|
68
|
+
git config --global user.email '${{ secrets.GH_USER_EMAIL }}'
|
|
69
|
+
# Get the version as defined in package.json
|
|
70
|
+
- name: Get package.json version
|
|
71
|
+
id: get-version
|
|
72
|
+
run: echo "version=$(cat package.json | jq -r '.version' | sed 's/^/v/')" >> $GITHUB_OUTPUT
|
|
73
|
+
# Check if a Git tag already exists with the pattern: `v{version}`
|
|
74
|
+
- name: Validate tag
|
|
75
|
+
id: validate-tag
|
|
76
|
+
run:
|
|
77
|
+
test "$(git tag -l '${{ steps.get-version.outputs.version }}' | awk '{print $NF}')" = "${{
|
|
78
|
+
steps.get-version.outputs.version }}" || echo "new-tag=true" >> $GITHUB_OUTPUT
|
|
79
|
+
# Run the tag-and-release script only if the tag does *not* already exist
|
|
80
|
+
- name: Tag and release on Github
|
|
81
|
+
if: ${{ steps.validate-tag.outputs.new-tag }}
|
|
82
|
+
run: pnpm run release:tag
|
|
83
|
+
env:
|
|
84
|
+
GH_ACCESS_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
|
|
85
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './tag-and-release';
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// ########################################################################################
|
|
2
|
+
// This script uses the version defined in the root package.json to determine if a
|
|
3
|
+
// new Git tag and Github release should be created. The Github release notes are
|
|
4
|
+
// generated automatically using the commit information
|
|
5
|
+
//
|
|
6
|
+
// The following secrets are required:
|
|
7
|
+
//
|
|
8
|
+
// 1. GH_ACCESS_TOKEN - A "fine-grained personal access token" generated through the
|
|
9
|
+
// Github UI. It seems like these tokens are scoped to a user, rather than an
|
|
10
|
+
// organisation.
|
|
11
|
+
//
|
|
12
|
+
// The following minimum permissions are required:
|
|
13
|
+
// Read - access to metadata
|
|
14
|
+
// Read & write - access to actions and code
|
|
15
|
+
// #######################################################################################
|
|
16
|
+
|
|
17
|
+
import { execSync } from 'node:child_process';
|
|
18
|
+
import { readFileSync } from 'node:fs';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
|
|
21
|
+
import { go } from '@api3/promise-utils';
|
|
22
|
+
import { Octokit } from '@octokit/rest';
|
|
23
|
+
|
|
24
|
+
const execSyncWithErrorHandling = (command: string) => {
|
|
25
|
+
// eslint-disable-next-line functional/no-try-statements
|
|
26
|
+
try {
|
|
27
|
+
return execSync(command).toString();
|
|
28
|
+
} catch (error: any) {
|
|
29
|
+
console.info(error.message);
|
|
30
|
+
console.info('STDOUT', error.stdout.toString());
|
|
31
|
+
console.info('STDERR', error.stderr.toString());
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const createGithubRelease = async (repo: string, tagName: `v${string}`) => {
|
|
37
|
+
if (!process.env.GH_ACCESS_TOKEN) {
|
|
38
|
+
console.info(`GH_ACCESS_TOKEN not set. Skipping release creation`);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
// Ensure the GH_ACCESS_TOKEN secret is set on Github and has the relevant permissions
|
|
42
|
+
const octokit = new Octokit({ auth: process.env.GH_ACCESS_TOKEN });
|
|
43
|
+
const createRelease = async () =>
|
|
44
|
+
octokit.rest.repos.createRelease({
|
|
45
|
+
owner: 'api3dao',
|
|
46
|
+
repo,
|
|
47
|
+
tag_name: tagName, // eslint-disable-line camelcase
|
|
48
|
+
generate_release_notes: true, // eslint-disable-line camelcase
|
|
49
|
+
});
|
|
50
|
+
console.info(`Creating Github release...`);
|
|
51
|
+
const goRes = await go(createRelease, { totalTimeoutMs: 15_000 });
|
|
52
|
+
if (!goRes.success) {
|
|
53
|
+
// We don't want to fail CI if the release fails to create. This can be done manually through Github's UI
|
|
54
|
+
console.info(`Unable to create Github release`);
|
|
55
|
+
console.info(goRes.error.message);
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return goRes.data;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const tagAndRelease = async (repo: string, branch: string = 'main') => {
|
|
62
|
+
console.info('Ensuring working directory is clean...');
|
|
63
|
+
const gitStatus = execSyncWithErrorHandling('git status --porcelain');
|
|
64
|
+
if (gitStatus !== '') throw new Error('Working directory is not clean');
|
|
65
|
+
|
|
66
|
+
console.info(`Ensuring we are on the ${branch} branch...`);
|
|
67
|
+
const currentBranch = execSyncWithErrorHandling('git branch --show-current');
|
|
68
|
+
if (currentBranch !== `${branch}\n`) throw new Error(`Not on the ${branch} branch`);
|
|
69
|
+
|
|
70
|
+
console.info('Ensuring we are up to date with the remote...');
|
|
71
|
+
execSyncWithErrorHandling('git fetch');
|
|
72
|
+
|
|
73
|
+
const gitDiff = execSyncWithErrorHandling(`git diff origin/${branch}`);
|
|
74
|
+
if (gitDiff !== '') throw new Error('Not up to date with the remote');
|
|
75
|
+
|
|
76
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8')) as any;
|
|
77
|
+
const { version } = packageJson;
|
|
78
|
+
console.info(`Version set to ${version}...`);
|
|
79
|
+
|
|
80
|
+
const gitTag = execSyncWithErrorHandling(`git tag -l '*v${version}*'`);
|
|
81
|
+
if (gitTag !== '') throw new Error(`git tag v${version} already exists`);
|
|
82
|
+
|
|
83
|
+
console.info('Creating new annotated git tag...');
|
|
84
|
+
execSyncWithErrorHandling(`git tag -a v${version} -m "v${version}"`);
|
|
85
|
+
|
|
86
|
+
console.info('Pushing git tag...');
|
|
87
|
+
// NOTE: in order to push, a valid access token is expected as GH_ACCESS_TOKEN
|
|
88
|
+
execSyncWithErrorHandling(`git push origin v${version} --no-verify`);
|
|
89
|
+
|
|
90
|
+
await createGithubRelease(repo, `v${version}`);
|
|
91
|
+
|
|
92
|
+
console.info(`Done!`);
|
|
93
|
+
};
|