From b0ccc44f793a62922c63fffa88a26d4f24a5ab67 Mon Sep 17 00:00:00 2001 From: jjijon7000 Date: Tue, 14 Jul 2026 13:26:53 -0700 Subject: [PATCH 1/2] introduced change that fixes issue 91 --- src/__tests__/fixtures/mock-deps.ts | 9 +++++ src/__tests__/unit/helper.test.ts | 19 +++++++++++ src/__tests__/unit/main.test.ts | 51 +++++++++++++++++++++++++++++ src/helper.ts | 48 +++++++++++++++++++++++++-- src/main.ts | 3 +- src/types/Dependencies.ts | 11 +++++++ 6 files changed, 138 insertions(+), 3 deletions(-) diff --git a/src/__tests__/fixtures/mock-deps.ts b/src/__tests__/fixtures/mock-deps.ts index 4fd4c836..d7ce9cc0 100644 --- a/src/__tests__/fixtures/mock-deps.ts +++ b/src/__tests__/fixtures/mock-deps.ts @@ -41,6 +41,15 @@ export function createMockOctokit( parent: null, }, }), + getLatestRelease: + (overrides.rest?.repos + ?.getLatestRelease as OctokitClient["rest"]["repos"]["getLatestRelease"]) ?? + jest.fn().mockResolvedValue({ + data: { + tag_name: "v1.2.1", + name: "v1.2.1", + }, + }), listLanguages: (overrides.rest?.repos ?.listLanguages as OctokitClient["rest"]["repos"]["listLanguages"]) ?? diff --git a/src/__tests__/unit/helper.test.ts b/src/__tests__/unit/helper.test.ts index 74d05ae6..a59dd051 100644 --- a/src/__tests__/unit/helper.test.ts +++ b/src/__tests__/unit/helper.test.ts @@ -36,6 +36,7 @@ describe("createHelpers - calculateMetaData", () => { const result = await helpers.calculateMetaData(); expect(result.name).toBe("test-repo"); + expect(result.version).toBe("1.2.1"); expect(result.description).toBe("A test repository"); expect(result.repositoryURL).toBe( "https://github.com/test-owner/test-repo", @@ -47,6 +48,24 @@ describe("createHelpers - calculateMetaData", () => { expect(result.tags).toEqual(["test", "automation"]); }); + it("falls back to the existing version when the release lookup fails", async () => { + const mockOctokit = createMockOctokit({ + rest: { + repos: { + getLatestRelease: jest + .fn() + .mockRejectedValue(new Error("rate limited")), + }, + }, + }); + + deps = createMockDeps({ octokit: mockOctokit }); + const helpers = createHelpers(deps); + const result = await helpers.calculateMetaData({ version: "9.9.9" } as any); + + expect(result.version).toBe("9.9.9"); + }); + it("reports private visibility for private repos", async () => { const mockOctokit = createMockOctokit({ rest: { diff --git a/src/__tests__/unit/main.test.ts b/src/__tests__/unit/main.test.ts index 475666be..21ad2b1b 100644 --- a/src/__tests__/unit/main.test.ts +++ b/src/__tests__/unit/main.test.ts @@ -109,6 +109,51 @@ describe("getMetaData", () => { URL: "https://github.com/upstream-owner/upstream-repo", }); }); + + it("uses the latest release version when available", async () => { + const releaseOctokit = createMockOctokit({ + rest: { + repos: { + getLatestRelease: jest.fn().mockResolvedValue({ + data: { + tag_name: "v2.4.6", + name: "Release 2.4.6", + }, + }), + }, + }, + }); + + const deps = createMockDeps({ octokit: releaseOctokit }); + const helpers = createHelpers(deps); + + const result = await getMetaData(helpers, deps, null); + + expect(result.version).toBe("2.4.6"); + }); + + it("falls back to the existing version when latest release cannot be fetched", async () => { + const failingReleaseOctokit = createMockOctokit({ + rest: { + repos: { + getLatestRelease: jest + .fn() + .mockRejectedValue(new Error("network issue")), + }, + }, + }); + + const deps = createMockDeps({ octokit: failingReleaseOctokit }); + const helpers = createHelpers(deps); + + const result = await getMetaData( + helpers, + deps, + { version: "7.8.9" } as any, + ); + + expect(result.version).toBe("7.8.9"); + }); }); describe("runWithDeps", () => { @@ -161,6 +206,12 @@ describe("runWithDeps", () => { get: jest .fn() .mockResolvedValue({ data: { default_branch: "main" } }), + getLatestRelease: jest.fn().mockResolvedValue({ + data: { + tag_name: "v1.2.1", + name: "v1.2.1", + }, + }), listLanguages: jest.fn().mockResolvedValue({ data: {} }), getContent: jest .fn() diff --git a/src/helper.ts b/src/helper.ts index 57be7b12..05d08643 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -13,15 +13,19 @@ export function createHelpers(deps: Dependencies) { //=============================================== // Meta Data //=============================================== - async function calculateMetaData(): Promise> { + async function calculateMetaData( + existingCodeJSON?: CodeJSON | null, + ): Promise> { try { - const [laborHours, basicInfo] = await Promise.all([ + const [laborHours, basicInfo, version] = await Promise.all([ getLaborHours(), getBasicInfo(), + getVersion(existingCodeJSON?.version), ]); return { name: basicInfo.title, + version: version, description: basicInfo.description, repositoryURL: basicInfo.url, repositoryVisibility: basicInfo.repositoryVisibility, @@ -44,6 +48,46 @@ export function createHelpers(deps: Dependencies) { } } + async function getVersion(existingVersion?: string): Promise { + try { + const release = await octokit.rest.repos.getLatestRelease({ owner, repo }); + const versionFromRelease = normalizeVersionString(release.data.tag_name); + + if (versionFromRelease !== "") { + return versionFromRelease; + } + + const releaseName = release.data.name; + if (typeof releaseName === "string") { + const versionFromName = normalizeVersionString(releaseName); + + if (versionFromName !== "") { + return versionFromName; + } + } + + log.warning("Latest release did not include a usable version string."); + } catch (error) { + log.warning(`Failed to fetch latest release version: ${error}`); + } + + if (typeof existingVersion === "string" && existingVersion.trim() !== "") { + return existingVersion.trim(); + } + + return ""; + } + + function normalizeVersionString(value: string): string { + const trimmedValue = value.trim(); + + if (trimmedValue.length > 1 && /^v\d/i.test(trimmedValue)) { + return trimmedValue.replace(/^v/i, ""); + } + + return trimmedValue; + } + async function getBasicInfo(): Promise { try { const [repoData, languagesData] = await Promise.all([ diff --git a/src/main.ts b/src/main.ts index 496e5729..19ebe904 100644 --- a/src/main.ts +++ b/src/main.ts @@ -93,7 +93,7 @@ async function getMetaData( deps: Dependencies, existingCodeJSON?: CodeJSON | null, ): Promise> { - const partialCodeJSON = await helpers.calculateMetaData(); + const partialCodeJSON = await helpers.calculateMetaData(existingCodeJSON); // preserve existing feedback mechanisms if they exist, otherwise default to GitHub Issues const feedbackMechanism = @@ -150,6 +150,7 @@ async function getMetaData( return { name: partialCodeJSON.name, + version: partialCodeJSON.version, description: description, status: status, repositoryURL: partialCodeJSON.repositoryURL, diff --git a/src/types/Dependencies.ts b/src/types/Dependencies.ts index c2c28d18..d6db88da 100644 --- a/src/types/Dependencies.ts +++ b/src/types/Dependencies.ts @@ -32,6 +32,10 @@ export interface OctokitClient { rest: { repos: { get: (params: { owner: string; repo: string }) => Promise; + getLatestRelease: (params: { + owner: string; + repo: string; + }) => Promise; listLanguages: (params: { owner: string; repo: string; @@ -91,6 +95,13 @@ export interface LanguagesResponse { data: Record; } +export interface ReleaseResponse { + data: { + tag_name: string; + name: string | null; + }; +} + export interface ContentResponse { data: { sha?: string } | Array; } From 0aa22c4e21733515fd6735857f85850cdd341777 Mon Sep 17 00:00:00 2001 From: jjijon7000 Date: Wed, 22 Jul 2026 10:19:50 -0700 Subject: [PATCH 2/2] changed based on feedback --- src/__tests__/unit/helper.test.ts | 9 ++++--- src/__tests__/unit/main.test.ts | 45 ++++++++++++++++--------------- src/helper.ts | 12 +++------ src/main.ts | 5 ++-- 4 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/__tests__/unit/helper.test.ts b/src/__tests__/unit/helper.test.ts index a59dd051..3ceed02d 100644 --- a/src/__tests__/unit/helper.test.ts +++ b/src/__tests__/unit/helper.test.ts @@ -48,7 +48,7 @@ describe("createHelpers - calculateMetaData", () => { expect(result.tags).toEqual(["test", "automation"]); }); - it("falls back to the existing version when the release lookup fails", async () => { + it("returns an empty version when the release lookup fails", async () => { const mockOctokit = createMockOctokit({ rest: { repos: { @@ -61,9 +61,12 @@ describe("createHelpers - calculateMetaData", () => { deps = createMockDeps({ octokit: mockOctokit }); const helpers = createHelpers(deps); - const result = await helpers.calculateMetaData({ version: "9.9.9" } as any); + const result = await helpers.calculateMetaData(); - expect(result.version).toBe("9.9.9"); + expect(result.version).toBe(""); + expect(deps.log.warning).toHaveBeenCalledWith( + expect.stringContaining("Failed to fetch latest release version"), + ); }); it("reports private visibility for private repos", async () => { diff --git a/src/__tests__/unit/main.test.ts b/src/__tests__/unit/main.test.ts index 21ad2b1b..324baac2 100644 --- a/src/__tests__/unit/main.test.ts +++ b/src/__tests__/unit/main.test.ts @@ -54,6 +54,29 @@ describe("getMetaData", () => { expect(result.feedbackMechanism).toContain("/issues"); }); + it("preserves an existing version when the latest release is unavailable", async () => { + const releaseOctokit = createMockOctokit({ + rest: { + repos: { + getLatestRelease: jest + .fn() + .mockRejectedValue(new Error("not found")), + }, + }, + }); + + const deps = createMockDeps({ octokit: releaseOctokit }); + const helpers = createHelpers(deps); + + const existing = { + ...validCodeJSON, + version: "7.8.9", + } as any; + const result = await getMetaData(helpers, deps, existing); + + expect(result.version).toBe("7.8.9"); + }); + it("sets Archival status when isArchived", async () => { const deps = createMockDeps({ isArchived: true }); const helpers = createHelpers(deps); @@ -132,28 +155,6 @@ describe("getMetaData", () => { expect(result.version).toBe("2.4.6"); }); - it("falls back to the existing version when latest release cannot be fetched", async () => { - const failingReleaseOctokit = createMockOctokit({ - rest: { - repos: { - getLatestRelease: jest - .fn() - .mockRejectedValue(new Error("network issue")), - }, - }, - }); - - const deps = createMockDeps({ octokit: failingReleaseOctokit }); - const helpers = createHelpers(deps); - - const result = await getMetaData( - helpers, - deps, - { version: "7.8.9" } as any, - ); - - expect(result.version).toBe("7.8.9"); - }); }); describe("runWithDeps", () => { diff --git a/src/helper.ts b/src/helper.ts index 05d08643..4d81143d 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -13,14 +13,12 @@ export function createHelpers(deps: Dependencies) { //=============================================== // Meta Data //=============================================== - async function calculateMetaData( - existingCodeJSON?: CodeJSON | null, - ): Promise> { + async function calculateMetaData(): Promise> { try { const [laborHours, basicInfo, version] = await Promise.all([ getLaborHours(), getBasicInfo(), - getVersion(existingCodeJSON?.version), + getVersion(), ]); return { @@ -48,7 +46,7 @@ export function createHelpers(deps: Dependencies) { } } - async function getVersion(existingVersion?: string): Promise { + async function getVersion(): Promise { try { const release = await octokit.rest.repos.getLatestRelease({ owner, repo }); const versionFromRelease = normalizeVersionString(release.data.tag_name); @@ -71,10 +69,6 @@ export function createHelpers(deps: Dependencies) { log.warning(`Failed to fetch latest release version: ${error}`); } - if (typeof existingVersion === "string" && existingVersion.trim() !== "") { - return existingVersion.trim(); - } - return ""; } diff --git a/src/main.ts b/src/main.ts index 19ebe904..d36115cc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -93,7 +93,8 @@ async function getMetaData( deps: Dependencies, existingCodeJSON?: CodeJSON | null, ): Promise> { - const partialCodeJSON = await helpers.calculateMetaData(existingCodeJSON); + const partialCodeJSON = await helpers.calculateMetaData(); + const version = existingCodeJSON?.version || partialCodeJSON.version; // preserve existing feedback mechanisms if they exist, otherwise default to GitHub Issues const feedbackMechanism = @@ -150,7 +151,7 @@ async function getMetaData( return { name: partialCodeJSON.name, - version: partialCodeJSON.version, + version: version, description: description, status: status, repositoryURL: partialCodeJSON.repositoryURL,