From 85e39a627f0cfd07437797ebe3c54be93e2ceac2 Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Fri, 15 Apr 2022 11:19:23 -0700 Subject: [PATCH 1/3] feat(manifest): allow configuring release-search-depth and commit-search-depth --- src/manifest.ts | 20 ++- .../manifest/config/search-depth.json | 9 + test/helpers.ts | 12 +- test/manifest.ts | 170 ++++++++++++++++++ 4 files changed, 203 insertions(+), 8 deletions(-) create mode 100644 test/fixtures/manifest/config/search-depth.json diff --git a/src/manifest.ts b/src/manifest.ts index 5d6f0433c..68b54675c 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -140,6 +140,8 @@ export interface ManifestOptions { prerelease?: boolean; draftPullRequest?: boolean; groupPullRequestTitlePattern?: string; + releaseSearchDepth?: number; + commitSearchDepth?: number; } interface ReleaserPackageConfig extends ReleaserConfigJson { @@ -173,6 +175,8 @@ export interface ManifestConfig extends ReleaserConfigJson { plugins?: PluginType[]; 'separate-pull-requests'?: boolean; 'group-pull-request-title-pattern'?: string; + 'release-search-depth'?: number; + 'commit-search-depth'?: number; } // path => version export type ReleasedVersions = Record; @@ -187,6 +191,8 @@ export const DEFAULT_LABELS = ['autorelease: pending']; export const DEFAULT_RELEASE_LABELS = ['autorelease: tagged']; export const DEFAULT_SNAPSHOT_LABELS = ['autorelease: snapshot']; export const SNOOZE_LABEL = 'autorelease: snooze'; +const DEFAULT_RELEASE_SEARCH_DEPTH = 400; +const DEFAULT_COMMIT_SEARCH_DEPTH = 500; export const MANIFEST_PULL_REQUEST_TITLE_PATTERN = 'chore: release ${branch}'; @@ -220,6 +226,8 @@ export class Manifest { private prerelease?: boolean; private draftPullRequest?: boolean; private groupPullRequestTitlePattern?: string; + readonly releaseSearchDepth: number; + readonly commitSearchDepth: number; /** * Create a Manifest from explicit config in code. This assumes that the @@ -277,6 +285,10 @@ export class Manifest { this.draftPullRequest = manifestOptions?.draftPullRequest; this.groupPullRequestTitlePattern = manifestOptions?.groupPullRequestTitlePattern; + this.releaseSearchDepth = + manifestOptions?.releaseSearchDepth || DEFAULT_RELEASE_SEARCH_DEPTH; + this.commitSearchDepth = + manifestOptions?.commitSearchDepth || DEFAULT_COMMIT_SEARCH_DEPTH; } /** @@ -403,9 +415,11 @@ export class Manifest { // Releases by path const releasesByPath: Record = {}; + logger.info('release search depth', this.releaseSearchDepth); for await (const release of this.github.releaseIterator({ - maxResults: 400, + maxResults: this.releaseSearchDepth, })) { + logger.warn(release); const tagName = TagName.parse(release.tagName); if (!tagName) { logger.warn(`Unable to parse release name: ${release.name}`); @@ -482,7 +496,7 @@ export class Manifest { logger.info('Collecting commits since all latest releases'); const commits: Commit[] = []; const commitGenerator = this.github.mergeCommitIterator(this.targetBranch, { - maxResults: 500, + maxResults: this.commitSearchDepth, backfillFiles: true, }); const releaseShas = new Set(Object.values(releaseShasByPath)); @@ -1138,6 +1152,8 @@ async function parseConfig( configReleaseLabel === undefined ? undefined : [configReleaseLabel], snapshotLabels: configSnapshotLabel === undefined ? undefined : [configSnapshotLabel], + releaseSearchDepth: config['release-search-depth'], + commitSearchDepth: config['commit-search-depth'], }; return {config: repositoryConfig, options: manifestOptions}; } diff --git a/test/fixtures/manifest/config/search-depth.json b/test/fixtures/manifest/config/search-depth.json new file mode 100644 index 000000000..b454cf1a0 --- /dev/null +++ b/test/fixtures/manifest/config/search-depth.json @@ -0,0 +1,9 @@ +{ + "release-search-depth": 10, + "commit-search-depth": 50, + "packages": { + ".": { + "release-type": "simple" + } + } +} diff --git a/test/helpers.ts b/test/helpers.ts index bce987e7c..07ef11289 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -330,37 +330,37 @@ export function mockCommits( sandbox: sinon.SinonSandbox, github: GitHub, commits: Commit[] -) { +): sinon.SinonStub { async function* fakeGenerator() { for (const commit of commits) { yield commit; } } - sandbox.stub(github, 'mergeCommitIterator').returns(fakeGenerator()); + return sandbox.stub(github, 'mergeCommitIterator').returns(fakeGenerator()); } export function mockReleases( sandbox: sinon.SinonSandbox, github: GitHub, releases: GitHubRelease[] -) { +): sinon.SinonStub { async function* fakeGenerator() { for (const release of releases) { yield release; } } - sandbox.stub(github, 'releaseIterator').returns(fakeGenerator()); + return sandbox.stub(github, 'releaseIterator').returns(fakeGenerator()); } export function mockTags( sandbox: sinon.SinonSandbox, github: GitHub, tags: GitHubTag[] -) { +): sinon.SinonStub { async function* fakeGenerator() { for (const tag of tags) { yield tag; } } - sandbox.stub(github, 'tagIterator').returns(fakeGenerator()); + return sandbox.stub(github, 'tagIterator').returns(fakeGenerator()); } diff --git a/test/manifest.ts b/test/manifest.ts index 13bf05907..cb8baac36 100644 --- a/test/manifest.ts +++ b/test/manifest.ts @@ -527,6 +527,33 @@ describe('Manifest', () => { }, ]); }); + it('should configure search depth from manifest', async () => { + const getFileContentsStub = sandbox.stub( + github, + 'getFileContentsOnBranch' + ); + getFileContentsStub + .withArgs('release-please-config.json', 'main') + .resolves( + buildGitHubFileContent( + fixturesPath, + 'manifest/config/search-depth.json' + ) + ) + .withArgs('.release-please-manifest.json', 'main') + .resolves( + buildGitHubFileContent( + fixturesPath, + 'manifest/versions/versions.json' + ) + ); + const manifest = await Manifest.fromManifest( + github, + github.repository.defaultBranch + ); + expect(manifest.releaseSearchDepth).to.eql(10); + expect(manifest.commitSearchDepth).to.eql(50); + }); }); describe('fromConfig', () => { @@ -2554,6 +2581,149 @@ describe('Manifest', () => { expect(pullRequests[0].labels).to.eql(['autorelease: pending']); snapshot(dateSafe(pullRequests[0].body.toString())); }); + + it('should allow customizing release-search-depth', async () => { + const releaseStub = mockReleases(sandbox, github, []); + mockTags(sandbox, github, [ + { + name: 'pkg1-v1.0.0', + sha: 'abc123', + }, + ]); + mockCommits(sandbox, github, [ + { + sha: 'def456', + message: 'fix: some bugfix', + files: [], + }, + { + sha: 'abc123', + message: 'chore: release 1.0.0', + files: [], + pullRequest: { + headBranchName: 'release-please/branches/main/components/pkg1', + baseBranchName: 'main', + number: 123, + title: 'chore: release 1.0.0', + body: '', + labels: [], + files: [], + sha: 'abc123', + }, + }, + ]); + const getFileContentsStub = sandbox.stub( + github, + 'getFileContentsOnBranch' + ); + getFileContentsStub + .withArgs('package.json', 'main') + .resolves( + buildGitHubFileContent( + fixturesPath, + 'manifest/repo/node/pkg1/package.json' + ) + ); + const manifest = new Manifest( + github, + 'main', + { + '.': { + releaseType: 'node', + }, + }, + { + '.': Version.parse('1.0.0'), + }, + { + releaseSearchDepth: 1, + } + ); + expect(manifest.releaseSearchDepth).to.eql(1); + const pullRequests = await manifest.buildPullRequests(); + expect(pullRequests).lengthOf(1); + const pullRequest = pullRequests[0]; + expect(pullRequest.version?.toString()).to.eql('1.0.1'); + expect(pullRequest.headRefName).to.eql( + 'release-please--branches--main--components--pkg1' + ); + sinon.assert.calledOnceWithMatch( + releaseStub, + sinon.match.has('maxResults', 1) + ); + }); + + it('should allow customizing commit-search-depth', async () => { + mockReleases(sandbox, github, []); + mockTags(sandbox, github, [ + { + name: 'pkg1-v1.0.0', + sha: 'abc123', + }, + ]); + const commitsStub = mockCommits(sandbox, github, [ + { + sha: 'def456', + message: 'fix: some bugfix', + files: [], + }, + { + sha: 'abc123', + message: 'chore: release 1.0.0', + files: [], + pullRequest: { + headBranchName: 'release-please/branches/main/components/pkg1', + baseBranchName: 'main', + number: 123, + title: 'chore: release 1.0.0', + body: '', + labels: [], + files: [], + sha: 'abc123', + }, + }, + ]); + const getFileContentsStub = sandbox.stub( + github, + 'getFileContentsOnBranch' + ); + getFileContentsStub + .withArgs('package.json', 'main') + .resolves( + buildGitHubFileContent( + fixturesPath, + 'manifest/repo/node/pkg1/package.json' + ) + ); + const manifest = new Manifest( + github, + 'main', + { + '.': { + releaseType: 'node', + }, + }, + { + '.': Version.parse('1.0.0'), + }, + { + commitSearchDepth: 1, + } + ); + expect(manifest.commitSearchDepth).to.eql(1); + const pullRequests = await manifest.buildPullRequests(); + expect(pullRequests).lengthOf(1); + const pullRequest = pullRequests[0]; + expect(pullRequest.version?.toString()).to.eql('1.0.1'); + expect(pullRequest.headRefName).to.eql( + 'release-please--branches--main--components--pkg1' + ); + sinon.assert.calledOnceWithMatch( + commitsStub, + 'main', + sinon.match.has('maxResults', 1) + ); + }); }); describe('createPullRequests', () => { From 2a83646b5d7242a0b26a4fd85c86ce6a76dc0e71 Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Fri, 15 Apr 2022 11:24:11 -0700 Subject: [PATCH 2/3] docs: document the new manifest fields --- docs/manifest-releaser.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/manifest-releaser.md b/docs/manifest-releaser.md index c941d4eba..0756a2caa 100644 --- a/docs/manifest-releaser.md +++ b/docs/manifest-releaser.md @@ -210,6 +210,21 @@ documented in comments) // absence defaults to "chore: release ${branch}" "group-pull-request-title-pattern": "chore: release ${branch}", + // When searching for the latest release SHAs, only consider the last N releases. + // This option prevents paginating through all releases in history when we + // expect to find the release within the last N releases. For repositories with + // a large number of individual packages, you may want to consider raising this + // value, but it will increase the number of API calls used. + "release-search-depth": 400, + + // When fetching the list of commits to consider, only consider the last N commits. + // This option limits paginating through every commit in history when we may not + // find the release SHA of the last release (there may not be one). We expect to + // only need to consider the last 500 commits on a branch. For repositories with + // a large number of individual packages, you may want to consider raising this + // value, but it will increase the number of API calls used. + "commit-search-depth": 500, + // per package configuration: at least one entry required. // the key is the relative path from the repo root to the folder that contains // all the files for that package. From 2457511ebb80ca4f06a1f86006d5910318427049 Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Fri, 15 Apr 2022 11:46:28 -0700 Subject: [PATCH 3/3] fix debug log statements --- src/manifest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/manifest.ts b/src/manifest.ts index 68b54675c..4863c095f 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -415,11 +415,10 @@ export class Manifest { // Releases by path const releasesByPath: Record = {}; - logger.info('release search depth', this.releaseSearchDepth); + logger.debug(`release search depth: ${this.releaseSearchDepth}`); for await (const release of this.github.releaseIterator({ maxResults: this.releaseSearchDepth, })) { - logger.warn(release); const tagName = TagName.parse(release.tagName); if (!tagName) { logger.warn(`Unable to parse release name: ${release.name}`); @@ -495,6 +494,7 @@ export class Manifest { // seen all release commits logger.info('Collecting commits since all latest releases'); const commits: Commit[] = []; + logger.debug(`commit search depth: ${this.commitSearchDepth}`); const commitGenerator = this.github.mergeCommitIterator(this.targetBranch, { maxResults: this.commitSearchDepth, backfillFiles: true,