@api3/commons 0.12.0 → 0.13.1

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 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
 
@@ -3,4 +3,5 @@ export * from './logger';
3
3
  export * from './processing';
4
4
  export * from './config-parsing';
5
5
  export * from './config-hash';
6
+ export * from './release-scripts';
6
7
  //# sourceMappingURL=node-index.d.ts.map
@@ -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"}
@@ -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
@@ -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,2 @@
1
+ export * from './tag-and-release';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export declare const tagAndRelease: (repo: string, packageJsonPath: string, branch?: string) => Promise<void>;
2
+ //# sourceMappingURL=tag-and-release.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-and-release.d.ts","sourceRoot":"","sources":["../../src/release-scripts/tag-and-release.ts"],"names":[],"mappings":"AA2DA,eAAO,MAAM,aAAa,SAAgB,MAAM,mBAAmB,MAAM,WAAU,MAAM,kBAgCxF,CAAC"}
@@ -0,0 +1,87 @@
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 promise_utils_1 = require("@api3/promise-utils");
22
+ const rest_1 = require("@octokit/rest");
23
+ const execSyncWithErrorHandling = (command) => {
24
+ // eslint-disable-next-line functional/no-try-statements
25
+ try {
26
+ return (0, node_child_process_1.execSync)(command).toString();
27
+ }
28
+ catch (error) {
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
+ const createGithubRelease = async (repo, tagName) => {
36
+ if (!process.env.GH_ACCESS_TOKEN) {
37
+ console.info(`GH_ACCESS_TOKEN not set. Skipping release creation`);
38
+ return null;
39
+ }
40
+ // Ensure the GH_ACCESS_TOKEN secret is set on Github and has the relevant permissions
41
+ const octokit = new rest_1.Octokit({ auth: process.env.GH_ACCESS_TOKEN });
42
+ const createRelease = async () => octokit.rest.repos.createRelease({
43
+ owner: 'api3dao',
44
+ repo,
45
+ tag_name: tagName, // eslint-disable-line camelcase
46
+ generate_release_notes: true, // eslint-disable-line camelcase
47
+ });
48
+ console.info(`Creating Github release...`);
49
+ const goRes = await (0, promise_utils_1.go)(createRelease, { totalTimeoutMs: 15_000 });
50
+ if (!goRes.success) {
51
+ // We don't want to fail CI if the release fails to create. This can be done manually through Github's UI
52
+ console.info(`Unable to create Github release`);
53
+ console.info(goRes.error.message);
54
+ return null;
55
+ }
56
+ return goRes.data;
57
+ };
58
+ const tagAndRelease = async (repo, packageJsonPath, branch = 'main') => {
59
+ console.info('Ensuring working directory is clean...');
60
+ const gitStatus = execSyncWithErrorHandling('git status --porcelain');
61
+ if (gitStatus !== '')
62
+ throw new Error('Working directory is not clean');
63
+ console.info(`Ensuring we are on the ${branch} branch...`);
64
+ const currentBranch = execSyncWithErrorHandling('git branch --show-current');
65
+ if (currentBranch !== `${branch}\n`)
66
+ throw new Error(`Not on the ${branch} branch`);
67
+ console.info('Ensuring we are up to date with the remote...');
68
+ execSyncWithErrorHandling('git fetch');
69
+ const gitDiff = execSyncWithErrorHandling(`git diff origin/${branch}`);
70
+ if (gitDiff !== '')
71
+ throw new Error('Not up to date with the remote');
72
+ const packageJson = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, 'utf8'));
73
+ const { version } = packageJson;
74
+ console.info(`Version set to ${version}...`);
75
+ const gitTag = execSyncWithErrorHandling(`git tag -l '*v${version}*'`);
76
+ if (gitTag !== '')
77
+ throw new Error(`git tag v${version} already exists`);
78
+ console.info('Creating new annotated git tag...');
79
+ execSyncWithErrorHandling(`git tag -a v${version} -m "v${version}"`);
80
+ console.info('Pushing git tag...');
81
+ // NOTE: in order to push, a valid access token is expected as GH_ACCESS_TOKEN
82
+ execSyncWithErrorHandling(`git push origin v${version} --no-verify`);
83
+ await createGithubRelease(repo, `v${version}`);
84
+ console.info(`Done!`);
85
+ };
86
+ exports.tagAndRelease = tagAndRelease;
87
+ //# 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.12.0",
3
+ "version": "0.13.1",
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
- "axios": "^1.7.3",
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.13.1",
35
+ "winston": "^3.14.2",
34
36
  "winston-console-format": "^1.0.8",
