release-please 14.11.2 → 14.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/CHANGELOG.md +14 -0
- package/README.md +1 -0
- package/build/src/factory.js +2 -0
- package/build/src/github.d.ts +55 -4
- package/build/src/github.js +126 -0
- package/build/src/manifest.d.ts +1 -0
- package/build/src/manifest.js +43 -14
- package/build/src/strategies/expo.d.ts +15 -0
- package/build/src/strategies/expo.js +51 -0
- package/build/src/strategies/node.d.ts +2 -1
- package/build/src/updaters/expo/app-json.d.ts +31 -0
- package/build/src/updaters/expo/app-json.js +67 -0
- package/build/src/util/pull-request-body.js +3 -0
- package/build/src/util/pull-request-overflow-handler.d.ts +54 -0
- package/build/src/util/pull-request-overflow-handler.js +78 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
[1]: https://www.npmjs.com/package/release-please?activeTab=versions
|
|
6
6
|
|
|
7
|
+
## [14.13.0](https://github.com/googleapis/release-please/compare/v14.12.0...v14.13.0) (2022-10-13)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* Handle extremely large pull request body fields ([#1689](https://github.com/googleapis/release-please/issues/1689)) ([ecc424d](https://github.com/googleapis/release-please/commit/ecc424db9a86e742eb6b4f6f9271a8eae13e4efc))
|
|
13
|
+
|
|
14
|
+
## [14.12.0](https://github.com/googleapis/release-please/compare/v14.11.2...v14.12.0) (2022-10-12)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* Added expo strategy and updater ([#1646](https://github.com/googleapis/release-please/issues/1646)) ([9cb84cb](https://github.com/googleapis/release-please/commit/9cb84cb18211c61ed94d856b936fff30036b0988))
|
|
20
|
+
|
|
7
21
|
## [14.11.2](https://github.com/googleapis/release-please/compare/v14.11.1...v14.11.2) (2022-10-11)
|
|
8
22
|
|
|
9
23
|
|
package/README.md
CHANGED
|
@@ -141,6 +141,7 @@ Release Please automates releases for the following flavors of repositories:
|
|
|
141
141
|
| `krm-blueprint` | [A kpt package, with 1 or more KRM files and a CHANGELOG.md](https://github.com/GoogleCloudPlatform/blueprints/tree/main/catalog/project) |
|
|
142
142
|
| `maven` | [Strategy for Maven projects, generates SNAPSHOT version after each release and updates `pom.xml` automatically](docs/java.md) |
|
|
143
143
|
| `node` | [A Node.js repository, with a package.json and CHANGELOG.md](https://github.com/yargs/yargs) |
|
|
144
|
+
| `expo` | [An Expo based React Native repository, with a package.json, app.json and CHANGELOG.md](https://github.com/yargs/yargs) |
|
|
144
145
|
| `ocaml` | [An OCaml repository, containing 1 or more opam or esy files and a CHANGELOG.md](https://github.com/grain-lang/binaryen.ml) |
|
|
145
146
|
| `php` | A repository with a composer.json and a CHANGELOG.md |
|
|
146
147
|
| `python` | [A Python repository, with a setup.py, setup.cfg, CHANGELOG.md](https://github.com/googleapis/python-storage) and optionally a pyproject.toml and a <project>/\_\_init\_\_.py |
|
package/build/src/factory.js
CHANGED
|
@@ -45,6 +45,7 @@ const helm_1 = require("./strategies/helm");
|
|
|
45
45
|
const elixir_1 = require("./strategies/elixir");
|
|
46
46
|
const dart_1 = require("./strategies/dart");
|
|
47
47
|
const node_1 = require("./strategies/node");
|
|
48
|
+
const expo_1 = require("./strategies/expo");
|
|
48
49
|
const always_bump_patch_1 = require("./versioning-strategies/always-bump-patch");
|
|
49
50
|
const service_pack_1 = require("./versioning-strategies/service-pack");
|
|
50
51
|
const dependency_manifest_1 = require("./versioning-strategies/dependency-manifest");
|
|
@@ -81,6 +82,7 @@ const releasers = {
|
|
|
81
82
|
}),
|
|
82
83
|
'krm-blueprint': options => new krm_blueprint_1.KRMBlueprint(options),
|
|
83
84
|
node: options => new node_1.Node(options),
|
|
85
|
+
expo: options => new expo_1.Expo(options),
|
|
84
86
|
ocaml: options => new ocaml_1.OCaml(options),
|
|
85
87
|
php: options => new php_1.PHP(options),
|
|
86
88
|
'php-yoshi': options => new php_yoshi_1.PHPYoshi(options),
|
package/build/src/github.d.ts
CHANGED
|
@@ -73,6 +73,10 @@ interface FileDiff {
|
|
|
73
73
|
readonly originalContent: string | null;
|
|
74
74
|
}
|
|
75
75
|
export declare type ChangeSet = Map<string, FileDiff>;
|
|
76
|
+
interface CreatePullRequestOptions {
|
|
77
|
+
fork?: boolean;
|
|
78
|
+
draft?: boolean;
|
|
79
|
+
}
|
|
76
80
|
export declare class GitHub {
|
|
77
81
|
readonly repository: Repository;
|
|
78
82
|
private octokit;
|
|
@@ -226,6 +230,7 @@ export declare class GitHub {
|
|
|
226
230
|
* @param {string} path The path to the file in the repository
|
|
227
231
|
* @param {string} branch The branch to fetch from
|
|
228
232
|
* @returns {GitHubFileContents}
|
|
233
|
+
* @throws {FileNotFoundError} if the file cannot be found
|
|
229
234
|
* @throws {GitHubAPIError} on other API errors
|
|
230
235
|
*/
|
|
231
236
|
getFileContentsOnBranch(path: string, branch: string): Promise<GitHubFileContents>;
|
|
@@ -281,6 +286,8 @@ export declare class GitHub {
|
|
|
281
286
|
/**
|
|
282
287
|
* Open a pull request
|
|
283
288
|
*
|
|
289
|
+
* @deprecated This logic is handled by the Manifest class now as it
|
|
290
|
+
* can be more complicated if the release notes are too big
|
|
284
291
|
* @param {ReleasePullRequest} releasePullRequest Pull request data to update
|
|
285
292
|
* @param {string} targetBranch The base branch of the pull request
|
|
286
293
|
* @param {GitHubPR} options The pull request options
|
|
@@ -291,10 +298,17 @@ export declare class GitHub {
|
|
|
291
298
|
fork?: boolean;
|
|
292
299
|
skipLabeling?: boolean;
|
|
293
300
|
}): Promise<PullRequest>;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
301
|
+
/**
|
|
302
|
+
* Open a pull request
|
|
303
|
+
*
|
|
304
|
+
* @param {PullRequest} pullRequest Pull request data to update
|
|
305
|
+
* @param {string} targetBranch The base branch of the pull request
|
|
306
|
+
* @param {string} message The commit message for the commit
|
|
307
|
+
* @param {Update[]} updates The files to update
|
|
308
|
+
* @param {CreatePullRequestOptions} options The pull request options
|
|
309
|
+
* @throws {GitHubAPIError} on an API error
|
|
310
|
+
*/
|
|
311
|
+
createPullRequest: (pullRequest: PullRequest, targetBranch: string, message: string, updates: Update[], options?: CreatePullRequestOptions | undefined) => Promise<PullRequest>;
|
|
298
312
|
/**
|
|
299
313
|
* Fetch a pull request given the pull number
|
|
300
314
|
* @param {number} number The pull request number
|
|
@@ -387,6 +401,43 @@ export declare class GitHub {
|
|
|
387
401
|
* @param {string} previousTag Optional. Name of previous tag to analyze commits since
|
|
388
402
|
*/
|
|
389
403
|
generateReleaseNotes(tagName: string, targetCommitish: string, previousTag?: string): Promise<string>;
|
|
404
|
+
/**
|
|
405
|
+
* Create a single file on a new branch based on an existing
|
|
406
|
+
* branch. This will force-push to that branch.
|
|
407
|
+
* @param {string} filename Filename with path in the repository
|
|
408
|
+
* @param {string} contents Contents of the file
|
|
409
|
+
* @param {string} newBranchName Name of the new branch
|
|
410
|
+
* @param {string} baseBranchName Name of the base branch (where
|
|
411
|
+
* new branch is forked from)
|
|
412
|
+
* @returns {string} HTML URL of the new file
|
|
413
|
+
*/
|
|
414
|
+
createFileOnNewBranch(filename: string, contents: string, newBranchName: string, baseBranchName: string): Promise<string>;
|
|
415
|
+
/**
|
|
416
|
+
* Helper to fetch the SHA of a branch
|
|
417
|
+
* @param {string} branchName The name of the branch
|
|
418
|
+
* @return {string | undefined} Returns the SHA of the branch
|
|
419
|
+
* or undefined if it can't be found.
|
|
420
|
+
*/
|
|
421
|
+
private getBranchSha;
|
|
422
|
+
/**
|
|
423
|
+
* Helper to fork a branch from an existing branch. Uses `force` so
|
|
424
|
+
* it will overwrite the contents of `targetBranchName` to match
|
|
425
|
+
* the current contents of `baseBranchName`.
|
|
426
|
+
*
|
|
427
|
+
* @param {string} targetBranchName The name of the new forked branch
|
|
428
|
+
* @param {string} baseBranchName The base branch from which to fork.
|
|
429
|
+
* @returns {string} The branch SHA
|
|
430
|
+
* @throws {ConfigurationError} if the base branch cannot be found.
|
|
431
|
+
*/
|
|
432
|
+
private forkBranch;
|
|
433
|
+
/**
|
|
434
|
+
* Helper to create a new branch from a given SHA.
|
|
435
|
+
* @param {string} branchName The new branch name
|
|
436
|
+
* @param {string} branchSha The SHA of the branch
|
|
437
|
+
* @returns {string} The SHA of the new branch
|
|
438
|
+
*/
|
|
439
|
+
private createNewBranch;
|
|
440
|
+
private updateBranchSha;
|
|
390
441
|
}
|
|
391
442
|
export declare const sleepInMs: (ms: number) => Promise<unknown>;
|
|
392
443
|
export {};
|
package/build/src/github.js
CHANGED
|
@@ -130,6 +130,16 @@ class GitHub {
|
|
|
130
130
|
this.logger.debug(`finding files by glob: ${glob}, ref: ${ref}, prefix: ${prefix}`);
|
|
131
131
|
return await this.fileCache.findFilesByGlob(glob, ref, prefix);
|
|
132
132
|
});
|
|
133
|
+
/**
|
|
134
|
+
* Open a pull request
|
|
135
|
+
*
|
|
136
|
+
* @param {PullRequest} pullRequest Pull request data to update
|
|
137
|
+
* @param {string} targetBranch The base branch of the pull request
|
|
138
|
+
* @param {string} message The commit message for the commit
|
|
139
|
+
* @param {Update[]} updates The files to update
|
|
140
|
+
* @param {CreatePullRequestOptions} options The pull request options
|
|
141
|
+
* @throws {GitHubAPIError} on an API error
|
|
142
|
+
*/
|
|
133
143
|
this.createPullRequest = wrapAsync(async (pullRequest, targetBranch, message, updates, options) => {
|
|
134
144
|
// Update the files for the release if not already supplied
|
|
135
145
|
const changes = await this.buildChangeSet(updates, targetBranch);
|
|
@@ -914,6 +924,7 @@ class GitHub {
|
|
|
914
924
|
* @param {string} path The path to the file in the repository
|
|
915
925
|
* @param {string} branch The branch to fetch from
|
|
916
926
|
* @returns {GitHubFileContents}
|
|
927
|
+
* @throws {FileNotFoundError} if the file cannot be found
|
|
917
928
|
* @throws {GitHubAPIError} on other API errors
|
|
918
929
|
*/
|
|
919
930
|
async getFileContentsOnBranch(path, branch) {
|
|
@@ -963,6 +974,8 @@ class GitHub {
|
|
|
963
974
|
/**
|
|
964
975
|
* Open a pull request
|
|
965
976
|
*
|
|
977
|
+
* @deprecated This logic is handled by the Manifest class now as it
|
|
978
|
+
* can be more complicated if the release notes are too big
|
|
966
979
|
* @param {ReleasePullRequest} releasePullRequest Pull request data to update
|
|
967
980
|
* @param {string} targetBranch The base branch of the pull request
|
|
968
981
|
* @param {GitHubPR} options The pull request options
|
|
@@ -1060,6 +1073,119 @@ class GitHub {
|
|
|
1060
1073
|
});
|
|
1061
1074
|
return resp.data.body;
|
|
1062
1075
|
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Create a single file on a new branch based on an existing
|
|
1078
|
+
* branch. This will force-push to that branch.
|
|
1079
|
+
* @param {string} filename Filename with path in the repository
|
|
1080
|
+
* @param {string} contents Contents of the file
|
|
1081
|
+
* @param {string} newBranchName Name of the new branch
|
|
1082
|
+
* @param {string} baseBranchName Name of the base branch (where
|
|
1083
|
+
* new branch is forked from)
|
|
1084
|
+
* @returns {string} HTML URL of the new file
|
|
1085
|
+
*/
|
|
1086
|
+
async createFileOnNewBranch(filename, contents, newBranchName, baseBranchName) {
|
|
1087
|
+
// create or update new branch to match base branch
|
|
1088
|
+
await this.forkBranch(newBranchName, baseBranchName);
|
|
1089
|
+
// use the single file upload API
|
|
1090
|
+
const { data: { content }, } = await this.octokit.repos.createOrUpdateFileContents({
|
|
1091
|
+
owner: this.repository.owner,
|
|
1092
|
+
repo: this.repository.repo,
|
|
1093
|
+
path: filename,
|
|
1094
|
+
// contents need to be base64 encoded
|
|
1095
|
+
content: Buffer.from(contents, 'binary').toString('base64'),
|
|
1096
|
+
message: 'Saving release notes',
|
|
1097
|
+
branch: newBranchName,
|
|
1098
|
+
});
|
|
1099
|
+
if (!(content === null || content === void 0 ? void 0 : content.html_url)) {
|
|
1100
|
+
throw new Error(`Failed to write to file: ${filename} on branch: ${newBranchName}`);
|
|
1101
|
+
}
|
|
1102
|
+
return content.html_url;
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Helper to fetch the SHA of a branch
|
|
1106
|
+
* @param {string} branchName The name of the branch
|
|
1107
|
+
* @return {string | undefined} Returns the SHA of the branch
|
|
1108
|
+
* or undefined if it can't be found.
|
|
1109
|
+
*/
|
|
1110
|
+
async getBranchSha(branchName) {
|
|
1111
|
+
this.logger.debug(`Looking up SHA for branch: ${branchName}`);
|
|
1112
|
+
try {
|
|
1113
|
+
const { data: { object: { sha }, }, } = await this.octokit.git.getRef({
|
|
1114
|
+
owner: this.repository.owner,
|
|
1115
|
+
repo: this.repository.repo,
|
|
1116
|
+
ref: `heads/${branchName}`,
|
|
1117
|
+
});
|
|
1118
|
+
this.logger.debug(`SHA for branch: ${sha}`);
|
|
1119
|
+
return sha;
|
|
1120
|
+
}
|
|
1121
|
+
catch (e) {
|
|
1122
|
+
if (e instanceof request_error_1.RequestError && e.status === 404) {
|
|
1123
|
+
this.logger.debug(`Branch: ${branchName} does not exist`);
|
|
1124
|
+
return undefined;
|
|
1125
|
+
}
|
|
1126
|
+
throw e;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Helper to fork a branch from an existing branch. Uses `force` so
|
|
1131
|
+
* it will overwrite the contents of `targetBranchName` to match
|
|
1132
|
+
* the current contents of `baseBranchName`.
|
|
1133
|
+
*
|
|
1134
|
+
* @param {string} targetBranchName The name of the new forked branch
|
|
1135
|
+
* @param {string} baseBranchName The base branch from which to fork.
|
|
1136
|
+
* @returns {string} The branch SHA
|
|
1137
|
+
* @throws {ConfigurationError} if the base branch cannot be found.
|
|
1138
|
+
*/
|
|
1139
|
+
async forkBranch(targetBranchName, baseBranchName) {
|
|
1140
|
+
const baseBranchSha = await this.getBranchSha(baseBranchName);
|
|
1141
|
+
if (!baseBranchSha) {
|
|
1142
|
+
// this is highly unlikely to be thrown as we will have
|
|
1143
|
+
// already attempted to read from the branch
|
|
1144
|
+
throw new errors_1.ConfigurationError(`Unable to find base branch: ${baseBranchName}`, 'core', `${this.repository.owner}/${this.repository.repo}`);
|
|
1145
|
+
}
|
|
1146
|
+
// see if newBranchName exists
|
|
1147
|
+
if (await this.getBranchSha(targetBranchName)) {
|
|
1148
|
+
// branch already exists, update it to the match the base branch
|
|
1149
|
+
const branchSha = await this.updateBranchSha(targetBranchName, baseBranchSha);
|
|
1150
|
+
this.logger.debug(`Updated ${targetBranchName} to match ${baseBranchName} at ${branchSha}`);
|
|
1151
|
+
return branchSha;
|
|
1152
|
+
}
|
|
1153
|
+
else {
|
|
1154
|
+
// branch does not exist, create a new branch from the base branch
|
|
1155
|
+
const branchSha = await this.createNewBranch(targetBranchName, baseBranchSha);
|
|
1156
|
+
this.logger.debug(`Forked ${targetBranchName} from ${baseBranchName} at ${branchSha}`);
|
|
1157
|
+
return branchSha;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Helper to create a new branch from a given SHA.
|
|
1162
|
+
* @param {string} branchName The new branch name
|
|
1163
|
+
* @param {string} branchSha The SHA of the branch
|
|
1164
|
+
* @returns {string} The SHA of the new branch
|
|
1165
|
+
*/
|
|
1166
|
+
async createNewBranch(branchName, branchSha) {
|
|
1167
|
+
this.logger.debug(`Creating new branch: ${branchName} at ${branchSha}`);
|
|
1168
|
+
const { data: { object: { sha }, }, } = await this.octokit.git.createRef({
|
|
1169
|
+
owner: this.repository.owner,
|
|
1170
|
+
repo: this.repository.repo,
|
|
1171
|
+
ref: `refs/heads/${branchName}`,
|
|
1172
|
+
sha: branchSha,
|
|
1173
|
+
});
|
|
1174
|
+
this.logger.debug(`New branch: ${branchName} at ${sha}`);
|
|
1175
|
+
return sha;
|
|
1176
|
+
}
|
|
1177
|
+
async updateBranchSha(branchName, branchSha) {
|
|
1178
|
+
this.logger.debug(`Updating branch ${branchName} to ${branchSha}`);
|
|
1179
|
+
const { data: { object: { sha }, }, } = await this.octokit.git.updateRef({
|
|
1180
|
+
owner: this.repository.owner,
|
|
1181
|
+
repo: this.repository.repo,
|
|
1182
|
+
ref: `heads/${branchName}`,
|
|
1183
|
+
sha: branchSha,
|
|
1184
|
+
force: true,
|
|
1185
|
+
});
|
|
1186
|
+
this.logger.debug(`Updated branch: ${branchName} to ${sha}`);
|
|
1187
|
+
return sha;
|
|
1188
|
+
}
|
|
1063
1189
|
}
|
|
1064
1190
|
exports.GitHub = GitHub;
|
|
1065
1191
|
/**
|
package/build/src/manifest.d.ts
CHANGED
|
@@ -211,6 +211,7 @@ export declare class Manifest {
|
|
|
211
211
|
readonly releaseSearchDepth: number;
|
|
212
212
|
readonly commitSearchDepth: number;
|
|
213
213
|
readonly logger: Logger;
|
|
214
|
+
private pullRequestOverflowHandler;
|
|
214
215
|
/**
|
|
215
216
|
* Create a Manifest from explicit config in code. This assumes that the
|
|
216
217
|
* repository has a single component at the root path.
|
package/build/src/manifest.js
CHANGED
|
@@ -21,10 +21,11 @@ const tag_name_1 = require("./util/tag-name");
|
|
|
21
21
|
const branch_name_1 = require("./util/branch-name");
|
|
22
22
|
const pull_request_title_1 = require("./util/pull-request-title");
|
|
23
23
|
const factory_1 = require("./factory");
|
|
24
|
-
const pull_request_body_1 = require("./util/pull-request-body");
|
|
25
24
|
const merge_1 = require("./plugins/merge");
|
|
26
25
|
const release_please_manifest_1 = require("./updaters/release-please-manifest");
|
|
27
26
|
const errors_1 = require("./errors");
|
|
27
|
+
const pull_request_overflow_handler_1 = require("./util/pull-request-overflow-handler");
|
|
28
|
+
const signoff_commit_message_1 = require("./util/signoff-commit-message");
|
|
28
29
|
exports.DEFAULT_RELEASE_PLEASE_CONFIG = 'release-please-config.json';
|
|
29
30
|
exports.DEFAULT_RELEASE_PLEASE_MANIFEST = '.release-please-manifest.json';
|
|
30
31
|
exports.ROOT_PROJECT_PATH = '.';
|
|
@@ -100,6 +101,7 @@ class Manifest {
|
|
|
100
101
|
repositoryConfig: this.repositoryConfig,
|
|
101
102
|
manifestPath: this.manifestPath,
|
|
102
103
|
}));
|
|
104
|
+
this.pullRequestOverflowHandler = new pull_request_overflow_handler_1.FilePullRequestOverflowHandler(this.github, this.logger);
|
|
103
105
|
}
|
|
104
106
|
/**
|
|
105
107
|
* Create a Manifest from config files in the repository.
|
|
@@ -451,11 +453,16 @@ class Manifest {
|
|
|
451
453
|
const openPullRequests = [];
|
|
452
454
|
const generator = this.github.pullRequestIterator(this.targetBranch, 'OPEN', Number.MAX_SAFE_INTEGER, false);
|
|
453
455
|
for await (const openPullRequest of generator) {
|
|
454
|
-
if (
|
|
455
|
-
hasAllLabels(this.snapshotLabels, openPullRequest.labels))
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
456
|
+
if (hasAllLabels(this.labels, openPullRequest.labels) ||
|
|
457
|
+
hasAllLabels(this.snapshotLabels, openPullRequest.labels)) {
|
|
458
|
+
const body = await this.pullRequestOverflowHandler.parseOverflow(openPullRequest);
|
|
459
|
+
if (body) {
|
|
460
|
+
// maybe replace with overflow body
|
|
461
|
+
openPullRequests.push({
|
|
462
|
+
...openPullRequest,
|
|
463
|
+
body: body.toString(),
|
|
464
|
+
});
|
|
465
|
+
}
|
|
459
466
|
}
|
|
460
467
|
}
|
|
461
468
|
this.logger.info(`found ${openPullRequests.length} open release pull requests.`);
|
|
@@ -467,9 +474,15 @@ class Manifest {
|
|
|
467
474
|
const closedGenerator = this.github.pullRequestIterator(this.targetBranch, 'CLOSED', 200, false);
|
|
468
475
|
for await (const closedPullRequest of closedGenerator) {
|
|
469
476
|
if (hasAllLabels([exports.SNOOZE_LABEL], closedPullRequest.labels) &&
|
|
470
|
-
branch_name_1.BranchName.parse(closedPullRequest.headBranchName, this.logger)
|
|
471
|
-
|
|
472
|
-
|
|
477
|
+
branch_name_1.BranchName.parse(closedPullRequest.headBranchName, this.logger)) {
|
|
478
|
+
const body = await this.pullRequestOverflowHandler.parseOverflow(closedPullRequest);
|
|
479
|
+
if (body) {
|
|
480
|
+
// maybe replace with overflow body
|
|
481
|
+
snoozedPullRequests.push({
|
|
482
|
+
...closedPullRequest,
|
|
483
|
+
body: body.toString(),
|
|
484
|
+
});
|
|
485
|
+
}
|
|
473
486
|
}
|
|
474
487
|
}
|
|
475
488
|
this.logger.info(`found ${snoozedPullRequests.length} snoozed release pull requests.`);
|
|
@@ -486,10 +499,21 @@ class Manifest {
|
|
|
486
499
|
if (snoozed) {
|
|
487
500
|
return await this.maybeUpdateSnoozedPullRequest(snoozed, pullRequest);
|
|
488
501
|
}
|
|
489
|
-
const
|
|
502
|
+
const body = await this.pullRequestOverflowHandler.handleOverflow(pullRequest);
|
|
503
|
+
const message = this.signoffUser
|
|
504
|
+
? (0, signoff_commit_message_1.signoffCommitMessage)(pullRequest.title.toString(), this.signoffUser)
|
|
505
|
+
: pullRequest.title.toString();
|
|
506
|
+
const newPullRequest = await this.github.createPullRequest({
|
|
507
|
+
headBranchName: pullRequest.headRefName,
|
|
508
|
+
baseBranchName: this.targetBranch,
|
|
509
|
+
number: -1,
|
|
510
|
+
title: pullRequest.title.toString(),
|
|
511
|
+
body,
|
|
512
|
+
labels: this.skipLabeling ? [] : pullRequest.labels,
|
|
513
|
+
files: [],
|
|
514
|
+
}, this.targetBranch, message, pullRequest.updates, {
|
|
490
515
|
fork: this.fork,
|
|
491
|
-
|
|
492
|
-
skipLabeling: this.skipLabeling,
|
|
516
|
+
draft: pullRequest.draft,
|
|
493
517
|
});
|
|
494
518
|
return newPullRequest;
|
|
495
519
|
}
|
|
@@ -529,12 +553,17 @@ class Manifest {
|
|
|
529
553
|
continue;
|
|
530
554
|
}
|
|
531
555
|
this.logger.debug(`Found pull request #${pullRequest.number}: '${pullRequest.title}'`);
|
|
532
|
-
|
|
556
|
+
// if the pull request body overflows, handle it
|
|
557
|
+
const pullRequestBody = await this.pullRequestOverflowHandler.parseOverflow(pullRequest);
|
|
533
558
|
if (!pullRequestBody) {
|
|
534
559
|
this.logger.debug('could not parse pull request body as a release PR');
|
|
535
560
|
continue;
|
|
536
561
|
}
|
|
537
|
-
|
|
562
|
+
// replace with the complete fetched body
|
|
563
|
+
yield {
|
|
564
|
+
...pullRequest,
|
|
565
|
+
body: pullRequestBody.toString(),
|
|
566
|
+
};
|
|
538
567
|
}
|
|
539
568
|
}
|
|
540
569
|
/**
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BuildUpdatesOptions } from './base';
|
|
2
|
+
import { Node } from './node';
|
|
3
|
+
import { Update } from '../update';
|
|
4
|
+
import { Version } from '../version';
|
|
5
|
+
/**
|
|
6
|
+
* Strategy for building Expo based React Native projects. This strategy extends
|
|
7
|
+
* the Node strategy to additionally update the `app.json` file of a project.
|
|
8
|
+
*/
|
|
9
|
+
export declare class Expo extends Node {
|
|
10
|
+
protected buildUpdates(options: BuildUpdatesOptions): Promise<Update[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Determine the Expo SDK version by parsing the package.json dependencies.
|
|
13
|
+
*/
|
|
14
|
+
getExpoSDKVersion(): Promise<Version>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2022 Google LLC
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.Expo = void 0;
|
|
17
|
+
const node_1 = require("./node");
|
|
18
|
+
const app_json_1 = require("../updaters/expo/app-json");
|
|
19
|
+
const version_1 = require("../version");
|
|
20
|
+
/**
|
|
21
|
+
* Strategy for building Expo based React Native projects. This strategy extends
|
|
22
|
+
* the Node strategy to additionally update the `app.json` file of a project.
|
|
23
|
+
*/
|
|
24
|
+
class Expo extends node_1.Node {
|
|
25
|
+
async buildUpdates(options) {
|
|
26
|
+
const version = options.newVersion;
|
|
27
|
+
const updates = await super.buildUpdates(options);
|
|
28
|
+
const expoSDKVersion = await this.getExpoSDKVersion();
|
|
29
|
+
updates.push({
|
|
30
|
+
path: this.addPath('app.json'),
|
|
31
|
+
createIfMissing: false,
|
|
32
|
+
updater: new app_json_1.AppJson({ version, expoSDKVersion }),
|
|
33
|
+
});
|
|
34
|
+
return updates;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Determine the Expo SDK version by parsing the package.json dependencies.
|
|
38
|
+
*/
|
|
39
|
+
async getExpoSDKVersion() {
|
|
40
|
+
var _a, _b, _c, _d;
|
|
41
|
+
const pkgJsonContents = await this.getPkgJsonContents();
|
|
42
|
+
const pkg = JSON.parse(pkgJsonContents.parsedContent);
|
|
43
|
+
return version_1.Version.parse(((_a = pkg.dependencies) === null || _a === void 0 ? void 0 : _a.expo) ||
|
|
44
|
+
((_b = pkg.devDependencies) === null || _b === void 0 ? void 0 : _b.expo) ||
|
|
45
|
+
((_c = pkg.peerDependencies) === null || _c === void 0 ? void 0 : _c.expo) ||
|
|
46
|
+
((_d = pkg.optionalDependencies) === null || _d === void 0 ? void 0 : _d.expo) ||
|
|
47
|
+
'0.0.0');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.Expo = Expo;
|
|
51
|
+
//# sourceMappingURL=expo.js.map
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { BaseStrategy, BuildUpdatesOptions } from './base';
|
|
2
2
|
import { Update } from '../update';
|
|
3
|
+
import { GitHubFileContents } from '@google-automations/git-file-utils';
|
|
3
4
|
export declare class Node extends BaseStrategy {
|
|
4
5
|
private pkgJsonContents?;
|
|
5
6
|
protected buildUpdates(options: BuildUpdatesOptions): Promise<Update[]>;
|
|
6
7
|
getDefaultPackageName(): Promise<string | undefined>;
|
|
7
8
|
protected normalizeComponent(component: string | undefined): string;
|
|
8
|
-
|
|
9
|
+
protected getPkgJsonContents(): Promise<GitHubFileContents>;
|
|
9
10
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Logger } from '../../util/logger';
|
|
2
|
+
import { DefaultUpdater, UpdateOptions } from '../default';
|
|
3
|
+
import { Version } from '../../version';
|
|
4
|
+
export interface AppJson {
|
|
5
|
+
expo: {
|
|
6
|
+
version: string;
|
|
7
|
+
ios?: {
|
|
8
|
+
buildNumber?: string;
|
|
9
|
+
};
|
|
10
|
+
android?: {
|
|
11
|
+
versionCode?: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface AppJsonOptions extends UpdateOptions {
|
|
16
|
+
expoSDKVersion: Version;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* This updates a React Natve Expo project app.json file's main, ios and android
|
|
20
|
+
* versions. All values except the `android.versionCode` are standard semver
|
|
21
|
+
* version numbers. For the `android.versionCode`, the semver number is used as
|
|
22
|
+
* the basis for the `versionCode`.
|
|
23
|
+
*/
|
|
24
|
+
export declare class AppJson extends DefaultUpdater {
|
|
25
|
+
expoSDKVersion: Version;
|
|
26
|
+
constructor(options: AppJsonOptions);
|
|
27
|
+
/**
|
|
28
|
+
* Given initial file contents, return updated contents.
|
|
29
|
+
*/
|
|
30
|
+
updateContent(content: string, logger?: Logger): string;
|
|
31
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2022 Google LLC
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.AppJson = void 0;
|
|
17
|
+
const json_stringify_1 = require("../../util/json-stringify");
|
|
18
|
+
const logger_1 = require("../../util/logger");
|
|
19
|
+
const default_1 = require("../default");
|
|
20
|
+
/**
|
|
21
|
+
* This updates a React Natve Expo project app.json file's main, ios and android
|
|
22
|
+
* versions. All values except the `android.versionCode` are standard semver
|
|
23
|
+
* version numbers. For the `android.versionCode`, the semver number is used as
|
|
24
|
+
* the basis for the `versionCode`.
|
|
25
|
+
*/
|
|
26
|
+
class AppJson extends default_1.DefaultUpdater {
|
|
27
|
+
constructor(options) {
|
|
28
|
+
super(options);
|
|
29
|
+
this.expoSDKVersion = options.expoSDKVersion;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Given initial file contents, return updated contents.
|
|
33
|
+
*/
|
|
34
|
+
updateContent(content, logger = logger_1.logger) {
|
|
35
|
+
var _a, _b;
|
|
36
|
+
const parsed = JSON.parse(content);
|
|
37
|
+
logger.info(`updating Expo version from ${parsed.expo.version} to ${this.version}`);
|
|
38
|
+
parsed.expo.version = this.version.toString();
|
|
39
|
+
if ((_a = parsed.expo.ios) === null || _a === void 0 ? void 0 : _a.buildNumber) {
|
|
40
|
+
logger.info(`updating iOS version from ${parsed.expo.ios.buildNumber} to ${this.version}`);
|
|
41
|
+
parsed.expo.ios.buildNumber = this.version.toString();
|
|
42
|
+
}
|
|
43
|
+
if ((_b = parsed.expo.android) === null || _b === void 0 ? void 0 : _b.versionCode) {
|
|
44
|
+
// Android versionCode
|
|
45
|
+
// https://developer.android.com/studio/publish/versioning#appversioning
|
|
46
|
+
let expoMajorVersion = 0;
|
|
47
|
+
try {
|
|
48
|
+
expoMajorVersion = this.expoSDKVersion.major;
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
// Rethrow with a nice error message.
|
|
52
|
+
throw new Error('Unable to determine the Expo SDK version for this project. Make sure that the expo package is installed for your project.');
|
|
53
|
+
}
|
|
54
|
+
// Implements the `versionCode` strategy described by Maxi Rosson
|
|
55
|
+
// @see https://medium.com/@maxirosson/versioning-android-apps-d6ec171cfd82
|
|
56
|
+
const versionCode = expoMajorVersion * 10000000 +
|
|
57
|
+
this.version.major * 10000 +
|
|
58
|
+
this.version.minor * 100 +
|
|
59
|
+
this.version.patch;
|
|
60
|
+
logger.info(`updating Android version from ${parsed.expo.android.versionCode} to ${versionCode}`);
|
|
61
|
+
parsed.expo.android.versionCode = versionCode.toString();
|
|
62
|
+
}
|
|
63
|
+
return (0, json_stringify_1.jsonStringify)(parsed, content);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.AppJson = AppJson;
|
|
67
|
+
//# sourceMappingURL=app-json.js.map
|
|
@@ -36,8 +36,10 @@ class PullRequestBody {
|
|
|
36
36
|
return undefined;
|
|
37
37
|
}
|
|
38
38
|
let data = extractMultipleReleases(parts.content, logger);
|
|
39
|
+
let useComponents = true;
|
|
39
40
|
if (data.length === 0) {
|
|
40
41
|
data = extractSingleRelease(parts.content, logger);
|
|
42
|
+
useComponents = false;
|
|
41
43
|
if (data.length === 0) {
|
|
42
44
|
logger.warn('Failed to parse releases.');
|
|
43
45
|
}
|
|
@@ -45,6 +47,7 @@ class PullRequestBody {
|
|
|
45
47
|
return new PullRequestBody(data, {
|
|
46
48
|
header: parts.header,
|
|
47
49
|
footer: parts.footer,
|
|
50
|
+
useComponents,
|
|
48
51
|
});
|
|
49
52
|
}
|
|
50
53
|
notes() {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { PullRequestBody } from './pull-request-body';
|
|
2
|
+
import { GitHub } from '../github';
|
|
3
|
+
import { PullRequest } from '../pull-request';
|
|
4
|
+
import { Logger } from './logger';
|
|
5
|
+
import { ReleasePullRequest } from '../release-pull-request';
|
|
6
|
+
/**
|
|
7
|
+
* Interface for managing the pull request body contents when the content
|
|
8
|
+
* is too large to fit into a pull request.
|
|
9
|
+
*/
|
|
10
|
+
export interface PullRequestOverflowHandler {
|
|
11
|
+
/**
|
|
12
|
+
* If a pull request's body is too big, store it somewhere and return
|
|
13
|
+
* a new pull request body with information about the new location.
|
|
14
|
+
* @param {ReleasePullRequest} pullRequest The candidate release pull request
|
|
15
|
+
* @returns {string} The new pull request body which may contain a link to
|
|
16
|
+
* the full content.
|
|
17
|
+
*/
|
|
18
|
+
handleOverflow(pullRequest: ReleasePullRequest, maxSize?: number): Promise<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Given a pull request, parse the pull request body from the pull request
|
|
21
|
+
* or storage if the body was too big to store in the pull request body.
|
|
22
|
+
* @param {PullRequest} pullRequest The pull request from GitHub
|
|
23
|
+
* @return {PullRequestBody} The parsed pull request body
|
|
24
|
+
*/
|
|
25
|
+
parseOverflow(pullRequest: PullRequest): Promise<PullRequestBody | undefined>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* This implementation of PullRequestOverflowHandler stores the full release
|
|
29
|
+
* notes on a new git branch. The branch name is derived from the head branch
|
|
30
|
+
* name of the release pull request.
|
|
31
|
+
*/
|
|
32
|
+
export declare class FilePullRequestOverflowHandler implements PullRequestOverflowHandler {
|
|
33
|
+
private github;
|
|
34
|
+
private logger;
|
|
35
|
+
constructor(github: GitHub, logger?: Logger);
|
|
36
|
+
/**
|
|
37
|
+
* Optionally store the full release notes into `release-notes.md` file
|
|
38
|
+
* on a new branch if they do not fit into the body of a pull request.
|
|
39
|
+
*
|
|
40
|
+
* The new release notes will have a link to the GitHub UI for that file
|
|
41
|
+
* which should render the release notes nicely.
|
|
42
|
+
* @param {ReleasePullRequest} pullRequest The candidate release pull request
|
|
43
|
+
* @returns {string} The new pull request body which contains a link to
|
|
44
|
+
* the full content.
|
|
45
|
+
*/
|
|
46
|
+
handleOverflow(pullRequest: ReleasePullRequest, maxSize?: number): Promise<string>;
|
|
47
|
+
/**
|
|
48
|
+
* Given a pull request, retrieve the full release notes from the stored
|
|
49
|
+
* file if the body was too big to store in the pull request body.
|
|
50
|
+
* @param {PullRequest} pullRequest The pull request from GitHub
|
|
51
|
+
* @return {PullRequestBody} The parsed pull request body
|
|
52
|
+
*/
|
|
53
|
+
parseOverflow(pullRequest: PullRequest): Promise<PullRequestBody | undefined>;
|
|
54
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2022 Google LLC
|
|
3
|
+
//
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// you may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
//
|
|
8
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
//
|
|
10
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
// See the License for the specific language governing permissions and
|
|
14
|
+
// limitations under the License.
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.FilePullRequestOverflowHandler = void 0;
|
|
17
|
+
const pull_request_body_1 = require("./pull-request-body");
|
|
18
|
+
const logger_1 = require("./logger");
|
|
19
|
+
const url_1 = require("url");
|
|
20
|
+
const MAX_ISSUE_BODY_SIZE = 65536;
|
|
21
|
+
const OVERFLOW_MESSAGE = 'This release is too large to preview in the pull request body. View the full release notes here:';
|
|
22
|
+
const OVERFLOW_MESSAGE_REGEX = new RegExp(`${OVERFLOW_MESSAGE} (?<url>.*)`);
|
|
23
|
+
const RELEASE_NOTES_FILENAME = 'release-notes.md';
|
|
24
|
+
const FILE_PATH_REGEX = new RegExp(`blob/(?<branchName>.*)/${RELEASE_NOTES_FILENAME}`);
|
|
25
|
+
/**
|
|
26
|
+
* This implementation of PullRequestOverflowHandler stores the full release
|
|
27
|
+
* notes on a new git branch. The branch name is derived from the head branch
|
|
28
|
+
* name of the release pull request.
|
|
29
|
+
*/
|
|
30
|
+
class FilePullRequestOverflowHandler {
|
|
31
|
+
constructor(github, logger = logger_1.logger) {
|
|
32
|
+
this.github = github;
|
|
33
|
+
this.logger = logger;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Optionally store the full release notes into `release-notes.md` file
|
|
37
|
+
* on a new branch if they do not fit into the body of a pull request.
|
|
38
|
+
*
|
|
39
|
+
* The new release notes will have a link to the GitHub UI for that file
|
|
40
|
+
* which should render the release notes nicely.
|
|
41
|
+
* @param {ReleasePullRequest} pullRequest The candidate release pull request
|
|
42
|
+
* @returns {string} The new pull request body which contains a link to
|
|
43
|
+
* the full content.
|
|
44
|
+
*/
|
|
45
|
+
async handleOverflow(pullRequest, maxSize = MAX_ISSUE_BODY_SIZE) {
|
|
46
|
+
const notes = pullRequest.body.toString();
|
|
47
|
+
if (notes.length > maxSize) {
|
|
48
|
+
const notesBranchName = `${pullRequest.headRefName}--release-notes`;
|
|
49
|
+
const url = await this.github.createFileOnNewBranch(RELEASE_NOTES_FILENAME, notes, notesBranchName, this.github.repository.defaultBranch);
|
|
50
|
+
return `${OVERFLOW_MESSAGE} ${url}`;
|
|
51
|
+
}
|
|
52
|
+
return notes;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Given a pull request, retrieve the full release notes from the stored
|
|
56
|
+
* file if the body was too big to store in the pull request body.
|
|
57
|
+
* @param {PullRequest} pullRequest The pull request from GitHub
|
|
58
|
+
* @return {PullRequestBody} The parsed pull request body
|
|
59
|
+
*/
|
|
60
|
+
async parseOverflow(pullRequest) {
|
|
61
|
+
var _a, _b;
|
|
62
|
+
const match = pullRequest.body.match(OVERFLOW_MESSAGE_REGEX);
|
|
63
|
+
if ((_a = match === null || match === void 0 ? void 0 : match.groups) === null || _a === void 0 ? void 0 : _a.url) {
|
|
64
|
+
this.logger.info(`Pull request body overflows, parsing full body from: ${match.groups.url}`);
|
|
65
|
+
const url = new url_1.URL(match.groups.url);
|
|
66
|
+
const pathMatch = url.pathname.match(FILE_PATH_REGEX);
|
|
67
|
+
if ((_b = pathMatch === null || pathMatch === void 0 ? void 0 : pathMatch.groups) === null || _b === void 0 ? void 0 : _b.branchName) {
|
|
68
|
+
const fileContents = await this.github.getFileContentsOnBranch(RELEASE_NOTES_FILENAME, pathMatch.groups.branchName);
|
|
69
|
+
return pull_request_body_1.PullRequestBody.parse(fileContents.parsedContent);
|
|
70
|
+
}
|
|
71
|
+
this.logger.warn(`Could not parse branch from ${match.groups.url}`);
|
|
72
|
+
return pull_request_body_1.PullRequestBody.parse(pullRequest.body, this.logger);
|
|
73
|
+
}
|
|
74
|
+
return pull_request_body_1.PullRequestBody.parse(pullRequest.body, this.logger);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.FilePullRequestOverflowHandler = FilePullRequestOverflowHandler;
|
|
78
|
+
//# sourceMappingURL=pull-request-overflow-handler.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "release-please",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.13.0",
|
|
4
4
|
"description": "generate release PRs based on the conventionalcommits.org spec",
|
|
5
5
|
"main": "./build/src/index.js",
|
|
6
6
|
"bin": "./build/src/bin/release-please.js",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@octokit/types": "^
|
|
41
|
+
"@octokit/types": "^8.0.0",
|
|
42
42
|
"@types/chai": "^4.1.7",
|
|
43
43
|
"@types/diff": "^5.0.2",
|
|
44
44
|
"@types/iarna__toml": "^2.0.1",
|