release-please 13.4.7 → 13.4.11
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 +36 -0
- package/build/src/errors/index.d.ts +4 -0
- package/build/src/errors/index.js +10 -1
- package/build/src/factory.js +1 -0
- package/build/src/github.d.ts +3 -23
- package/build/src/github.js +38 -92
- package/build/src/manifest.d.ts +4 -0
- package/build/src/manifest.js +56 -5
- package/build/src/strategies/base.js +15 -5
- package/build/src/strategies/go-yoshi.d.ts +1 -0
- package/build/src/strategies/go-yoshi.js +19 -18
- package/build/src/strategies/java-yoshi.d.ts +1 -1
- package/build/src/update.d.ts +1 -1
- package/build/src/util/file-cache.d.ts +104 -0
- package/build/src/util/file-cache.js +206 -0
- package/build/src/util/pull-request-body.js +23 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@
|
|
|
4
4
|
|
|
5
5
|
[1]: https://www.npmjs.com/package/release-please?activeTab=versions
|
|
6
6
|
|
|
7
|
+
### [13.4.11](https://github.com/googleapis/release-please/compare/v13.4.10...v13.4.11) (2022-02-18)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
* introduce file cache to simplify fetching files including file mode ([#1280](https://github.com/googleapis/release-please/issues/1280)) ([d7280b7](https://github.com/googleapis/release-please/commit/d7280b7eac3056e28399a0b80ea26002f0dff1b4))
|
|
13
|
+
|
|
14
|
+
### [13.4.10](https://github.com/googleapis/release-please/compare/v13.4.9...v13.4.10) (2022-02-16)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* **go-yoshi:** dynamically load list of ignored submodules for google-cloud-go ([#1291](https://github.com/googleapis/release-please/issues/1291)) ([36f6ad9](https://github.com/googleapis/release-please/commit/36f6ad94fe471e5a46cc46ebd6f5b5c581a29c2c))
|
|
20
|
+
* manifest release can handle componentless entry ([#1300](https://github.com/googleapis/release-please/issues/1300)) ([6b58573](https://github.com/googleapis/release-please/commit/6b585734fe7b49f0e351b73b27260a304d6c80dd))
|
|
21
|
+
* return uploadUrl and body when creating release ([#1298](https://github.com/googleapis/release-please/issues/1298)) ([5d767c5](https://github.com/googleapis/release-please/commit/5d767c536594a8e24d274a4268cda1f1aa3babff))
|
|
22
|
+
|
|
23
|
+
### [13.4.9](https://github.com/googleapis/release-please/compare/v13.4.8...v13.4.9) (2022-02-14)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Bug Fixes
|
|
27
|
+
|
|
28
|
+
* standalone releases should only be released by its matching component ([#1296](https://github.com/googleapis/release-please/issues/1296)) ([75dd686](https://github.com/googleapis/release-please/commit/75dd686a667da397b54498f543128d4cc6bb784e))
|
|
29
|
+
|
|
30
|
+
### [13.4.8](https://github.com/googleapis/release-please/compare/v13.4.7...v13.4.8) (2022-02-08)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### Bug Fixes
|
|
34
|
+
|
|
35
|
+
* allow configuring includeComponentInTag and tagSeparator from manifest config ([71d9b6d](https://github.com/googleapis/release-please/commit/71d9b6d5775bb1a35157c7ec512ef4d1d9f7feec))
|
|
36
|
+
* check recent commits and latest releases for latest version ([#1267](https://github.com/googleapis/release-please/issues/1267)) ([f931842](https://github.com/googleapis/release-please/commit/f931842a117c97dd117f161c89beb1c9e2257fa2))
|
|
37
|
+
* correctly fetch full list of files ([71d9b6d](https://github.com/googleapis/release-please/commit/71d9b6d5775bb1a35157c7ec512ef4d1d9f7feec))
|
|
38
|
+
* **go-yoshi:** allows using go-yoshi with manifest ([#1287](https://github.com/googleapis/release-please/issues/1287)) ([71d9b6d](https://github.com/googleapis/release-please/commit/71d9b6d5775bb1a35157c7ec512ef4d1d9f7feec))
|
|
39
|
+
* **go-yoshi:** should not always skip modules ([71d9b6d](https://github.com/googleapis/release-please/commit/71d9b6d5775bb1a35157c7ec512ef4d1d9f7feec))
|
|
40
|
+
* Manifest should be able to find tagged versions without a release ([71d9b6d](https://github.com/googleapis/release-please/commit/71d9b6d5775bb1a35157c7ec512ef4d1d9f7feec))
|
|
41
|
+
* provide pull request to commit parser allow overrides ([#1285](https://github.com/googleapis/release-please/issues/1285)) ([e54028b](https://github.com/googleapis/release-please/commit/e54028bb39c4535f42b0b90c60b7f331847d005c))
|
|
42
|
+
|
|
7
43
|
### [13.4.7](https://github.com/googleapis/release-please/compare/v13.4.6...v13.4.7) (2022-02-02)
|
|
8
44
|
|
|
9
45
|
|
|
@@ -29,4 +29,8 @@ export declare class DuplicateReleaseError extends GitHubAPIError {
|
|
|
29
29
|
tag: string;
|
|
30
30
|
constructor(requestError: RequestError, tag: string);
|
|
31
31
|
}
|
|
32
|
+
export declare class FileNotFoundError extends Error {
|
|
33
|
+
path: string;
|
|
34
|
+
constructor(path: string);
|
|
35
|
+
}
|
|
32
36
|
export {};
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// See the License for the specific language governing permissions and
|
|
14
14
|
// limitations under the License.
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.DuplicateReleaseError = exports.AuthError = exports.GitHubAPIError = exports.MissingRequiredFileError = exports.ConfigurationError = void 0;
|
|
16
|
+
exports.FileNotFoundError = exports.DuplicateReleaseError = exports.AuthError = exports.GitHubAPIError = exports.MissingRequiredFileError = exports.ConfigurationError = void 0;
|
|
17
17
|
class ConfigurationError extends Error {
|
|
18
18
|
constructor(message, releaserName, repository) {
|
|
19
19
|
super(`${releaserName} (${repository}): ${message}`);
|
|
@@ -38,6 +38,7 @@ class GitHubAPIError extends Error {
|
|
|
38
38
|
this.body = GitHubAPIError.parseErrorBody(requestError);
|
|
39
39
|
this.name = GitHubAPIError.name;
|
|
40
40
|
this.cause = requestError;
|
|
41
|
+
this.stack = requestError.stack;
|
|
41
42
|
}
|
|
42
43
|
static parseErrorBody(requestError) {
|
|
43
44
|
const body = requestError.response;
|
|
@@ -65,4 +66,12 @@ class DuplicateReleaseError extends GitHubAPIError {
|
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
exports.DuplicateReleaseError = DuplicateReleaseError;
|
|
69
|
+
class FileNotFoundError extends Error {
|
|
70
|
+
constructor(path) {
|
|
71
|
+
super(`Failed to find file: ${path}`);
|
|
72
|
+
this.path = path;
|
|
73
|
+
this.name = FileNotFoundError.name;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.FileNotFoundError = FileNotFoundError;
|
|
68
77
|
//# sourceMappingURL=index.js.map
|
package/build/src/factory.js
CHANGED
|
@@ -123,6 +123,7 @@ async function buildStrategy(options) {
|
|
|
123
123
|
changelogNotes,
|
|
124
124
|
pullRequestTitlePattern: options.pullRequestTitlePattern,
|
|
125
125
|
extraFiles: options.extraFiles,
|
|
126
|
+
tagSeparator: options.tagSeparator,
|
|
126
127
|
};
|
|
127
128
|
switch (options.releaseType) {
|
|
128
129
|
case 'ruby': {
|
package/build/src/github.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { Repository } from './repository';
|
|
|
9
9
|
import { ReleasePullRequest } from './release-pull-request';
|
|
10
10
|
import { Update } from './update';
|
|
11
11
|
import { Release } from './release';
|
|
12
|
+
import { GitHubFileContents } from './util/file-cache';
|
|
12
13
|
declare type RequestBuilderType = typeof request;
|
|
13
14
|
declare type DefaultFunctionType = RequestBuilderType['defaults'];
|
|
14
15
|
declare type RequestFunctionType = ReturnType<DefaultFunctionType>;
|
|
@@ -30,11 +31,6 @@ interface GitHubCreateOptions {
|
|
|
30
31
|
octokitAPIs?: OctokitAPIs;
|
|
31
32
|
token?: string;
|
|
32
33
|
}
|
|
33
|
-
export interface GitHubFileContents {
|
|
34
|
-
sha: string;
|
|
35
|
-
content: string;
|
|
36
|
-
parsedContent: string;
|
|
37
|
-
}
|
|
38
34
|
declare type CommitFilter = (commit: Commit) => boolean;
|
|
39
35
|
interface CommitIteratorOptions {
|
|
40
36
|
maxResults?: number;
|
|
@@ -57,6 +53,7 @@ export interface GitHubRelease {
|
|
|
57
53
|
notes?: string;
|
|
58
54
|
url: string;
|
|
59
55
|
draft?: boolean;
|
|
56
|
+
uploadUrl?: string;
|
|
60
57
|
}
|
|
61
58
|
export interface GitHubTag {
|
|
62
59
|
name: string;
|
|
@@ -67,6 +64,7 @@ export declare class GitHub {
|
|
|
67
64
|
private octokit;
|
|
68
65
|
private request;
|
|
69
66
|
private graphql;
|
|
67
|
+
private fileCache;
|
|
70
68
|
private constructor();
|
|
71
69
|
/**
|
|
72
70
|
* Build a new GitHub client with auto-detected default branch.
|
|
@@ -184,24 +182,6 @@ export declare class GitHub {
|
|
|
184
182
|
* @throws {GitHubAPIError} on other API errors
|
|
185
183
|
*/
|
|
186
184
|
getFileContents(path: string): Promise<GitHubFileContents>;
|
|
187
|
-
/**
|
|
188
|
-
* Fetch the contents of a file with the Contents API
|
|
189
|
-
*
|
|
190
|
-
* @param {string} path The path to the file in the repository
|
|
191
|
-
* @param {string} branch The branch to fetch from
|
|
192
|
-
* @returns {GitHubFileContents}
|
|
193
|
-
* @throws {GitHubAPIError} on other API errors
|
|
194
|
-
*/
|
|
195
|
-
getFileContentsWithSimpleAPI: (path: string, ref: string, isBranch?: any) => Promise<GitHubFileContents>;
|
|
196
|
-
/**
|
|
197
|
-
* Fetch the contents of a file using the Git data API
|
|
198
|
-
*
|
|
199
|
-
* @param {string} path The path to the file in the repository
|
|
200
|
-
* @param {string} branch The branch to fetch from
|
|
201
|
-
* @returns {GitHubFileContents}
|
|
202
|
-
* @throws {GitHubAPIError} on other API errors
|
|
203
|
-
*/
|
|
204
|
-
getFileContentsWithDataAPI: (path: string, branch: string) => Promise<GitHubFileContents>;
|
|
205
185
|
/**
|
|
206
186
|
* Fetch the contents of a file
|
|
207
187
|
*
|
package/build/src/github.js
CHANGED
|
@@ -26,6 +26,7 @@ exports.GH_GRAPHQL_URL = 'https://api.github.com';
|
|
|
26
26
|
const logger_1 = require("./util/logger");
|
|
27
27
|
const manifest_1 = require("./manifest");
|
|
28
28
|
const signoff_commit_message_1 = require("./util/signoff-commit-message");
|
|
29
|
+
const file_cache_1 = require("./util/file-cache");
|
|
29
30
|
class GitHub {
|
|
30
31
|
constructor(options) {
|
|
31
32
|
/**
|
|
@@ -37,13 +38,25 @@ class GitHub {
|
|
|
37
38
|
*/
|
|
38
39
|
this.getCommitFiles = wrapAsync(async (sha) => {
|
|
39
40
|
logger_1.logger.debug(`Backfilling file list for commit: ${sha}`);
|
|
40
|
-
const
|
|
41
|
+
const files = [];
|
|
42
|
+
for await (const resp of this.octokit.paginate.iterator(this.octokit.repos.getCommit, {
|
|
41
43
|
owner: this.repository.owner,
|
|
42
44
|
repo: this.repository.repo,
|
|
43
45
|
ref: sha,
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
})) {
|
|
47
|
+
for (const f of resp.data.files || []) {
|
|
48
|
+
if (f.filename) {
|
|
49
|
+
files.push(f.filename);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (files.length >= 3000) {
|
|
54
|
+
logger_1.logger.warn(`Found ${files.length} files. This may not include all the files.`);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
logger_1.logger.debug(`Found ${files.length} files`);
|
|
58
|
+
}
|
|
59
|
+
return files;
|
|
47
60
|
});
|
|
48
61
|
this.graphqlRequest = wrapAsync(async (opts, maxRetries = 1) => {
|
|
49
62
|
while (maxRetries >= 0) {
|
|
@@ -58,58 +71,6 @@ class GitHub {
|
|
|
58
71
|
maxRetries -= 1;
|
|
59
72
|
}
|
|
60
73
|
});
|
|
61
|
-
/**
|
|
62
|
-
* Fetch the contents of a file with the Contents API
|
|
63
|
-
*
|
|
64
|
-
* @param {string} path The path to the file in the repository
|
|
65
|
-
* @param {string} branch The branch to fetch from
|
|
66
|
-
* @returns {GitHubFileContents}
|
|
67
|
-
* @throws {GitHubAPIError} on other API errors
|
|
68
|
-
*/
|
|
69
|
-
this.getFileContentsWithSimpleAPI = wrapAsync(async (path, ref, isBranch = true) => {
|
|
70
|
-
ref = isBranch ? fullyQualifyBranchRef(ref) : ref;
|
|
71
|
-
const options = {
|
|
72
|
-
owner: this.repository.owner,
|
|
73
|
-
repo: this.repository.repo,
|
|
74
|
-
path,
|
|
75
|
-
ref,
|
|
76
|
-
};
|
|
77
|
-
const resp = await this.request('GET /repos/:owner/:repo/contents/:path', options);
|
|
78
|
-
return {
|
|
79
|
-
parsedContent: Buffer.from(resp.data.content, 'base64').toString('utf8'),
|
|
80
|
-
content: resp.data.content,
|
|
81
|
-
sha: resp.data.sha,
|
|
82
|
-
};
|
|
83
|
-
});
|
|
84
|
-
/**
|
|
85
|
-
* Fetch the contents of a file using the Git data API
|
|
86
|
-
*
|
|
87
|
-
* @param {string} path The path to the file in the repository
|
|
88
|
-
* @param {string} branch The branch to fetch from
|
|
89
|
-
* @returns {GitHubFileContents}
|
|
90
|
-
* @throws {GitHubAPIError} on other API errors
|
|
91
|
-
*/
|
|
92
|
-
this.getFileContentsWithDataAPI = wrapAsync(async (path, branch) => {
|
|
93
|
-
const repoTree = await this.octokit.git.getTree({
|
|
94
|
-
owner: this.repository.owner,
|
|
95
|
-
repo: this.repository.repo,
|
|
96
|
-
tree_sha: branch,
|
|
97
|
-
});
|
|
98
|
-
const blobDescriptor = repoTree.data.tree.find(tree => tree.path === path);
|
|
99
|
-
if (!blobDescriptor) {
|
|
100
|
-
throw new Error(`Could not find requested path: ${path}`);
|
|
101
|
-
}
|
|
102
|
-
const resp = await this.octokit.git.getBlob({
|
|
103
|
-
owner: this.repository.owner,
|
|
104
|
-
repo: this.repository.repo,
|
|
105
|
-
file_sha: blobDescriptor.sha,
|
|
106
|
-
});
|
|
107
|
-
return {
|
|
108
|
-
parsedContent: Buffer.from(resp.data.content, 'base64').toString('utf8'),
|
|
109
|
-
content: resp.data.content,
|
|
110
|
-
sha: resp.data.sha,
|
|
111
|
-
};
|
|
112
|
-
});
|
|
113
74
|
/**
|
|
114
75
|
* Returns a list of paths to all files with a given name.
|
|
115
76
|
*
|
|
@@ -312,9 +273,13 @@ class GitHub {
|
|
|
312
273
|
name: resp.data.name || undefined,
|
|
313
274
|
tagName: resp.data.tag_name,
|
|
314
275
|
sha: resp.data.target_commitish,
|
|
315
|
-
notes: resp.data.body_text
|
|
276
|
+
notes: resp.data.body_text ||
|
|
277
|
+
resp.data.body ||
|
|
278
|
+
resp.data.body_html ||
|
|
279
|
+
undefined,
|
|
316
280
|
url: resp.data.html_url,
|
|
317
281
|
draft: resp.data.draft,
|
|
282
|
+
uploadUrl: resp.data.upload_url,
|
|
318
283
|
};
|
|
319
284
|
}, e => {
|
|
320
285
|
if (e instanceof request_error_1.RequestError) {
|
|
@@ -383,6 +348,7 @@ class GitHub {
|
|
|
383
348
|
this.octokit = options.octokitAPIs.octokit;
|
|
384
349
|
this.request = options.octokitAPIs.request;
|
|
385
350
|
this.graphql = options.octokitAPIs.graphql;
|
|
351
|
+
this.fileCache = new file_cache_1.RepositoryFileCache(this.octokit, this.repository);
|
|
386
352
|
}
|
|
387
353
|
/**
|
|
388
354
|
* Build a new GitHub client with auto-detected default branch.
|
|
@@ -509,6 +475,7 @@ class GitHub {
|
|
|
509
475
|
}
|
|
510
476
|
}
|
|
511
477
|
async mergeCommitsGraphQL(targetBranch, cursor, options = {}) {
|
|
478
|
+
var _a;
|
|
512
479
|
logger_1.logger.debug(`Fetching merge commits on branch ${targetBranch} with cursor: ${cursor}`);
|
|
513
480
|
const response = await this.graphqlRequest({
|
|
514
481
|
query: `query pullRequestsSince($owner: String!, $repo: String!, $num: Int!, $maxFilesChanged: Int, $targetBranch: String!, $cursor: String) {
|
|
@@ -562,7 +529,7 @@ class GitHub {
|
|
|
562
529
|
repo: this.repository.repo,
|
|
563
530
|
num: 25,
|
|
564
531
|
targetBranch,
|
|
565
|
-
maxFilesChanged:
|
|
532
|
+
maxFilesChanged: 100,
|
|
566
533
|
});
|
|
567
534
|
// if the branch does exist, return null
|
|
568
535
|
if (!response.repository.ref) {
|
|
@@ -592,9 +559,15 @@ class GitHub {
|
|
|
592
559
|
labels: pullRequest.labels.nodes.map(node => node.name),
|
|
593
560
|
files,
|
|
594
561
|
};
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
562
|
+
if (((_a = pullRequest.files.pageInfo) === null || _a === void 0 ? void 0 : _a.hasNextPage) && options.backfillFiles) {
|
|
563
|
+
logger_1.logger.info(`PR #${pullRequest.number} has many files, backfilling`);
|
|
564
|
+
commit.files = await this.getCommitFiles(graphCommit.sha);
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
// We cannot directly fetch files on commits via graphql, only provide file
|
|
568
|
+
// information for commits with associated pull requests
|
|
569
|
+
commit.files = files;
|
|
570
|
+
}
|
|
598
571
|
}
|
|
599
572
|
else if (options.backfillFiles) {
|
|
600
573
|
// In this case, there is no squashed merge commit. This could be a simple
|
|
@@ -848,15 +821,7 @@ class GitHub {
|
|
|
848
821
|
*/
|
|
849
822
|
async getFileContentsOnBranch(path, branch) {
|
|
850
823
|
logger_1.logger.debug(`Fetching ${path} from branch ${branch}`);
|
|
851
|
-
|
|
852
|
-
return await this.getFileContentsWithSimpleAPI(path, branch);
|
|
853
|
-
}
|
|
854
|
-
catch (err) {
|
|
855
|
-
if (err.status === 403) {
|
|
856
|
-
return await this.getFileContentsWithDataAPI(path, branch);
|
|
857
|
-
}
|
|
858
|
-
throw err;
|
|
859
|
-
}
|
|
824
|
+
return await this.fileCache.getFileContents(path, branch);
|
|
860
825
|
}
|
|
861
826
|
async getFileJson(path, branch) {
|
|
862
827
|
const content = await this.getFileContentsOnBranch(path, branch);
|
|
@@ -915,15 +880,7 @@ class GitHub {
|
|
|
915
880
|
for (const update of updates) {
|
|
916
881
|
let content;
|
|
917
882
|
try {
|
|
918
|
-
|
|
919
|
-
// we already loaded the file contents earlier, let's not
|
|
920
|
-
// hit GitHub again.
|
|
921
|
-
content = { data: update.cachedFileContents };
|
|
922
|
-
}
|
|
923
|
-
else {
|
|
924
|
-
const fileContent = await this.getFileContentsOnBranch(update.path, defaultBranch);
|
|
925
|
-
content = { data: fileContent };
|
|
926
|
-
}
|
|
883
|
+
content = await this.getFileContentsOnBranch(update.path, defaultBranch);
|
|
927
884
|
}
|
|
928
885
|
catch (err) {
|
|
929
886
|
if (err.status !== 404)
|
|
@@ -936,13 +893,13 @@ class GitHub {
|
|
|
936
893
|
}
|
|
937
894
|
}
|
|
938
895
|
const contentText = content
|
|
939
|
-
? Buffer.from(content.
|
|
896
|
+
? Buffer.from(content.content, 'base64').toString('utf8')
|
|
940
897
|
: undefined;
|
|
941
898
|
const updatedContent = update.updater.updateContent(contentText);
|
|
942
899
|
if (updatedContent) {
|
|
943
900
|
changes.set(update.path, {
|
|
944
901
|
content: updatedContent,
|
|
945
|
-
mode:
|
|
902
|
+
mode: (content === null || content === void 0 ? void 0 : content.mode) || file_cache_1.DEFAULT_FILE_MODE,
|
|
946
903
|
});
|
|
947
904
|
}
|
|
948
905
|
}
|
|
@@ -982,17 +939,6 @@ class GitHub {
|
|
|
982
939
|
}
|
|
983
940
|
}
|
|
984
941
|
exports.GitHub = GitHub;
|
|
985
|
-
// Takes a potentially unqualified branch name, and turns it
|
|
986
|
-
// into a fully qualified ref.
|
|
987
|
-
//
|
|
988
|
-
// e.g. main -> refs/heads/main
|
|
989
|
-
function fullyQualifyBranchRef(refName) {
|
|
990
|
-
let final = refName;
|
|
991
|
-
if (final.indexOf('/') < 0) {
|
|
992
|
-
final = `refs/heads/${final}`;
|
|
993
|
-
}
|
|
994
|
-
return final;
|
|
995
|
-
}
|
|
996
942
|
/**
|
|
997
943
|
* Normalize a provided prefix by removing leading and trailing
|
|
998
944
|
* slashes.
|
package/build/src/manifest.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export interface ReleaserConfig {
|
|
|
22
22
|
packageName?: string;
|
|
23
23
|
includeComponentInTag?: boolean;
|
|
24
24
|
pullRequestTitlePattern?: string;
|
|
25
|
+
tagSeparator?: string;
|
|
25
26
|
changelogSections?: ChangelogSection[];
|
|
26
27
|
changelogPath?: string;
|
|
27
28
|
changelogType?: ChangelogNotesType;
|
|
@@ -54,6 +55,7 @@ interface ReleaserConfigJson {
|
|
|
54
55
|
'include-component-in-tag'?: boolean;
|
|
55
56
|
'changelog-type'?: ChangelogNotesType;
|
|
56
57
|
'pull-request-title-pattern'?: string;
|
|
58
|
+
'tag-separator'?: string;
|
|
57
59
|
'version-file'?: string;
|
|
58
60
|
'extra-files'?: string[];
|
|
59
61
|
}
|
|
@@ -196,6 +198,8 @@ export declare class Manifest {
|
|
|
196
198
|
* @returns {ReleasePullRequest[]} The candidate pull requests to open or update.
|
|
197
199
|
*/
|
|
198
200
|
buildPullRequests(): Promise<ReleasePullRequest[]>;
|
|
201
|
+
private backfillReleasesFromTags;
|
|
202
|
+
private getAllTags;
|
|
199
203
|
/**
|
|
200
204
|
* Opens/updates all candidate release pull requests for this repository.
|
|
201
205
|
*
|
package/build/src/manifest.js
CHANGED
|
@@ -166,7 +166,6 @@ class Manifest {
|
|
|
166
166
|
for await (const release of this.github.releaseIterator({
|
|
167
167
|
maxResults: 400,
|
|
168
168
|
})) {
|
|
169
|
-
// logger.debug(release);
|
|
170
169
|
const tagName = tag_name_1.TagName.parse(release.tagName);
|
|
171
170
|
if (!tagName) {
|
|
172
171
|
logger_1.logger.warn(`Unable to parse release name: ${release.name}`);
|
|
@@ -199,6 +198,18 @@ class Manifest {
|
|
|
199
198
|
}
|
|
200
199
|
}
|
|
201
200
|
const needsBootstrap = releasesFound < expectedReleases;
|
|
201
|
+
if (releasesFound < expectedReleases) {
|
|
202
|
+
logger_1.logger.warn(`Expected ${expectedReleases} releases, only found ${releasesFound}`);
|
|
203
|
+
// Fall back to looking for missing releases using expected tags
|
|
204
|
+
const missingPaths = Object.keys(strategiesByPath).filter(path => !releasesByPath[path]);
|
|
205
|
+
logger_1.logger.warn(`Missing ${missingPaths.length} paths: ${missingPaths}`);
|
|
206
|
+
const missingReleases = await this.backfillReleasesFromTags(missingPaths, strategiesByPath);
|
|
207
|
+
for (const path in missingReleases) {
|
|
208
|
+
releaseShasByPath[path] = missingReleases[path].sha;
|
|
209
|
+
releasesByPath[path] = missingReleases[path];
|
|
210
|
+
releasesFound++;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
202
213
|
if (releasesFound < expectedReleases) {
|
|
203
214
|
logger_1.logger.warn(`Expected ${expectedReleases} releases, only found ${releasesFound}`);
|
|
204
215
|
}
|
|
@@ -246,6 +257,7 @@ class Manifest {
|
|
|
246
257
|
sha: commit.sha,
|
|
247
258
|
message: commit.message,
|
|
248
259
|
files: commit.files,
|
|
260
|
+
pullRequest: commit.pullRequest,
|
|
249
261
|
});
|
|
250
262
|
}
|
|
251
263
|
if (releaseCommitsFound < expectedShas) {
|
|
@@ -322,6 +334,38 @@ class Manifest {
|
|
|
322
334
|
}
|
|
323
335
|
return newReleasePullRequests.map(pullRequestWithConfig => pullRequestWithConfig.pullRequest);
|
|
324
336
|
}
|
|
337
|
+
async backfillReleasesFromTags(missingPaths, strategiesByPath) {
|
|
338
|
+
const releasesByPath = {};
|
|
339
|
+
const allTags = await this.getAllTags();
|
|
340
|
+
for (const path of missingPaths) {
|
|
341
|
+
const expectedVersion = this.releasedVersions[path];
|
|
342
|
+
if (!expectedVersion) {
|
|
343
|
+
logger_1.logger.warn(`No version for path ${path}`);
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
const component = await strategiesByPath[path].getComponent();
|
|
347
|
+
const expectedTag = new tag_name_1.TagName(expectedVersion, component, this.repositoryConfig[path].tagSeparator);
|
|
348
|
+
logger_1.logger.debug(`looking for tagName: ${expectedTag.toString()}`);
|
|
349
|
+
const foundTag = allTags[expectedTag.toString()];
|
|
350
|
+
if (foundTag) {
|
|
351
|
+
logger_1.logger.debug(`found: ${foundTag.name} ${foundTag.sha}`);
|
|
352
|
+
releasesByPath[path] = {
|
|
353
|
+
name: foundTag.name,
|
|
354
|
+
tag: expectedTag,
|
|
355
|
+
sha: foundTag.sha,
|
|
356
|
+
notes: '',
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return releasesByPath;
|
|
361
|
+
}
|
|
362
|
+
async getAllTags() {
|
|
363
|
+
const allTags = {};
|
|
364
|
+
for await (const tag of this.github.tagIterator()) {
|
|
365
|
+
allTags[tag.name] = tag;
|
|
366
|
+
}
|
|
367
|
+
return allTags;
|
|
368
|
+
}
|
|
325
369
|
/**
|
|
326
370
|
* Opens/updates all candidate release pull requests for this repository.
|
|
327
371
|
*
|
|
@@ -567,6 +611,7 @@ function extractReleaserConfig(config) {
|
|
|
567
611
|
includeComponentInTag: config['include-component-in-tag'],
|
|
568
612
|
changelogType: config['changelog-type'],
|
|
569
613
|
pullRequestTitlePattern: config['pull-request-title-pattern'],
|
|
614
|
+
tagSeparator: config['tag-separator'],
|
|
570
615
|
};
|
|
571
616
|
}
|
|
572
617
|
/**
|
|
@@ -628,6 +673,7 @@ async function latestReleaseVersion(github, targetBranch, prefix, pullRequestTit
|
|
|
628
673
|
// collect set of recent commit SHAs seen to verify that the release
|
|
629
674
|
// is in the current branch
|
|
630
675
|
const commitShas = new Set();
|
|
676
|
+
const candidateReleaseVersions = [];
|
|
631
677
|
// only look at the last 250 or so commits to find the latest tag - we
|
|
632
678
|
// don't want to scan the entire repository history if this repo has never
|
|
633
679
|
// been released
|
|
@@ -656,12 +702,15 @@ async function latestReleaseVersion(github, targetBranch, prefix, pullRequestTit
|
|
|
656
702
|
// FIXME, don't hardcode this
|
|
657
703
|
continue;
|
|
658
704
|
}
|
|
659
|
-
|
|
705
|
+
if (version) {
|
|
706
|
+
logger_1.logger.debug(`Found latest release pull request: ${mergedPullRequest.number} version: ${version}`);
|
|
707
|
+
candidateReleaseVersions.push(version);
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
660
710
|
}
|
|
661
711
|
// If not found from recent pull requests, look at releases. Iterate
|
|
662
712
|
// through releases finding valid tags, then cross reference
|
|
663
713
|
const releaseGenerator = github.releaseIterator();
|
|
664
|
-
const candidateReleaseVersions = [];
|
|
665
714
|
for await (const release of releaseGenerator) {
|
|
666
715
|
const tagName = tag_name_1.TagName.parse(release.tagName);
|
|
667
716
|
if (!tagName) {
|
|
@@ -703,7 +752,7 @@ async function latestReleaseVersion(github, targetBranch, prefix, pullRequestTit
|
|
|
703
752
|
return candidateTagVersion.sort((a, b) => b.compare(a))[0];
|
|
704
753
|
}
|
|
705
754
|
function mergeReleaserConfig(defaultConfig, pathConfig) {
|
|
706
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
755
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
707
756
|
return {
|
|
708
757
|
releaseType: (_b = (_a = pathConfig.releaseType) !== null && _a !== void 0 ? _a : defaultConfig.releaseType) !== null && _b !== void 0 ? _b : 'node',
|
|
709
758
|
bumpMinorPreMajor: (_c = pathConfig.bumpMinorPreMajor) !== null && _c !== void 0 ? _c : defaultConfig.bumpMinorPreMajor,
|
|
@@ -718,7 +767,9 @@ function mergeReleaserConfig(defaultConfig, pathConfig) {
|
|
|
718
767
|
packageName: (_m = pathConfig.packageName) !== null && _m !== void 0 ? _m : defaultConfig.packageName,
|
|
719
768
|
versionFile: (_o = pathConfig.versionFile) !== null && _o !== void 0 ? _o : defaultConfig.versionFile,
|
|
720
769
|
extraFiles: (_p = pathConfig.extraFiles) !== null && _p !== void 0 ? _p : defaultConfig.extraFiles,
|
|
721
|
-
|
|
770
|
+
includeComponentInTag: (_q = pathConfig.includeComponentInTag) !== null && _q !== void 0 ? _q : defaultConfig.includeComponentInTag,
|
|
771
|
+
tagSeparator: (_r = pathConfig.tagSeparator) !== null && _r !== void 0 ? _r : defaultConfig.tagSeparator,
|
|
772
|
+
pullRequestTitlePattern: (_s = pathConfig.pullRequestTitlePattern) !== null && _s !== void 0 ? _s : defaultConfig.pullRequestTitlePattern,
|
|
722
773
|
};
|
|
723
774
|
}
|
|
724
775
|
/**
|
|
@@ -239,14 +239,24 @@ class BaseStrategy {
|
|
|
239
239
|
return;
|
|
240
240
|
}
|
|
241
241
|
const component = await this.getComponent();
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
!pullRequestBody.releaseData[0].component
|
|
245
|
-
|
|
246
|
-
|
|
242
|
+
let releaseData;
|
|
243
|
+
if (pullRequestBody.releaseData.length === 1 &&
|
|
244
|
+
!pullRequestBody.releaseData[0].component) {
|
|
245
|
+
// standalone release PR, ensure the components match
|
|
246
|
+
if (this.normalizeComponent(branchName.component) !==
|
|
247
|
+
this.normalizeComponent(component)) {
|
|
248
|
+
logger_1.logger.warn(`PR component: ${branchName.component} does not match configured component: ${component}`);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
releaseData = pullRequestBody.releaseData[0];
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
// manifest release with multiple components
|
|
255
|
+
releaseData = pullRequestBody.releaseData.find(releaseData => {
|
|
247
256
|
return (this.normalizeComponent(releaseData.component) ===
|
|
248
257
|
this.normalizeComponent(component));
|
|
249
258
|
});
|
|
259
|
+
}
|
|
250
260
|
const notes = releaseData === null || releaseData === void 0 ? void 0 : releaseData.notes;
|
|
251
261
|
if (notes === undefined) {
|
|
252
262
|
logger_1.logger.warn('Failed to find release notes');
|
|
@@ -8,6 +8,7 @@ export declare class GoYoshi extends BaseStrategy {
|
|
|
8
8
|
constructor(options: BaseStrategyOptions);
|
|
9
9
|
protected buildUpdates(options: BuildUpdatesOptions): Promise<Update[]>;
|
|
10
10
|
protected postProcessCommits(commits: ConventionalCommit[]): Promise<ConventionalCommit[]>;
|
|
11
|
+
getIgnoredSubModules(): Promise<Set<string>>;
|
|
11
12
|
protected buildReleaseNotes(conventionalCommits: ConventionalCommit[], newVersion: Version, newVersionTag: TagName, latestRelease?: Release): Promise<string>;
|
|
12
13
|
protected initialReleaseVersion(): Version;
|
|
13
14
|
}
|
|
@@ -19,19 +19,7 @@ const changelog_1 = require("../updaters/changelog");
|
|
|
19
19
|
const version_1 = require("../version");
|
|
20
20
|
const version_go_1 = require("../updaters/go/version-go");
|
|
21
21
|
const logger_1 = require("../util/logger");
|
|
22
|
-
|
|
23
|
-
// ignored when generating a release PR for the parent module.
|
|
24
|
-
const IGNORED_SUB_MODULES = new Set([
|
|
25
|
-
'bigtable',
|
|
26
|
-
'bigquery',
|
|
27
|
-
'datastore',
|
|
28
|
-
'firestore',
|
|
29
|
-
'logging',
|
|
30
|
-
'pubsub',
|
|
31
|
-
'pubsublite',
|
|
32
|
-
'spanner',
|
|
33
|
-
'storage',
|
|
34
|
-
]);
|
|
22
|
+
const path_1 = require("path");
|
|
35
23
|
const REGEN_PR_REGEX = /.*auto-regenerate.*/;
|
|
36
24
|
const REGEN_ISSUE_REGEX = /(?<prefix>.*)\(#(?<pr>.*)\)(\n|$)/;
|
|
37
25
|
class GoYoshi extends base_1.BaseStrategy {
|
|
@@ -64,12 +52,9 @@ class GoYoshi extends base_1.BaseStrategy {
|
|
|
64
52
|
let regenCommit;
|
|
65
53
|
const component = await this.getComponent();
|
|
66
54
|
logger_1.logger.debug('Filtering commits');
|
|
55
|
+
const ignoredSubmodules = await this.getIgnoredSubModules();
|
|
67
56
|
return commits.filter(commit => {
|
|
68
57
|
var _a, _b;
|
|
69
|
-
// ignore commits whose scope is in the list of ignored modules
|
|
70
|
-
if (IGNORED_SUB_MODULES.has(commit.scope || '')) {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
58
|
// Only have a single entry of the nightly regen listed in the changelog.
|
|
74
59
|
// If there are more than one of these commits, append associated PR.
|
|
75
60
|
if (this.repository.owner === 'googleapis' &&
|
|
@@ -123,7 +108,7 @@ class GoYoshi extends base_1.BaseStrategy {
|
|
|
123
108
|
else {
|
|
124
109
|
// This is the main module release, so ignore sub modules that
|
|
125
110
|
// are released independently
|
|
126
|
-
for (const submodule of
|
|
111
|
+
for (const submodule of ignoredSubmodules) {
|
|
127
112
|
if (commitMatchesScope(commit.scope, submodule)) {
|
|
128
113
|
logger_1.logger.debug(`Skipping ignored commit scope: ${commit.scope}`);
|
|
129
114
|
return false;
|
|
@@ -134,6 +119,22 @@ class GoYoshi extends base_1.BaseStrategy {
|
|
|
134
119
|
return true;
|
|
135
120
|
});
|
|
136
121
|
}
|
|
122
|
+
async getIgnoredSubModules() {
|
|
123
|
+
// ignored submodules only applies to the root component of
|
|
124
|
+
// googleapis/google-cloud-go
|
|
125
|
+
if (this.repository.owner !== 'googleapis' ||
|
|
126
|
+
this.repository.repo !== 'google-cloud-go' ||
|
|
127
|
+
this.includeComponentInTag) {
|
|
128
|
+
return new Set();
|
|
129
|
+
}
|
|
130
|
+
logger_1.logger.info('Looking for go.mod files');
|
|
131
|
+
const paths = (await this.github.findFilesByFilenameAndRef('go.mod', this.targetBranch))
|
|
132
|
+
.filter(path => !path.includes('internal') && path !== 'go.mod')
|
|
133
|
+
.map(path => path_1.dirname(path));
|
|
134
|
+
logger_1.logger.info(`Found ${paths.length} submodules`);
|
|
135
|
+
logger_1.logger.debug(JSON.stringify(paths));
|
|
136
|
+
return new Set(paths);
|
|
137
|
+
}
|
|
137
138
|
// "closes" is a little presumptuous, let's just indicate that the
|
|
138
139
|
// PR references these other commits:
|
|
139
140
|
async buildReleaseNotes(conventionalCommits, newVersion, newVersionTag, latestRelease) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Update } from '../update';
|
|
2
2
|
import { Version, VersionsMap } from '../version';
|
|
3
3
|
import { BaseStrategy, BuildUpdatesOptions, BaseStrategyOptions } from './base';
|
|
4
|
-
import { GitHubFileContents } from '../
|
|
4
|
+
import { GitHubFileContents } from '../util/file-cache';
|
|
5
5
|
import { Commit, ConventionalCommit } from '../commit';
|
|
6
6
|
import { Release } from '../release';
|
|
7
7
|
import { ReleasePullRequest } from '../release-pull-request';
|
package/build/src/update.d.ts
CHANGED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Octokit } from '@octokit/rest';
|
|
2
|
+
import { Repository } from '../repository';
|
|
3
|
+
export declare const DEFAULT_FILE_MODE = "100644";
|
|
4
|
+
export interface GitHubFileContents {
|
|
5
|
+
sha: string;
|
|
6
|
+
content: string;
|
|
7
|
+
parsedContent: string;
|
|
8
|
+
mode: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* This class is a read-through cache aimed at minimizing the
|
|
12
|
+
* number of API requests needed to fetch file data/contents.
|
|
13
|
+
* It lazy-caches data as it reads and will return cached data
|
|
14
|
+
* for resources already fetched.
|
|
15
|
+
*/
|
|
16
|
+
export declare class RepositoryFileCache {
|
|
17
|
+
private octokit;
|
|
18
|
+
private repository;
|
|
19
|
+
private cache;
|
|
20
|
+
/**
|
|
21
|
+
* Instantiate a new loading cache instance
|
|
22
|
+
*
|
|
23
|
+
* @param {Octokit} octokit An authenticated octokit instance
|
|
24
|
+
* @param {Repository} repository The repository we are fetching data for
|
|
25
|
+
*/
|
|
26
|
+
constructor(octokit: Octokit, repository: Repository);
|
|
27
|
+
/**
|
|
28
|
+
* Fetch file contents for given path on a given branch. If the
|
|
29
|
+
* data has already been fetched, return a cached copy.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} path Path to the file
|
|
32
|
+
* @param {string} branch Branch to fetch the file from
|
|
33
|
+
* @returns {GitHubFileContents} The file contents
|
|
34
|
+
*/
|
|
35
|
+
getFileContents(path: string, branch: string): Promise<GitHubFileContents>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* This class is a read-through cache for a single branch aimed
|
|
39
|
+
* at minimizing the number of API requests needed to fetch file
|
|
40
|
+
* data/contents. It lazy-caches data as it reads and will return
|
|
41
|
+
* cached data for resources already fetched.
|
|
42
|
+
*/
|
|
43
|
+
export declare class BranchFileCache {
|
|
44
|
+
private octokit;
|
|
45
|
+
private repository;
|
|
46
|
+
private branch;
|
|
47
|
+
private cache;
|
|
48
|
+
private treeCache;
|
|
49
|
+
private treeEntries?;
|
|
50
|
+
/**
|
|
51
|
+
* Instantiate a new loading cache instance
|
|
52
|
+
*
|
|
53
|
+
* @param {Octokit} octokit An authenticated octokit instance
|
|
54
|
+
* @param {Repository} repository The repository we are fetching data for
|
|
55
|
+
* @param {string} branch The branch we are fetching data from
|
|
56
|
+
*/
|
|
57
|
+
constructor(octokit: Octokit, repository: Repository, branch: string);
|
|
58
|
+
/**
|
|
59
|
+
* Fetch file contents for given path. If the data has already been
|
|
60
|
+
* fetched, return the cached copy.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} path Path to the file
|
|
63
|
+
* @param {string} branch Branch to fetch the file from
|
|
64
|
+
* @returns {GitHubFileContents} The file contents
|
|
65
|
+
*/
|
|
66
|
+
getFileContents(path: string): Promise<GitHubFileContents>;
|
|
67
|
+
/**
|
|
68
|
+
* Actually fetch the file contents. Uses the tree API to fetch file
|
|
69
|
+
* data.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} path Path to the file
|
|
72
|
+
*/
|
|
73
|
+
private fetchFileContents;
|
|
74
|
+
/**
|
|
75
|
+
* Return the full recursive git tree. If already fetched, return
|
|
76
|
+
* the cached version. If the tree is too big, return null.
|
|
77
|
+
*
|
|
78
|
+
* @returns {TreeEntry[]} The tree entries
|
|
79
|
+
*/
|
|
80
|
+
private getFullTree;
|
|
81
|
+
/**
|
|
82
|
+
* Returns the git tree for a given SHA. If already fetched, return
|
|
83
|
+
* the cached version.
|
|
84
|
+
*
|
|
85
|
+
* @param {string} sha The tree SHA
|
|
86
|
+
* @returns {TreeEntry[]} The tree entries
|
|
87
|
+
*/
|
|
88
|
+
private getTree;
|
|
89
|
+
/**
|
|
90
|
+
* Fetch the git tree via the GitHub API
|
|
91
|
+
*
|
|
92
|
+
* @param {string} sha The tree SHA
|
|
93
|
+
* @returns {TreeEntry[]} The tree entries
|
|
94
|
+
*/
|
|
95
|
+
private fetchTree;
|
|
96
|
+
/**
|
|
97
|
+
* Fetch the git blob from the GitHub API and convert into a
|
|
98
|
+
* GitHubFileContents object.
|
|
99
|
+
*
|
|
100
|
+
* @param {string} blobSha The git blob SHA
|
|
101
|
+
* @param {TreeEntry} treeEntry The associated tree object
|
|
102
|
+
*/
|
|
103
|
+
private fetchContents;
|
|
104
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
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.BranchFileCache = exports.RepositoryFileCache = exports.DEFAULT_FILE_MODE = void 0;
|
|
17
|
+
const logger_1 = require("./logger");
|
|
18
|
+
const errors_1 = require("../errors");
|
|
19
|
+
exports.DEFAULT_FILE_MODE = '100644';
|
|
20
|
+
/**
|
|
21
|
+
* This class is a read-through cache aimed at minimizing the
|
|
22
|
+
* number of API requests needed to fetch file data/contents.
|
|
23
|
+
* It lazy-caches data as it reads and will return cached data
|
|
24
|
+
* for resources already fetched.
|
|
25
|
+
*/
|
|
26
|
+
class RepositoryFileCache {
|
|
27
|
+
/**
|
|
28
|
+
* Instantiate a new loading cache instance
|
|
29
|
+
*
|
|
30
|
+
* @param {Octokit} octokit An authenticated octokit instance
|
|
31
|
+
* @param {Repository} repository The repository we are fetching data for
|
|
32
|
+
*/
|
|
33
|
+
constructor(octokit, repository) {
|
|
34
|
+
this.octokit = octokit;
|
|
35
|
+
this.repository = repository;
|
|
36
|
+
this.cache = new Map();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Fetch file contents for given path on a given branch. If the
|
|
40
|
+
* data has already been fetched, return a cached copy.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} path Path to the file
|
|
43
|
+
* @param {string} branch Branch to fetch the file from
|
|
44
|
+
* @returns {GitHubFileContents} The file contents
|
|
45
|
+
*/
|
|
46
|
+
async getFileContents(path, branch) {
|
|
47
|
+
let fileCache = this.cache.get(branch);
|
|
48
|
+
if (!fileCache) {
|
|
49
|
+
fileCache = new BranchFileCache(this.octokit, this.repository, branch);
|
|
50
|
+
this.cache.set(branch, fileCache);
|
|
51
|
+
}
|
|
52
|
+
return await fileCache.getFileContents(path);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.RepositoryFileCache = RepositoryFileCache;
|
|
56
|
+
/**
|
|
57
|
+
* This class is a read-through cache for a single branch aimed
|
|
58
|
+
* at minimizing the number of API requests needed to fetch file
|
|
59
|
+
* data/contents. It lazy-caches data as it reads and will return
|
|
60
|
+
* cached data for resources already fetched.
|
|
61
|
+
*/
|
|
62
|
+
class BranchFileCache {
|
|
63
|
+
/**
|
|
64
|
+
* Instantiate a new loading cache instance
|
|
65
|
+
*
|
|
66
|
+
* @param {Octokit} octokit An authenticated octokit instance
|
|
67
|
+
* @param {Repository} repository The repository we are fetching data for
|
|
68
|
+
* @param {string} branch The branch we are fetching data from
|
|
69
|
+
*/
|
|
70
|
+
constructor(octokit, repository, branch) {
|
|
71
|
+
this.octokit = octokit;
|
|
72
|
+
this.repository = repository;
|
|
73
|
+
this.branch = branch;
|
|
74
|
+
this.cache = new Map();
|
|
75
|
+
this.treeCache = new Map();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Fetch file contents for given path. If the data has already been
|
|
79
|
+
* fetched, return the cached copy.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} path Path to the file
|
|
82
|
+
* @param {string} branch Branch to fetch the file from
|
|
83
|
+
* @returns {GitHubFileContents} The file contents
|
|
84
|
+
*/
|
|
85
|
+
async getFileContents(path) {
|
|
86
|
+
const cached = this.cache.get(path);
|
|
87
|
+
if (cached) {
|
|
88
|
+
return cached;
|
|
89
|
+
}
|
|
90
|
+
const fetched = await this.fetchFileContents(path);
|
|
91
|
+
this.cache.set(path, fetched);
|
|
92
|
+
return fetched;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Actually fetch the file contents. Uses the tree API to fetch file
|
|
96
|
+
* data.
|
|
97
|
+
*
|
|
98
|
+
* @param {string} path Path to the file
|
|
99
|
+
*/
|
|
100
|
+
async fetchFileContents(path) {
|
|
101
|
+
// try to use the entire git tree if it's not too big
|
|
102
|
+
const treeEntries = await this.getFullTree();
|
|
103
|
+
if (treeEntries) {
|
|
104
|
+
logger_1.logger.debug(`Using full tree to find ${path}`);
|
|
105
|
+
const found = treeEntries.find(entry => entry.path === path);
|
|
106
|
+
if (found === null || found === void 0 ? void 0 : found.sha) {
|
|
107
|
+
return await this.fetchContents(found.sha, found);
|
|
108
|
+
}
|
|
109
|
+
throw new errors_1.FileNotFoundError(path);
|
|
110
|
+
}
|
|
111
|
+
// full tree is too big, use data API to fetch
|
|
112
|
+
const parts = path.split('/');
|
|
113
|
+
let treeSha = this.branch;
|
|
114
|
+
let found;
|
|
115
|
+
for (const part of parts) {
|
|
116
|
+
const tree = await this.getTree(treeSha);
|
|
117
|
+
found = tree.find(item => item.path === part);
|
|
118
|
+
if (!(found === null || found === void 0 ? void 0 : found.sha)) {
|
|
119
|
+
throw new errors_1.FileNotFoundError(path);
|
|
120
|
+
}
|
|
121
|
+
treeSha = found.sha;
|
|
122
|
+
}
|
|
123
|
+
if (found === null || found === void 0 ? void 0 : found.sha) {
|
|
124
|
+
return await this.fetchContents(found.sha, found);
|
|
125
|
+
}
|
|
126
|
+
throw new errors_1.FileNotFoundError(path);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Return the full recursive git tree. If already fetched, return
|
|
130
|
+
* the cached version. If the tree is too big, return null.
|
|
131
|
+
*
|
|
132
|
+
* @returns {TreeEntry[]} The tree entries
|
|
133
|
+
*/
|
|
134
|
+
async getFullTree() {
|
|
135
|
+
if (this.treeEntries === undefined) {
|
|
136
|
+
// fetch all tree entries recursively
|
|
137
|
+
const { data: { tree, truncated }, } = await this.octokit.git.getTree({
|
|
138
|
+
owner: this.repository.owner,
|
|
139
|
+
repo: this.repository.repo,
|
|
140
|
+
tree_sha: this.branch,
|
|
141
|
+
recursive: 'true',
|
|
142
|
+
});
|
|
143
|
+
if (truncated) {
|
|
144
|
+
// the full tree is too big to use, mark it as unusable
|
|
145
|
+
this.treeEntries = null;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.treeEntries = tree;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return this.treeEntries;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Returns the git tree for a given SHA. If already fetched, return
|
|
155
|
+
* the cached version.
|
|
156
|
+
*
|
|
157
|
+
* @param {string} sha The tree SHA
|
|
158
|
+
* @returns {TreeEntry[]} The tree entries
|
|
159
|
+
*/
|
|
160
|
+
async getTree(sha) {
|
|
161
|
+
const cached = this.treeCache.get(sha);
|
|
162
|
+
if (cached) {
|
|
163
|
+
return cached;
|
|
164
|
+
}
|
|
165
|
+
const fetched = await this.fetchTree(sha);
|
|
166
|
+
this.treeCache.set(sha, fetched);
|
|
167
|
+
return fetched;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Fetch the git tree via the GitHub API
|
|
171
|
+
*
|
|
172
|
+
* @param {string} sha The tree SHA
|
|
173
|
+
* @returns {TreeEntry[]} The tree entries
|
|
174
|
+
*/
|
|
175
|
+
async fetchTree(sha) {
|
|
176
|
+
const { data: { tree }, } = await this.octokit.git.getTree({
|
|
177
|
+
owner: this.repository.owner,
|
|
178
|
+
repo: this.repository.repo,
|
|
179
|
+
tree_sha: sha,
|
|
180
|
+
recursive: 'false',
|
|
181
|
+
});
|
|
182
|
+
return tree;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Fetch the git blob from the GitHub API and convert into a
|
|
186
|
+
* GitHubFileContents object.
|
|
187
|
+
*
|
|
188
|
+
* @param {string} blobSha The git blob SHA
|
|
189
|
+
* @param {TreeEntry} treeEntry The associated tree object
|
|
190
|
+
*/
|
|
191
|
+
async fetchContents(blobSha, treeEntry) {
|
|
192
|
+
const { data: { content }, } = await this.octokit.git.getBlob({
|
|
193
|
+
owner: this.repository.owner,
|
|
194
|
+
repo: this.repository.repo,
|
|
195
|
+
file_sha: blobSha,
|
|
196
|
+
});
|
|
197
|
+
return {
|
|
198
|
+
sha: blobSha,
|
|
199
|
+
mode: treeEntry.mode || exports.DEFAULT_FILE_MODE,
|
|
200
|
+
content,
|
|
201
|
+
parsedContent: Buffer.from(content, 'base64').toString('utf8'),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
exports.BranchFileCache = BranchFileCache;
|
|
206
|
+
//# sourceMappingURL=file-cache.js.map
|
|
@@ -52,7 +52,7 @@ class PullRequestBody {
|
|
|
52
52
|
return this.releaseData
|
|
53
53
|
.map(release => {
|
|
54
54
|
var _a;
|
|
55
|
-
return `<details><summary>${release.component}: ${(_a = release.version) === null || _a === void 0 ? void 0 : _a.toString()}</summary>\n\n${release.notes}\n</details>`;
|
|
55
|
+
return `<details><summary>${release.component ? `${release.component}: ` : ''}${(_a = release.version) === null || _a === void 0 ? void 0 : _a.toString()}</summary>\n\n${release.notes}\n</details>`;
|
|
56
56
|
})
|
|
57
57
|
.join('\n\n');
|
|
58
58
|
}
|
|
@@ -91,6 +91,7 @@ function splitBody(body) {
|
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
93
|
const SUMMARY_PATTERN = /^(?<component>.*[^:]):? (?<version>\d+\.\d+\.\d+.*)$/;
|
|
94
|
+
const COMPONENTLESS_SUMMARY_PATTERN = /^(?<version>\d+\.\d+\.\d+.*)$/;
|
|
94
95
|
function extractMultipleReleases(notes) {
|
|
95
96
|
const data = [];
|
|
96
97
|
const root = node_html_parser_1.parse(notes);
|
|
@@ -98,17 +99,28 @@ function extractMultipleReleases(notes) {
|
|
|
98
99
|
const summaryNode = detail.getElementsByTagName('summary')[0];
|
|
99
100
|
const summary = summaryNode === null || summaryNode === void 0 ? void 0 : summaryNode.textContent;
|
|
100
101
|
const match = summary.match(SUMMARY_PATTERN);
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
if (match === null || match === void 0 ? void 0 : match.groups) {
|
|
103
|
+
detail.removeChild(summaryNode);
|
|
104
|
+
const notes = detail.textContent.trim();
|
|
105
|
+
data.push({
|
|
106
|
+
component: match.groups.component,
|
|
107
|
+
version: version_1.Version.parse(match.groups.version),
|
|
108
|
+
notes,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
const componentlessMatch = summary.match(COMPONENTLESS_SUMMARY_PATTERN);
|
|
113
|
+
if (!(componentlessMatch === null || componentlessMatch === void 0 ? void 0 : componentlessMatch.groups)) {
|
|
114
|
+
logger_1.logger.warn(`Summary: ${summary} did not match the expected pattern`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
detail.removeChild(summaryNode);
|
|
118
|
+
const notes = detail.textContent.trim();
|
|
119
|
+
data.push({
|
|
120
|
+
version: version_1.Version.parse(componentlessMatch.groups.version),
|
|
121
|
+
notes,
|
|
122
|
+
});
|
|
104
123
|
}
|
|
105
|
-
detail.removeChild(summaryNode);
|
|
106
|
-
const notes = detail.textContent.trim();
|
|
107
|
-
data.push({
|
|
108
|
-
component: match.groups.component,
|
|
109
|
-
version: version_1.Version.parse(match.groups.version),
|
|
110
|
-
notes,
|
|
111
|
-
});
|
|
112
124
|
}
|
|
113
125
|
return data;
|
|
114
126
|
}
|
package/package.json
CHANGED