fix: mirror releases during sync

This commit is contained in:
Arunavo Ray
2025-10-22 09:33:13 +05:30
parent 3b53a29e71
commit f63633f97e
2 changed files with 75 additions and 24 deletions

View File

@@ -7,6 +7,9 @@ mock.module("@/lib/helpers", () => ({
createMirrorJob: mockCreateMirrorJob createMirrorJob: mockCreateMirrorJob
})); }));
const mockMirrorGitHubReleasesToGitea = mock(() => Promise.resolve());
const mockGetGiteaRepoOwnerAsync = mock(() => Promise.resolve("starred"));
// Mock the database module // Mock the database module
const mockDb = { const mockDb = {
insert: mock((table: any) => ({ insert: mock((table: any) => ({
@@ -220,6 +223,9 @@ describe("Enhanced Gitea Operations", () => {
mockCreateMirrorJob.mockClear(); mockCreateMirrorJob.mockClear();
mockDb.insert.mockClear(); mockDb.insert.mockClear();
mockDb.update.mockClear(); mockDb.update.mockClear();
mockMirrorGitHubReleasesToGitea.mockClear();
mockGetGiteaRepoOwnerAsync.mockClear();
mockGetGiteaRepoOwnerAsync.mockImplementation(() => Promise.resolve("starred"));
// Reset tracking variables // Reset tracking variables
orgCheckCount = 0; orgCheckCount = 0;
orgTestContext = ""; orgTestContext = "";
@@ -250,6 +256,7 @@ describe("Enhanced Gitea Operations", () => {
url: "https://gitea.example.com", url: "https://gitea.example.com",
token: "encrypted-token", token: "encrypted-token",
defaultOwner: "testuser", defaultOwner: "testuser",
mirrorReleases: true,
}, },
}; };
@@ -280,6 +287,7 @@ describe("Enhanced Gitea Operations", () => {
url: "https://gitea.example.com", url: "https://gitea.example.com",
token: "encrypted-token", token: "encrypted-token",
defaultOwner: "testuser", defaultOwner: "testuser",
mirrorReleases: true,
}, },
}; };
@@ -306,6 +314,7 @@ describe("Enhanced Gitea Operations", () => {
url: "https://gitea.example.com", url: "https://gitea.example.com",
token: "encrypted-token", token: "encrypted-token",
defaultOwner: "testuser", defaultOwner: "testuser",
mirrorReleases: true,
}, },
}; };
@@ -362,6 +371,7 @@ describe("Enhanced Gitea Operations", () => {
url: "https://gitea.example.com", url: "https://gitea.example.com",
token: "encrypted-token", token: "encrypted-token",
defaultOwner: "testuser", defaultOwner: "testuser",
mirrorReleases: true,
}, },
}; };
@@ -391,6 +401,7 @@ describe("Enhanced Gitea Operations", () => {
url: "https://gitea.example.com", url: "https://gitea.example.com",
token: "encrypted-token", token: "encrypted-token",
defaultOwner: "testuser", defaultOwner: "testuser",
mirrorReleases: true,
}, },
}; };
@@ -409,18 +420,17 @@ describe("Enhanced Gitea Operations", () => {
updatedAt: new Date(), 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( await expect(
syncGiteaRepoEnhanced({ config, repository }) syncGiteaRepoEnhanced(
{ config, repository },
{
getGiteaRepoOwnerAsync: mockGetGiteaRepoOwnerAsync,
mirrorGitHubReleasesToGitea: mockMirrorGitHubReleasesToGitea,
}
)
).rejects.toThrow("Repository non-mirror-repo is not a mirror. Cannot sync."); ).rejects.toThrow("Repository non-mirror-repo is not a mirror. Cannot sync.");
expect(mockMirrorGitHubReleasesToGitea).not.toHaveBeenCalled();
}); });
test("should successfully sync a mirror repository", async () => { test("should successfully sync a mirror repository", async () => {
@@ -436,6 +446,7 @@ describe("Enhanced Gitea Operations", () => {
url: "https://gitea.example.com", url: "https://gitea.example.com",
token: "encrypted-token", token: "encrypted-token",
defaultOwner: "testuser", defaultOwner: "testuser",
mirrorReleases: true,
}, },
}; };
@@ -454,18 +465,22 @@ describe("Enhanced Gitea Operations", () => {
updatedAt: new Date(), updatedAt: new Date(),
}; };
// Mock getGiteaRepoOwnerAsync const result = await syncGiteaRepoEnhanced(
const mockGetOwner = mock(() => Promise.resolve("starred")); { config, repository },
global.import = mock(async (path: string) => { {
if (path === "./gitea") { getGiteaRepoOwnerAsync: mockGetGiteaRepoOwnerAsync,
return { getGiteaRepoOwnerAsync: mockGetOwner }; mirrorGitHubReleasesToGitea: mockMirrorGitHubReleasesToGitea,
} }
return {}; );
}) as any;
const result = await syncGiteaRepoEnhanced({ config, repository });
expect(result).toEqual({ success: true }); 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); expect(deleteCalled).toBe(true);
}); });
}); });
}); });

