Skip to content

Commit

Permalink
fix: fallback to look at tags when looking for latest release (#1160)
Browse files Browse the repository at this point in the history
Co-authored-by: Benjamin E. Coe <bencoe@google.com>
  • Loading branch information
chingor13 and bcoe committed Dec 22, 2021
1 parent c568b57 commit e06c6ba
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 4 deletions.
42 changes: 41 additions & 1 deletion src/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ interface ReleaseIteratorOptions {
maxResults?: number;
}

interface TagIteratorOptions {
maxResults?: number;
}

export interface GitHubRelease {
name?: string;
tagName: string;
Expand All @@ -153,6 +157,11 @@ export interface GitHubRelease {
draft?: boolean;
}

export interface GitHubTag {
name: string;
sha: string;
}

export class GitHub {
readonly repository: Repository;
private octokit: OctokitType;
Expand Down Expand Up @@ -583,7 +592,7 @@ export class GitHub {
}

/**
* Iterate through merged pull requests with a max number of results scanned.
* Iterate through releases with a max number of results scanned.
*
* @param {ReleaseIteratorOptions} options Query options
* @param {number} options.maxResults Limit the number of results searched.
Expand Down Expand Up @@ -670,6 +679,37 @@ export class GitHub {
};
}

/**
* Iterate through tags with a max number of results scanned.
*
* @param {TagIteratorOptions} options Query options
* @param {number} options.maxResults Limit the number of results searched.
* Defaults to unlimited.
* @yields {GitHubTag}
* @throws {GitHubAPIError} on an API error
*/
async *tagIterator(options: TagIteratorOptions = {}) {
const maxResults = options.maxResults || Number.MAX_SAFE_INTEGER;
let results = 0;
for await (const response of this.octokit.paginate.iterator(
this.octokit.rest.repos.listTags,
{
owner: this.repository.owner,
repo: this.repository.repo,
}
)) {
for (const tag of response.data) {
if ((results += 1) > maxResults) {
break;
}
yield {
name: tag.name,
sha: tag.commit.sha,
};
}
}
}

/**
* Fetch the contents of a file from the configured branch
*
Expand Down
34 changes: 32 additions & 2 deletions src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,7 @@ async function latestReleaseVersion(
continue;
}

if (tagName.component === prefix) {
if (tagName.component === branchPrefix) {
logger.debug(`found release for ${prefix}`, tagName.version);
if (!commitShas.has(release.sha)) {
logger.debug(
Expand All @@ -1011,8 +1011,38 @@ async function latestReleaseVersion(
candidateReleaseVersions
);

if (candidateReleaseVersions.length > 0) {
// Find largest release number (sort descending then return first)
return candidateReleaseVersions.sort((a, b) => b.compare(a))[0];
}

// If not found from recent pull requests or releases, look at tags. Iterate
// through tags and cross reference against SHAs in this branch
const tagGenerator = github.tagIterator();
const candidateTagVersion: Version[] = [];
for await (const tag of tagGenerator) {
const tagName = TagName.parse(tag.name);
if (!tagName) {
continue;
}

if (tagName.component === branchPrefix) {
if (!commitShas.has(tag.sha)) {
logger.debug(
`SHA not found in recent commits to branch ${targetBranch}, skipping`
);
continue;
}
candidateTagVersion.push(tagName.version);
}
}
logger.debug(
`found ${candidateTagVersion.length} possible tags.`,
candidateTagVersion
);

// Find largest release number (sort descending then return first)
return candidateReleaseVersions.sort((a, b) => b.compare(a))[0];
return candidateTagVersion.sort((a, b) => b.compare(a))[0];
}

function mergeReleaserConfig(
Expand Down
112 changes: 111 additions & 1 deletion test/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import {describe, it, beforeEach, afterEach} from 'mocha';
import {Manifest} from '../src/manifest';
import {GitHub, GitHubRelease} from '../src/github';
import {GitHub, GitHubRelease, GitHubTag} from '../src/github';
import * as sinon from 'sinon';
import {Commit} from '../src/commit';
import {
Expand Down Expand Up @@ -59,6 +59,15 @@ function mockReleases(github: GitHub, releases: GitHubRelease[]) {
sandbox.stub(github, 'releaseIterator').returns(fakeGenerator());
}

function mockTags(github: GitHub, tags: GitHubTag[]) {
async function* fakeGenerator() {
for (const tag of tags) {
yield tag;
}
}
sandbox.stub(github, 'tagIterator').returns(fakeGenerator());
}

function mockPullRequests(
github: GitHub,
pullRequests: PullRequest[],
Expand Down Expand Up @@ -335,6 +344,47 @@ describe('Manifest', () => {
'3.3.3'
);
});
it('finds legacy tags', async () => {
mockCommits(github, [
{
sha: 'abc123',
message: 'some commit message',
files: [],
pullRequest: {
headBranchName: 'release-please/branches/main/components/foobar',
baseBranchName: 'main',
number: 123,
title: 'chore: release foobar 1.2.3',
body: '',
labels: [],
files: [],
},
},
]);
mockReleases(github, []);
mockTags(github, [
{
name: 'other-v3.3.3',
sha: 'abc123',
},
]);

const manifest = await Manifest.fromConfig(github, 'target-branch', {
releaseType: 'simple',
bumpMinorPreMajor: true,
bumpPatchForMinorPreMajor: true,
component: 'other',
includeComponentInTag: true,
});
expect(Object.keys(manifest.repositoryConfig)).lengthOf(1);
expect(
Object.keys(manifest.releasedVersions),
'found release versions'
).lengthOf(1);
expect(Object.values(manifest.releasedVersions)[0].toString()).to.eql(
'3.3.3'
);
});
it('ignores manually tagged release if commit not found', async () => {
mockCommits(github, [
{
Expand All @@ -359,6 +409,7 @@ describe('Manifest', () => {
url: 'http://path/to/release',
},
]);
mockTags(github, []);

const manifest = await Manifest.fromConfig(github, 'target-branch', {
releaseType: 'simple',
Expand Down Expand Up @@ -415,6 +466,65 @@ describe('Manifest', () => {
},
]);

const manifest = await Manifest.fromConfig(github, 'target-branch', {
releaseType: 'simple',
bumpMinorPreMajor: true,
bumpPatchForMinorPreMajor: true,
component: 'other',
includeComponentInTag: true,
});
expect(Object.keys(manifest.repositoryConfig)).lengthOf(1);
expect(
Object.keys(manifest.releasedVersions),
'found release versions'
).lengthOf(1);
expect(Object.values(manifest.releasedVersions)[0].toString()).to.eql(
'3.3.3'
);
});
it('finds largest found tagged', async () => {
mockCommits(github, [
{
sha: 'abc123',
message: 'some commit message',
files: [],
pullRequest: {
headBranchName: 'release-please/branches/main/components/foobar',
baseBranchName: 'main',
number: 123,
title: 'chore: release foobar 1.2.3',
body: '',
labels: [],
files: [],
},
},
{
sha: 'def234',
message: 'some commit message',
files: [],
pullRequest: {
headBranchName: 'release-please/branches/main/components/foobar',
baseBranchName: 'main',
number: 123,
title: 'chore: release foobar 1.2.3',
body: '',
labels: [],
files: [],
},
},
]);
mockReleases(github, []);
mockTags(github, [
{
name: 'other-v3.3.3',
sha: 'abc123',
},
{
name: 'other-v3.3.2',
sha: 'def234',
},
]);

const manifest = await Manifest.fromConfig(github, 'target-branch', {
releaseType: 'simple',
bumpMinorPreMajor: true,
Expand Down

0 comments on commit e06c6ba

Please sign in to comment.