diff --git a/src/lib/gitea-enhanced.test.ts b/src/lib/gitea-enhanced.test.ts index e20f907..81ee3b2 100644 --- a/src/lib/gitea-enhanced.test.ts +++ b/src/lib/gitea-enhanced.test.ts @@ -7,6 +7,9 @@ mock.module("@/lib/helpers", () => ({ createMirrorJob: mockCreateMirrorJob })); +const mockMirrorGitHubReleasesToGitea = mock(() => Promise.resolve()); +const mockGetGiteaRepoOwnerAsync = mock(() => Promise.resolve("starred")); + // Mock the database module const mockDb = { insert: mock((table: any) => ({ @@ -220,6 +223,9 @@ describe("Enhanced Gitea Operations", () => { mockCreateMirrorJob.mockClear(); mockDb.insert.mockClear(); mockDb.update.mockClear(); + mockMirrorGitHubReleasesToGitea.mockClear(); + mockGetGiteaRepoOwnerAsync.mockClear(); + mockGetGiteaRepoOwnerAsync.mockImplementation(() => Promise.resolve("starred")); // Reset tracking variables orgCheckCount = 0; orgTestContext = ""; @@ -250,6 +256,7 @@ describe("Enhanced Gitea Operations", () => { url: "https://gitea.example.com", token: "encrypted-token", defaultOwner: "testuser", + mirrorReleases: true, }, }; @@ -280,6 +287,7 @@ describe("Enhanced Gitea Operations", () => { url: "https://gitea.example.com", token: "encrypted-token", defaultOwner: "testuser", + mirrorReleases: true, }, }; @@ -306,6 +314,7 @@ describe("Enhanced Gitea Operations", () => { url: "https://gitea.example.com", token: "encrypted-token", defaultOwner: "testuser", + mirrorReleases: true, }, }; @@ -362,6 +371,7 @@ describe("Enhanced Gitea Operations", () => { url: "https://gitea.example.com", token: "encrypted-token", defaultOwner: "testuser", + mirrorReleases: true, }, }; @@ -391,6 +401,7 @@ describe("Enhanced Gitea Operations", () => { url: "https://gitea.example.com", token: "encrypted-token", defaultOwner: "testuser", + mirrorReleases: true, }, }; @@ -409,18 +420,17 @@ describe("Enhanced Gitea Operations", () => { updatedAt: new Date(), }; - // Mock getGiteaRepoOwnerAsync - const mockGetOwner = mock(() => Promise.resolve("starred")); - global.import = mock(async (path: string) => { - if (path === "./gitea") { - return { getGiteaRepoOwnerAsync: mockGetOwner }; - } - return {}; - }) as any; - await expect( - syncGiteaRepoEnhanced({ config, repository }) + syncGiteaRepoEnhanced( + { config, repository }, + { + getGiteaRepoOwnerAsync: mockGetGiteaRepoOwnerAsync, + mirrorGitHubReleasesToGitea: mockMirrorGitHubReleasesToGitea, + } + ) ).rejects.toThrow("Repository non-mirror-repo is not a mirror. Cannot sync."); + + expect(mockMirrorGitHubReleasesToGitea).not.toHaveBeenCalled(); }); test("should successfully sync a mirror repository", async () => { @@ -436,6 +446,7 @@ describe("Enhanced Gitea Operations", () => { url: "https://gitea.example.com", token: "encrypted-token", defaultOwner: "testuser", + mirrorReleases: true, }, }; @@ -454,18 +465,22 @@ describe("Enhanced Gitea Operations", () => { updatedAt: new Date(), }; - // Mock getGiteaRepoOwnerAsync - const mockGetOwner = mock(() => Promise.resolve("starred")); - global.import = mock(async (path: string) => { - if (path === "./gitea") { - return { getGiteaRepoOwnerAsync: mockGetOwner }; + const result = await syncGiteaRepoEnhanced( + { config, repository }, + { + getGiteaRepoOwnerAsync: mockGetGiteaRepoOwnerAsync, + mirrorGitHubReleasesToGitea: mockMirrorGitHubReleasesToGitea, } - return {}; - }) as any; - - const result = await syncGiteaRepoEnhanced({ config, repository }); + ); expect(result).toEqual({ success: true }); + expect(mockGetGiteaRepoOwnerAsync).toHaveBeenCalled(); + expect(mockMirrorGitHubReleasesToGitea).toHaveBeenCalledTimes(1); + const releaseCall = mockMirrorGitHubReleasesToGitea.mock.calls[0][0]; + expect(releaseCall.giteaOwner).toBe("starred"); + expect(releaseCall.giteaRepoName).toBe("mirror-repo"); + expect(releaseCall.config.githubConfig?.token).toBe("github-token"); + expect(releaseCall.octokit).toBeDefined(); }); }); @@ -567,4 +582,4 @@ describe("Enhanced Gitea Operations", () => { expect(deleteCalled).toBe(true); }); }); -}); \ No newline at end of file +}); diff --git a/src/lib/gitea-enhanced.ts b/src/lib/gitea-enhanced.ts index 5063683..10a00ff 100644 --- a/src/lib/gitea-enhanced.ts +++ b/src/lib/gitea-enhanced.ts @@ -8,6 +8,7 @@ import type { Config } from "@/types/config"; import type { Repository } from "./db/schema"; +import { Octokit } from "@octokit/rest"; import { createMirrorJob } from "./helpers"; import { decryptConfigTokens } from "./utils/config-encryption"; import { httpPost, httpGet, httpPatch, HttpError } from "./http-client"; @@ -15,6 +16,11 @@ import { db, repositories } from "./db"; import { eq } from "drizzle-orm"; import { repoStatusEnum } from "@/types/Repository"; +type SyncDependencies = { + getGiteaRepoOwnerAsync: typeof import("./gitea")["getGiteaRepoOwnerAsync"]; + mirrorGitHubReleasesToGitea: typeof import("./gitea")["mirrorGitHubReleasesToGitea"]; +}; + /** * Enhanced repository information including mirror status */ @@ -239,7 +245,7 @@ export async function syncGiteaRepoEnhanced({ }: { config: Partial; repository: Repository; -}): Promise { +}, deps?: SyncDependencies): Promise { try { if (!config.userId || !config.giteaConfig?.url || !config.giteaConfig?.token) { throw new Error("Gitea config is required."); @@ -259,8 +265,8 @@ export async function syncGiteaRepoEnhanced({ .where(eq(repositories.id, repository.id!)); // Get the expected owner - const { getGiteaRepoOwnerAsync } = await import("./gitea"); - const repoOwner = await getGiteaRepoOwnerAsync({ config, repository }); + const dependencies = deps ?? (await import("./gitea")); + const repoOwner = await dependencies.getGiteaRepoOwnerAsync({ config, repository }); // Check if repo exists and get its info const repoInfo = await getGiteaRepoInfo({ @@ -324,6 +330,36 @@ export async function syncGiteaRepoEnhanced({ Authorization: `token ${decryptedConfig.giteaConfig.token}`, }); + const shouldMirrorReleases = + decryptedConfig.giteaConfig?.mirrorReleases && + !(repository.isStarred && decryptedConfig.githubConfig?.starredCodeOnly); + + if (shouldMirrorReleases) { + if (!decryptedConfig.githubConfig?.token) { + console.warn( + `[Sync] Skipping release mirroring for ${repository.name}: Missing GitHub token` + ); + } else { + try { + const octokit = new Octokit({ auth: decryptedConfig.githubConfig.token }); + await dependencies.mirrorGitHubReleasesToGitea({ + config: decryptedConfig, + octokit, + repository, + giteaOwner: repoOwner, + giteaRepoName: repository.name, + }); + console.log(`[Sync] Mirrored releases for ${repository.name} after sync`); + } catch (releaseError) { + console.error( + `[Sync] Failed to mirror releases for ${repository.name}: ${ + releaseError instanceof Error ? releaseError.message : String(releaseError) + }` + ); + } + } + } + // Mark repo as "synced" in DB await db .update(repositories) @@ -535,4 +571,4 @@ export async function handleExistingNonMirrorRepo({ // TODO: Implement rename strategy if needed break; } -} \ No newline at end of file +}