Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Github } from "../../../../providers/github/Github.js";
import { isValidRelease } from "./isValidRelease.js";
import { pickLatestRelease } from "./pickLatestRelease.js";

export async function fetchGithubUpstreamVersion(
repo: string
): Promise<string | null> {
try {
const newVersion = await fetchGithubLatestTag(repo);
if (!isValidRelease(newVersion)) {
if (!newVersion) {
console.log(
`This is not a valid release (probably a release candidate) - ${repo}: ${newVersion}`
`No stable release found (only drafts, pre-releases or release candidates) - ${repo}`
);
return null;
}
Expand All @@ -21,13 +21,12 @@ export async function fetchGithubUpstreamVersion(
}
}

async function fetchGithubLatestTag(repo: string): Promise<string> {
async function fetchGithubLatestTag(repo: string): Promise<string | null> {
const [owner, repoName] = repo.split("/");
const githubRepo = new Github({ owner, repo: repoName });

const releases = await githubRepo.listReleases();
const latestRelease = releases[0];
if (!latestRelease) throw Error(`No release found for ${repo}`);
if (releases.length === 0) throw Error(`No release found for ${repo}`);

return latestRelease.tag_name;
return pickLatestRelease(releases);
}
1 change: 1 addition & 0 deletions src/commands/githubActions/bumpUpstream/github/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./getBumpPrBody.js";
export * from "./getGithubSettings.js";
export * from "./isBranchNew.js";
export * from "./isValidRelease.js";
export * from "./pickLatestRelease.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { isValidRelease } from "./isValidRelease.js";

interface Release {
tag_name: string;
draft: boolean;
prerelease: boolean;
}

/**
* Returns the tag of the newest release that is not a draft, not flagged as a
* pre-release and passes isValidRelease, or null when there is none.
* `releases` comes newest first, as GitHub lists them. A release candidate
* published after a stable release must not hide that stable release.
*/
export function pickLatestRelease(releases: Release[]): string | null {
for (const { tag_name, draft, prerelease } of releases) {
if (draft || prerelease || !isValidRelease(tag_name)) continue;
return tag_name;
}
return null;
}
49 changes: 49 additions & 0 deletions test/commands/gaBumpUpstream/pickLatestRelease.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { expect } from "chai";
import { pickLatestRelease } from "../../../src/commands/githubActions/bumpUpstream/github/index.js";

const release = (
tag_name: string,
{ draft = false, prerelease = false } = {}
) => ({ tag_name, draft, prerelease });

describe("command / gaBumpUpstream / pickLatestRelease", () => {
it("returns the newest release when it is stable", () => {
expect(pickLatestRelease([release("v1.2.0"), release("v1.1.0")])).to.equal(
"v1.2.0"
);
});

it("skips a newer release flagged as pre-release", () => {
expect(
pickLatestRelease([
release("0.3.5-rc.1", { prerelease: true }),
release("0.3.4")
])
).to.equal("0.3.4");
});

it("skips a newer release candidate tag that is not flagged", () => {
expect(
pickLatestRelease([release("v1.2.0-rc.1"), release("v1.1.0")])
).to.equal("v1.1.0");
});

it("skips drafts and nightly builds", () => {
expect(
pickLatestRelease([
release("v2.0.0", { draft: true }),
release("nightly-2026-11-10"),
release("v1.9.0")
])
).to.equal("v1.9.0");
});

it("returns null when no release is stable", () => {
expect(
pickLatestRelease([
release("v2.0.0-rc.2", { prerelease: true }),
release("v2.0.0-rc.1")
])
).to.equal(null);
});
});