View File

@@ -8,6 +8,7 @@
import type { Config } from "@/types/config"; import type { Config } from "@/types/config";
import type { Repository } from "./db/schema"; import type { Repository } from "./db/schema";
import { Octokit } from "@octokit/rest";
import { createMirrorJob } from "./helpers"; import { createMirrorJob } from "./helpers";
import { decryptConfigTokens } from "./utils/config-encryption"; import { decryptConfigTokens } from "./utils/config-encryption";
import { httpPost, httpGet, httpPatch, HttpError } from "./http-client"; import { httpPost, httpGet, httpPatch, HttpError } from "./http-client";
@@ -15,6 +16,11 @@ import { db, repositories } from "./db";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { repoStatusEnum } from "@/types/Repository"; import { repoStatusEnum } from "@/types/Repository";
type SyncDependencies = {
getGiteaRepoOwnerAsync: typeof import("./gitea")["getGiteaRepoOwnerAsync"];
mirrorGitHubReleasesToGitea: typeof import("./gitea")["mirrorGitHubReleasesToGitea"];
};
/** /**
* Enhanced repository information including mirror status * Enhanced repository information including mirror status
*/ */
@@ -239,7 +245,7 @@ export async function syncGiteaRepoEnhanced({
}: { }: {
config: Partial<Config>; config: Partial<Config>;
repository: Repository; repository: Repository;
}): Promise<any> { }, deps?: SyncDependencies): Promise<any> {
try { try {
if (!config.userId || !config.giteaConfig?.url || !config.giteaConfig?.token) { if (!config.userId || !config.giteaConfig?.url || !config.giteaConfig?.token) {
throw new Error("Gitea config is required."); throw new Error("Gitea config is required.");
@@ -259,8 +265,8 @@ export async function syncGiteaRepoEnhanced({
.where(eq(repositories.id, repository.id!)); .where(eq(repositories.id, repository.id!));
// Get the expected owner // Get the expected owner
const { getGiteaRepoOwnerAsync } = await import("./gitea"); const dependencies = deps ?? (await import("./gitea"));
const repoOwner = await getGiteaRepoOwnerAsync({ config, repository }); const repoOwner = await dependencies.getGiteaRepoOwnerAsync({ config, repository });
// Check if repo exists and get its info // Check if repo exists and get its info
const repoInfo = await getGiteaRepoInfo({ const repoInfo = await getGiteaRepoInfo({
@@ -324,6 +330,36 @@ export async function syncGiteaRepoEnhanced({
Authorization: `token ${decryptedConfig.giteaConfig.token}`, 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 // Mark repo as "synced" in DB
await db await db
.update(repositories) .update(repositories)
@@ -535,4 +571,4 @@ export async function handleExistingNonMirrorRepo({
// TODO: Implement rename strategy if needed // TODO: Implement rename strategy if needed
break; break;
} }
} }