35
37
  "zod": "^3.23.8"
36
38
  },
@@ -38,13 +40,14 @@
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.14.14",
43
+ "@types/node": "^20.16.5",
42
44
  "eslint": "^8.57.0",
43
- "husky": "^9.1.4",
45
+ "husky": "^9.1.5",
44
46
  "jest": "^29.7.0",
45
47
  "prettier": "^3.3.3",
46
- "ts-jest": "^29.2.4",
47
- "typescript": "^5.5.4"
48
+ "ts-jest": "^29.2.5",
49
+ "ts-node": "^10.9.2",
50
+ "typescript": "^5.6.2"
48
51
  },
49
52
  "scripts": {
50
53
  "build": "tsc --project tsconfig.build.json",
@@ -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
@@ -3,3 +3,4 @@ export * from './logger';
3
3
  export * from './processing';
4
4
  export * from './config-parsing';
5
5
  export * from './config-hash';
6
+ export * from './release-scripts';
@@ -0,0 +1,81 @@
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 and releases are created with the following naming scheme: `v1.2.3`. i.e. a `v` is prepended to the version
11
+ defined in `package.json`.
12
+
13
+ ### Usage
14
+
15
+ It is recommended to 1) create a script that imports and uses the `tagAndRelease` function as demonstrated below, 2)
16
+ define a script in `package.json`, and then 3) call that script as part of the CI process.
17
+
18
+ ```ts
19
+ // The following environment variable is expected. See the script itself for more details
20
+ //
21
+ // GH_ACCESS_TOKEN - created through the Github UI with relevant permissions to the repo. See the tag-and-release source for more information
22
+
23
+ // scripts/tag-and-release.ts
24
+ import { join } from 'node:path';
25
+
26
+ import { tagAndRelease } from '@api3/commons';
27
+
28
+ const main = async () => {
29
+ const packageJsonPath = join(__dirname, '../package.json'); // the script is one level deep in the repo
30
+ await tagAndRelease('my-repo-name', packageJsonPath, 'optional-branch-name-if-not-main');
31
+ };
32
+
33
+ main()
34
+ .then(() => process.exit(0))
35
+ .catch((error: unknown) => {
36
+ console.info(error);
37
+ process.exitCode = 1;
38
+ });
39
+
40
+ // package.json
41
+ {
42
+ "scripts": {
43
+ "release:tag": "ts-node scripts/tag-and-release.ts",
44
+ }
45
+ }
46
+ ```
47
+
48
+ It's also recommended to setup a step in CI that checks if the Git tag already exists before executing.
49
+
50
+ ```yml
51
+ # NOTE: irrelevant names and steps have been omitted such cloning, installing dependencies etc.
52
+ tag-and-release:
53
+ # Only tag and release on pushes to main (or the release branch)
54
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
55
+ steps:
56
+ - name: Clone repo
57
+ - name: Install pnpm
58
+ - name: Setup Node
59
+ - name: Install Dependencies
60
+ # Configure the Git user
61
+ - name: Configure Git credentials
62
+ run: |
63
+ git config --global user.name '${{ secrets.GH_USER_NAME }}'
64
+ git config --global user.email '${{ secrets.GH_USER_EMAIL }}'
65
+ # Get the version as defined in package.json
66
+ - name: Get package.json version
67
+ id: get-version
68
+ run: echo "version=$(cat package.json | jq -r '.version' | sed 's/^/v/')" >> $GITHUB_OUTPUT
69
+ # Check if a Git tag already exists with the pattern: `v{version}`
70
+ - name: Validate tag
71
+ id: validate-tag
72
+ run:
73
+ test "$(git tag -l '${{ steps.get-version.outputs.version }}' | awk '{print $NF}')" = "${{
74
+ steps.get-version.outputs.version }}" || echo "new-tag=true" >> $GITHUB_OUTPUT
75
+ # Run the tag-and-release script only if the tag does *not* already exist
76
+ - name: Tag and release on Github
77
+ if: ${{ steps.validate-tag.outputs.new-tag }}
78
+ run: pnpm run release:tag
79
+ env:
80
+ GH_ACCESS_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
81
+ ```
@@ -0,0 +1 @@
1
+ export * from './tag-and-release';
@@ -0,0 +1,92 @@
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
+
20
+ import { go } from '@api3/promise-utils';
21
+ import { Octokit } from '@octokit/rest';
22
+
23
+ const execSyncWithErrorHandling = (command: string) => {
24
+ // eslint-disable-next-line functional/no-try-statements
25
+ try {
26
+ return execSync(command).toString();
27
+ } catch (error: any) {
28
+ console.info(error.message);
29
+ console.info('STDOUT', error.stdout.toString());
30
+ console.info('STDERR', error.stderr.toString());
31
+ process.exit(1);
32
+ }
33
+ };
34
+
35
+ const createGithubRelease = async (repo: string, tagName: `v${string}`) => {
36
+ if (!process.env.GH_ACCESS_TOKEN) {
37
+ console.info(`GH_ACCESS_TOKEN not set. Skipping release creation`);
38
+ return null;
39
+ }
40
+ // Ensure the GH_ACCESS_TOKEN secret is set on Github and has the relevant permissions
41
+ const octokit = new Octokit({ auth: process.env.GH_ACCESS_TOKEN });
42
+ const createRelease = async () =>
43
+ 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 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
+
60
+ export const tagAndRelease = async (repo: string, packageJsonPath: string, branch: string = 'main') => {
61
+ console.info('Ensuring working directory is clean...');
62
+ const gitStatus = execSyncWithErrorHandling('git status --porcelain');
63
+ if (gitStatus !== '') throw new Error('Working directory is not clean');
64
+
65
+ console.info(`Ensuring we are on the ${branch} branch...`);
66
+ const currentBranch = execSyncWithErrorHandling('git branch --show-current');
67
+ if (currentBranch !== `${branch}\n`) throw new Error(`Not on the ${branch} branch`);
68
+
69
+ console.info('Ensuring we are up to date with the remote...');
70
+ execSyncWithErrorHandling('git fetch');
71
+
72
+ const gitDiff = execSyncWithErrorHandling(`git diff origin/${branch}`);
73
+ if (gitDiff !== '') throw new Error('Not up to date with the remote');
74
+
75
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as any;
76
+ const { version } = packageJson;
77
+ console.info(`Version set to ${version}...`);
78
+
79
+ const gitTag = execSyncWithErrorHandling(`git tag -l '*v${version}*'`);
80
+ if (gitTag !== '') throw new Error(`git tag v${version} already exists`);
81
+
82
+ console.info('Creating new annotated git tag...');
83
+ execSyncWithErrorHandling(`git tag -a v${version} -m "v${version}"`);
84
+
85
+ console.info('Pushing git tag...');
86
+ // NOTE: in order to push, a valid access token is expected as GH_ACCESS_TOKEN
87
+ execSyncWithErrorHandling(`git push origin v${version} --no-verify`);
88
+
89
+ await createGithubRelease(repo, `v${version}`);
90
+
91
+ console.info(`Done!`);
92
+ };