release-please 13.4.9 → 13.4.12

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 CHANGED
@@ -4,6 +4,31 @@
4
4
 
5
5
  [1]: https://www.npmjs.com/package/release-please?activeTab=versions
6
6
 
7
+ ### [13.4.12](https://github.com/googleapis/release-please/compare/v13.4.11...v13.4.12) (2022-02-22)
8
+
9
+
10
+ ### Bug Fixes
11
+
12
+ * address false-positive matches for autorelease branch naming ([#1311](https://github.com/googleapis/release-please/issues/1311)) ([c5e76dc](https://github.com/googleapis/release-please/commit/c5e76dc8202958ed5af0f3635188261b8845f561)), closes [#1310](https://github.com/googleapis/release-please/issues/1310)
13
+ * catch FileNotFound error when building changeset ([#1306](https://github.com/googleapis/release-please/issues/1306)) ([3944b17](https://github.com/googleapis/release-please/commit/3944b17f33500cecc63a1ff63db81cdbd50ce1a1))
14
+ * manifest config should allow overriding labels ([#1303](https://github.com/googleapis/release-please/issues/1303)) ([f4d0314](https://github.com/googleapis/release-please/commit/f4d0314d1a394389a233ba9e1383852f0875dcd1)), closes [#1302](https://github.com/googleapis/release-please/issues/1302)
15
+
16
+ ### [13.4.11](https://github.com/googleapis/release-please/compare/v13.4.10...v13.4.11) (2022-02-18)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * 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))
22
+
23
+ ### [13.4.10](https://github.com/googleapis/release-please/compare/v13.4.9...v13.4.10) (2022-02-16)
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * **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))
29
+ * manifest release can handle componentless entry ([#1300](https://github.com/googleapis/release-please/issues/1300)) ([6b58573](https://github.com/googleapis/release-please/commit/6b585734fe7b49f0e351b73b27260a304d6c80dd))
30
+ * 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))
31
+
7
32
  ### [13.4.9](https://github.com/googleapis/release-please/compare/v13.4.8...v13.4.9) (2022-02-14)
8
33
 
9
34
 
@@ -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
@@ -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
  *
@@ -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
  /**
@@ -70,58 +71,6 @@ class GitHub {
70
71
  maxRetries -= 1;
71
72
  }
72
73
  });
73
- /**
74
- * Fetch the contents of a file with the Contents API
75
- *
76
- * @param {string} path The path to the file in the repository
77
- * @param {string} branch The branch to fetch from
78
- * @returns {GitHubFileContents}
79
- * @throws {GitHubAPIError} on other API errors
80
- */
81
- this.getFileContentsWithSimpleAPI = wrapAsync(async (path, ref, isBranch = true) => {
82
- ref = isBranch ? fullyQualifyBranchRef(ref) : ref;
83
- const options = {
84
- owner: this.repository.owner,
85
- repo: this.repository.repo,
86
- path,
87
- ref,
88
- };
89
- const resp = await this.request('GET /repos/:owner/:repo/contents/:path', options);
90
- return {
91
- parsedContent: Buffer.from(resp.data.content, 'base64').toString('utf8'),
92
- content: resp.data.content,
93
- sha: resp.data.sha,
94
- };
95
- });
96
- /**
97
- * Fetch the contents of a file using the Git data API
98
- *
99
- * @param {string} path The path to the file in the repository
100
- * @param {string} branch The branch to fetch from
101
- * @returns {GitHubFileContents}
102
- * @throws {GitHubAPIError} on other API errors
103
- */
104
- this.getFileContentsWithDataAPI = wrapAsync(async (path, branch) => {
105
- const repoTree = await this.octokit.git.getTree({
106
- owner: this.repository.owner,
107
- repo: this.repository.repo,
108
- tree_sha: branch,
109
- });
110
- const blobDescriptor = repoTree.data.tree.find(tree => tree.path === path);
111
- if (!blobDescriptor) {
112
- throw new Error(`Could not find requested path: ${path}`);
113
- }
114
- const resp = await this.octokit.git.getBlob({
115
- owner: this.repository.owner,
116
- repo: this.repository.repo,
117
- file_sha: blobDescriptor.sha,
118
- });
119
- return {
120
- parsedContent: Buffer.from(resp.data.content, 'base64').toString('utf8'),
121
- content: resp.data.content,
122
- sha: resp.data.sha,
123
- };
124
- });
125
74
  /**
126
75
  * Returns a list of paths to all files with a given name.
127
76
  *
@@ -324,9 +273,13 @@ class GitHub {
324
273
  name: resp.data.name || undefined,
325
274
  tagName: resp.data.tag_name,
326
275
  sha: resp.data.target_commitish,
327
- notes: resp.data.body_text,
276
+ notes: resp.data.body_text ||
277
+ resp.data.body ||
278
+ resp.data.body_html ||
279
+ undefined,
328
280
  url: resp.data.html_url,
329
281
  draft: resp.data.draft,
282
+ uploadUrl: resp.data.upload_url,
330
283
  };
331
284
  }, e => {
332
285
  if (e instanceof request_error_1.RequestError) {
@@ -395,6 +348,7 @@ class GitHub {
395
348
  this.octokit = options.octokitAPIs.octokit;
396
349
  this.request = options.octokitAPIs.request;
397
350
  this.graphql = options.octokitAPIs.graphql;
351
+ this.fileCache = new file_cache_1.RepositoryFileCache(this.octokit, this.repository);
398
352
  }
399
353
  /**
400
354
  * Build a new GitHub client with auto-detected default branch.
@@ -867,15 +821,7 @@ class GitHub {
867
821
  */
868
822
  async getFileContentsOnBranch(path, branch) {
869
823
  logger_1.logger.debug(`Fetching ${path} from branch ${branch}`);
870
- try {
871
- return await this.getFileContentsWithSimpleAPI(path, branch);
872
- }
873
- catch (err) {
874
- if (err.status === 403) {
875
- return await this.getFileContentsWithDataAPI(path, branch);
876
- }
877
- throw err;
878
- }
824
+ return await this.fileCache.getFileContents(path, branch);
879
825
  }
880
826
  async getFileJson(path, branch) {
881
827
  const content = await this.getFileContentsOnBranch(path, branch);
@@ -934,18 +880,10 @@ class GitHub {
934
880
  for (const update of updates) {
935
881
  let content;
936
882
  try {
937
- if (update.cachedFileContents) {
938
- // we already loaded the file contents earlier, let's not
939
- // hit GitHub again.
940
- content = { data: update.cachedFileContents };
941
- }
942
- else {
943
- const fileContent = await this.getFileContentsOnBranch(update.path, defaultBranch);
944
- content = { data: fileContent };
945
- }
883
+ content = await this.getFileContentsOnBranch(update.path, defaultBranch);
946
884
  }
947
885
  catch (err) {
948
- if (err.status !== 404)
886
+ if (!(err instanceof errors_1.FileNotFoundError))
949
887
  throw err;
950
888
  // if the file is missing and create = false, just continue
951
889
  // to the next update, otherwise create the file.
@@ -955,13 +893,13 @@ class GitHub {
955
893
  }
956
894
  }
957
895
  const contentText = content
958
- ? Buffer.from(content.data.content, 'base64').toString('utf8')
896
+ ? Buffer.from(content.content, 'base64').toString('utf8')
959
897
  : undefined;
960
898
  const updatedContent = update.updater.updateContent(contentText);
961
899
  if (updatedContent) {
962
900
  changes.set(update.path, {
963
901
  content: updatedContent,
964
- mode: '100644',
902
+ mode: (content === null || content === void 0 ? void 0 : content.mode) || file_cache_1.DEFAULT_FILE_MODE,
965
903
  });
966
904
  }
967
905
  }
@@ -1001,17 +939,6 @@ class GitHub {
1001
939
  }
1002
940
  }
1003
941
  exports.GitHub = GitHub;
1004
- // Takes a potentially unqualified branch name, and turns it
1005
- // into a fully qualified ref.
1006
- //
1007
- // e.g. main -> refs/heads/main
1008
- function fullyQualifyBranchRef(refName) {
1009
- let final = refName;
1010
- if (final.indexOf('/') < 0) {
1011
- final = `refs/heads/${final}`;
1012
- }
1013
- return final;
1014
- }
1015
942
  /**
1016
943
  * Normalize a provided prefix by removing leading and trailing
1017
944
  * slashes.
@@ -629,6 +629,8 @@ async function parseConfig(github, configFile, branch) {
629
629
  for (const path in config.packages) {
630
630
  repositoryConfig[path] = mergeReleaserConfig(defaultConfig, extractReleaserConfig(config.packages[path]));
631
631
  }
632
+ const configLabel = config['label'];
633
+ const configReleaseLabel = config['release-label'];
632
634
  const manifestOptions = {
633
635
  bootstrapSha: config['bootstrap-sha'],
634
636
  lastReleaseSha: config['last-release-sha'],
@@ -636,6 +638,8 @@ async function parseConfig(github, configFile, branch) {
636
638
  separatePullRequests: config['separate-pull-requests'],
637
639
  groupPullRequestTitlePattern: config['group-pull-request-title-pattern'],
638
640
  plugins: config['plugins'],
641
+ labels: configLabel === undefined ? undefined : [configLabel],
642
+ releaseLabels: configReleaseLabel === undefined ? undefined : [configReleaseLabel],
639
643
  };
640
644
  return { config: repositoryConfig, options: manifestOptions };
641
645
  }
@@ -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
- // Commits containing a scope prefixed with an item in this array will be
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,6 +52,7 @@ 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
58
  // Only have a single entry of the nightly regen listed in the changelog.
@@ -119,7 +108,7 @@ class GoYoshi extends base_1.BaseStrategy {
119
108
  else {
120
109
  // This is the main module release, so ignore sub modules that
121
110
  // are released independently
122
- for (const submodule of IGNORED_SUB_MODULES) {
111
+ for (const submodule of ignoredSubmodules) {
123
112
  if (commitMatchesScope(commit.scope, submodule)) {
124
113
  logger_1.logger.debug(`Skipping ignored commit scope: ${commit.scope}`);
125
114
  return false;
@@ -130,6 +119,22 @@ class GoYoshi extends base_1.BaseStrategy {
130
119
  return true;
131
120
  });
132
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
+ }
133
138
  // "closes" is a little presumptuous, let's just indicate that the
134
139
  // PR references these other commits:
135
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 '../github';
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';
@@ -1,4 +1,4 @@
1
- import { GitHubFileContents } from './github';
1
+ import { GitHubFileContents } from './util/file-cache';
2
2
  /**
3
3
  * An update is a collection of data that represents changes to
4
4
  * a file in a repository.
@@ -82,8 +82,12 @@ exports.BranchName = BranchName;
82
82
  * @see https://github.com/googleapis/releasetool
83
83
  */
84
84
  const AUTORELEASE_PATTERN = /^release-?(?<component>[\w-.]*)?-v(?<version>[0-9].*)$/;
85
+ const RELEASE_PLEASE_BRANCH_PREFIX = 'release-please--branches';
85
86
  class AutoreleaseBranchName extends BranchName {
86
87
  static matches(branchName) {
88
+ if (branchName.startsWith(RELEASE_PLEASE_BRANCH_PREFIX)) {
89
+ return false;
90
+ }
87
91
  return !!branchName.match(AUTORELEASE_PATTERN);
88
92
  }
89
93
  constructor(branchName) {
@@ -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 (!(match === null || match === void 0 ? void 0 : match.groups)) {
102
- logger_1.logger.warn(`Summary: ${summary} did not match the expected pattern`);
103
- continue;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-please",
3
- "version": "13.4.9",
3
+ "version": "13.4.12",
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",