release-please 13.4.10 → 13.4.13
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 +23 -0
- package/README.md +10 -0
- package/build/src/errors/index.d.ts +4 -0
- package/build/src/errors/index.js +10 -1
- package/build/src/github.d.ts +2 -23
- package/build/src/github.js +7 -84
- package/build/src/manifest.js +24 -1
- package/build/src/strategies/java-yoshi.d.ts +1 -1
- package/build/src/update.d.ts +1 -1
- package/build/src/util/branch-name.js +4 -0
- package/build/src/util/file-cache.d.ts +104 -0
- package/build/src/util/file-cache.js +206 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@
|
|
|
4
4
|
|
|
5
5
|
[1]: https://www.npmjs.com/package/release-please?activeTab=versions
|
|
6
6
|
|
|
7
|
+
### [13.4.13](https://github.com/googleapis/release-please/compare/v13.4.12...v13.4.13) (2022-02-28)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
* handle failures during multiple release creation ([#1315](https://github.com/googleapis/release-please/issues/1315)) ([fc856ae](https://github.com/googleapis/release-please/commit/fc856aed1d95def38170eff6381829cd6d7d1e0b))
|
|
13
|
+
|
|
14
|
+
### [13.4.12](https://github.com/googleapis/release-please/compare/v13.4.11...v13.4.12) (2022-02-22)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* 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)
|
|
20
|
+
* catch FileNotFound error when building changeset ([#1306](https://github.com/googleapis/release-please/issues/1306)) ([3944b17](https://github.com/googleapis/release-please/commit/3944b17f33500cecc63a1ff63db81cdbd50ce1a1))
|
|
21
|
+
* 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)
|
|
22
|
+
|
|
23
|
+
### [13.4.11](https://github.com/googleapis/release-please/compare/v13.4.10...v13.4.11) (2022-02-18)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Bug Fixes
|
|
27
|
+
|
|
28
|
+
* 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))
|
|
29
|
+
|
|
7
30
|
### [13.4.10](https://github.com/googleapis/release-please/compare/v13.4.9...v13.4.10) (2022-02-16)
|
|
8
31
|
|
|
9
32
|
|
package/README.md
CHANGED
|
@@ -109,6 +109,16 @@ END_COMMIT_OVERRIDE
|
|
|
109
109
|
The next time release please runs, it will use that override section as the
|
|
110
110
|
commit message instead of the merged commit message.
|
|
111
111
|
|
|
112
|
+
## Release Please bot does not create a release PR. Why?
|
|
113
|
+
|
|
114
|
+
Release Please creates a release pull request after it sees the default branch
|
|
115
|
+
contains "releaseable units" since the last release.
|
|
116
|
+
A releasable unit is a commit to the branch with one of the following
|
|
117
|
+
prefixes: "feat" and "fix". (A "chore" commit is not a releasable unit.)
|
|
118
|
+
|
|
119
|
+
Some languages have their specific releasable unit configuration. For example,
|
|
120
|
+
"docs" is a prefix for releasable units in Java and Python.
|
|
121
|
+
|
|
112
122
|
## Strategy (Language) types supported
|
|
113
123
|
|
|
114
124
|
Release Please automates releases for the following flavors of repositories:
|
|
@@ -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/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;
|
|
@@ -68,6 +64,7 @@ export declare class GitHub {
|
|
|
68
64
|
private octokit;
|
|
69
65
|
private request;
|
|
70
66
|
private graphql;
|
|
67
|
+
private fileCache;
|
|
71
68
|
private constructor();
|
|
72
69
|
/**
|
|
73
70
|
* Build a new GitHub client with auto-detected default branch.
|
|
@@ -185,24 +182,6 @@ export declare class GitHub {
|
|
|
185
182
|
* @throws {GitHubAPIError} on other API errors
|
|
186
183
|
*/
|
|
187
184
|
getFileContents(path: string): Promise<GitHubFileContents>;
|
|
188
|
-
/**
|
|
189
|
-
* Fetch the contents of a file with the Contents API
|
|
190
|
-
*
|
|
191
|
-
* @param {string} path The path to the file in the repository
|
|
192
|
-
* @param {string} branch The branch to fetch from
|
|
193
|
-
* @returns {GitHubFileContents}
|
|
194
|
-
* @throws {GitHubAPIError} on other API errors
|
|
195
|
-
*/
|
|
196
|
-
getFileContentsWithSimpleAPI: (path: string, ref: string, isBranch?: any) => Promise<GitHubFileContents>;
|
|
197
|
-
/**
|
|
198
|
-
* Fetch the contents of a file using the Git data API
|
|
199
|
-
*
|
|
200
|
-
* @param {string} path The path to the file in the repository
|
|
201
|
-
* @param {string} branch The branch to fetch from
|
|
202
|
-
* @returns {GitHubFileContents}
|
|
203
|
-
* @throws {GitHubAPIError} on other API errors
|
|
204
|
-
*/
|
|
205
|
-
getFileContentsWithDataAPI: (path: string, branch: string) => Promise<GitHubFileContents>;
|
|
206
185
|
/**
|
|
207
186
|
* Fetch the contents of a file
|
|
208
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
|
/**
|
|
@@ -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
|
*
|
|
@@ -399,6 +348,7 @@ class GitHub {
|
|
|
399
348
|
this.octokit = options.octokitAPIs.octokit;
|
|
400
349
|
this.request = options.octokitAPIs.request;
|
|
401
350
|
this.graphql = options.octokitAPIs.graphql;
|
|
351
|
+
this.fileCache = new file_cache_1.RepositoryFileCache(this.octokit, this.repository);
|
|
402
352
|
}
|
|
403
353
|
/**
|
|
404
354
|
* Build a new GitHub client with auto-detected default branch.
|
|
@@ -871,15 +821,7 @@ class GitHub {
|
|
|
871
821
|
*/
|
|
872
822
|
async getFileContentsOnBranch(path, branch) {
|
|
873
823
|
logger_1.logger.debug(`Fetching ${path} from branch ${branch}`);
|
|
874
|
-
|
|
875
|
-
return await this.getFileContentsWithSimpleAPI(path, branch);
|
|
876
|
-
}
|
|
877
|
-
catch (err) {
|
|
878
|
-
if (err.status === 403) {
|
|
879
|
-
return await this.getFileContentsWithDataAPI(path, branch);
|
|
880
|
-
}
|
|
881
|
-
throw err;
|
|
882
|
-
}
|
|
824
|
+
return await this.fileCache.getFileContents(path, branch);
|
|
883
825
|
}
|
|
884
826
|
async getFileJson(path, branch) {
|
|
885
827
|
const content = await this.getFileContentsOnBranch(path, branch);
|
|
@@ -938,18 +880,10 @@ class GitHub {
|
|
|
938
880
|
for (const update of updates) {
|
|
939
881
|
let content;
|
|
940
882
|
try {
|
|
941
|
-
|
|
942
|
-
// we already loaded the file contents earlier, let's not
|
|
943
|
-
// hit GitHub again.
|
|
944
|
-
content = { data: update.cachedFileContents };
|
|
945
|
-
}
|
|
946
|
-
else {
|
|
947
|
-
const fileContent = await this.getFileContentsOnBranch(update.path, defaultBranch);
|
|
948
|
-
content = { data: fileContent };
|
|
949
|
-
}
|
|
883
|
+
content = await this.getFileContentsOnBranch(update.path, defaultBranch);
|
|
950
884
|
}
|
|
951
885
|
catch (err) {
|
|
952
|
-
if (err
|
|
886
|
+
if (!(err instanceof errors_1.FileNotFoundError))
|
|
953
887
|
throw err;
|
|
954
888
|
// if the file is missing and create = false, just continue
|
|
955
889
|
// to the next update, otherwise create the file.
|
|
@@ -959,13 +893,13 @@ class GitHub {
|
|
|
959
893
|
}
|
|
960
894
|
}
|
|
961
895
|
const contentText = content
|
|
962
|
-
? Buffer.from(content.
|
|
896
|
+
? Buffer.from(content.content, 'base64').toString('utf8')
|
|
963
897
|
: undefined;
|
|
964
898
|
const updatedContent = update.updater.updateContent(contentText);
|
|
965
899
|
if (updatedContent) {
|
|
966
900
|
changes.set(update.path, {
|
|
967
901
|
content: updatedContent,
|
|
968
|
-
mode:
|
|
902
|
+
mode: (content === null || content === void 0 ? void 0 : content.mode) || file_cache_1.DEFAULT_FILE_MODE,
|
|
969
903
|
});
|
|
970
904
|
}
|
|
971
905
|
}
|
|
@@ -1005,17 +939,6 @@ class GitHub {
|
|
|
1005
939
|
}
|
|
1006
940
|
}
|
|
1007
941
|
exports.GitHub = GitHub;
|
|
1008
|
-
// Takes a potentially unqualified branch name, and turns it
|
|
1009
|
-
// into a fully qualified ref.
|
|
1010
|
-
//
|
|
1011
|
-
// e.g. main -> refs/heads/main
|
|
1012
|
-
function fullyQualifyBranchRef(refName) {
|
|
1013
|
-
let final = refName;
|
|
1014
|
-
if (final.indexOf('/') < 0) {
|
|
1015
|
-
final = `refs/heads/${final}`;
|
|
1016
|
-
}
|
|
1017
|
-
return final;
|
|
1018
|
-
}
|
|
1019
942
|
/**
|
|
1020
943
|
* Normalize a provided prefix by removing leading and trailing
|
|
1021
944
|
* slashes.
|
package/build/src/manifest.js
CHANGED
|
@@ -24,6 +24,7 @@ const factory_1 = require("./factory");
|
|
|
24
24
|
const pull_request_body_1 = require("./util/pull-request-body");
|
|
25
25
|
const merge_1 = require("./plugins/merge");
|
|
26
26
|
const release_please_manifest_1 = require("./updaters/release-please-manifest");
|
|
27
|
+
const errors_1 = require("./errors");
|
|
27
28
|
exports.DEFAULT_RELEASE_PLEASE_CONFIG = 'release-please-config.json';
|
|
28
29
|
exports.DEFAULT_RELEASE_PLEASE_MANIFEST = '.release-please-manifest.json';
|
|
29
30
|
exports.ROOT_PROJECT_PATH = '.';
|
|
@@ -525,7 +526,25 @@ class Manifest {
|
|
|
525
526
|
for (const release of releases) {
|
|
526
527
|
promises.push(this.createRelease(release));
|
|
527
528
|
}
|
|
528
|
-
const
|
|
529
|
+
const duplicateReleases = [];
|
|
530
|
+
const githubReleases = [];
|
|
531
|
+
for (const promise of promises) {
|
|
532
|
+
try {
|
|
533
|
+
githubReleases.push(await promise);
|
|
534
|
+
}
|
|
535
|
+
catch (err) {
|
|
536
|
+
if (err instanceof errors_1.DuplicateReleaseError) {
|
|
537
|
+
logger_1.logger.warn(`Duplicate release tag: ${err.tag}`);
|
|
538
|
+
duplicateReleases.push(err);
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
throw err;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if (duplicateReleases.length > 0 && githubReleases.length === 0) {
|
|
546
|
+
throw duplicateReleases[0];
|
|
547
|
+
}
|
|
529
548
|
// adjust tags on pullRequest
|
|
530
549
|
await Promise.all([
|
|
531
550
|
this.github.removeIssueLabels(this.labels, pullRequest.number),
|
|
@@ -629,6 +648,8 @@ async function parseConfig(github, configFile, branch) {
|
|
|
629
648
|
for (const path in config.packages) {
|
|
630
649
|
repositoryConfig[path] = mergeReleaserConfig(defaultConfig, extractReleaserConfig(config.packages[path]));
|
|
631
650
|
}
|
|
651
|
+
const configLabel = config['label'];
|
|
652
|
+
const configReleaseLabel = config['release-label'];
|
|
632
653
|
const manifestOptions = {
|
|
633
654
|
bootstrapSha: config['bootstrap-sha'],
|
|
634
655
|
lastReleaseSha: config['last-release-sha'],
|
|
@@ -636,6 +657,8 @@ async function parseConfig(github, configFile, branch) {
|
|
|
636
657
|
separatePullRequests: config['separate-pull-requests'],
|
|
637
658
|
groupPullRequestTitlePattern: config['group-pull-request-title-pattern'],
|
|
638
659
|
plugins: config['plugins'],
|
|
660
|
+
labels: configLabel === undefined ? undefined : [configLabel],
|
|
661
|
+
releaseLabels: configReleaseLabel === undefined ? undefined : [configReleaseLabel],
|
|
639
662
|
};
|
|
640
663
|
return { config: repositoryConfig, options: manifestOptions };
|
|
641
664
|
}
|
|
@@ -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
|
@@ -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
|
package/package.json
CHANGED