diff --git a/.env.example b/.env.example index b6924ff..ea64e23 100644 --- a/.env.example +++ b/.env.example @@ -47,6 +47,7 @@ DOCKER_TAG=latest # SKIP_FORKS=false # MIRROR_STARRED=false # STARRED_REPOS_ORG=starred # Organization name for starred repos +# STARRED_REPOS_MODE=dedicated-org # dedicated-org | preserve-owner # Organization Settings # MIRROR_ORGANIZATIONS=false @@ -183,4 +184,4 @@ DOCKER_TAG=latest # =========================================== # TLS/SSL Configuration -# GITEA_SKIP_TLS_VERIFY=false # WARNING: Only use for testing \ No newline at end of file +# GITEA_SKIP_TLS_VERIFY=false # WARNING: Only use for testing diff --git a/README.md b/README.md index 515bb87..e82f634 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ bun run dev 3. **Customization** - Click edit buttons on organization cards to set custom destinations - Override individual repository destinations in the table view - - Starred repositories automatically go to a dedicated organization + - Starred repositories can go to a dedicated org or preserve source owner/org paths ## Advanced Features diff --git a/docs/ENVIRONMENT_VARIABLES.md b/docs/ENVIRONMENT_VARIABLES.md index 3e0b314..1f7c3f5 100644 --- a/docs/ENVIRONMENT_VARIABLES.md +++ b/docs/ENVIRONMENT_VARIABLES.md @@ -62,6 +62,7 @@ Settings for connecting to and configuring GitHub repository sources. | `SKIP_FORKS` | Skip forked repositories | `false` | `true`, `false` | | `MIRROR_STARRED` | Mirror starred repositories | `false` | `true`, `false` | | `STARRED_REPOS_ORG` | Organization name for starred repos | `starred` | Any string | +| `STARRED_REPOS_MODE` | How starred repos are mirrored | `dedicated-org` | `dedicated-org`, `preserve-owner` | ### Organization Settings diff --git a/src/components/config/ConfigTabs.tsx b/src/components/config/ConfigTabs.tsx index 6a05fb8..4e2bcde 100644 --- a/src/components/config/ConfigTabs.tsx +++ b/src/components/config/ConfigTabs.tsx @@ -47,6 +47,7 @@ export function ConfigTabs() { organization: 'github-mirrors', visibility: 'public', starredReposOrg: 'starred', + starredReposMode: 'dedicated-org', preserveOrgStructure: false, }, scheduleConfig: { diff --git a/src/components/config/GiteaConfigForm.tsx b/src/components/config/GiteaConfigForm.tsx index 08f542d..a9d543d 100644 --- a/src/components/config/GiteaConfigForm.tsx +++ b/src/components/config/GiteaConfigForm.tsx @@ -224,6 +224,7 @@ export function GiteaConfigForm({ config, setConfig, onAutoSave, isAutoSaving, g strategy={mirrorStrategy} destinationOrg={config.organization} starredReposOrg={config.starredReposOrg} + starredReposMode={config.starredReposMode} onStrategyChange={setMirrorStrategy} githubUsername={githubUsername} giteaUsername={config.username} @@ -235,6 +236,7 @@ export function GiteaConfigForm({ config, setConfig, onAutoSave, isAutoSaving, g strategy={mirrorStrategy} destinationOrg={config.organization} starredReposOrg={config.starredReposOrg} + starredReposMode={config.starredReposMode} personalReposOrg={config.personalReposOrg} visibility={config.visibility} onDestinationOrgChange={(org) => { @@ -247,6 +249,11 @@ export function GiteaConfigForm({ config, setConfig, onAutoSave, isAutoSaving, g setConfig(newConfig); if (onAutoSave) onAutoSave(newConfig); }} + onStarredReposModeChange={(mode) => { + const newConfig = { ...config, starredReposMode: mode }; + setConfig(newConfig); + if (onAutoSave) onAutoSave(newConfig); + }} onPersonalReposOrgChange={(org) => { const newConfig = { ...config, personalReposOrg: org }; setConfig(newConfig); diff --git a/src/components/config/OrganizationConfiguration.tsx b/src/components/config/OrganizationConfiguration.tsx index 6e4cf99..3dfb40f 100644 --- a/src/components/config/OrganizationConfiguration.tsx +++ b/src/components/config/OrganizationConfiguration.tsx @@ -9,16 +9,18 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; -import type { MirrorStrategy, GiteaOrgVisibility } from "@/types/config"; +import type { MirrorStrategy, GiteaOrgVisibility, StarredReposMode } from "@/types/config"; interface OrganizationConfigurationProps { strategy: MirrorStrategy; destinationOrg?: string; starredReposOrg?: string; + starredReposMode?: StarredReposMode; personalReposOrg?: string; visibility: GiteaOrgVisibility; onDestinationOrgChange: (org: string) => void; onStarredReposOrgChange: (org: string) => void; + onStarredReposModeChange: (mode: StarredReposMode) => void; onPersonalReposOrgChange: (org: string) => void; onVisibilityChange: (visibility: GiteaOrgVisibility) => void; } @@ -33,13 +35,19 @@ export const OrganizationConfiguration: React.FC strategy, destinationOrg, starredReposOrg, + starredReposMode, personalReposOrg, visibility, onDestinationOrgChange, onStarredReposOrgChange, + onStarredReposModeChange, onPersonalReposOrgChange, onVisibilityChange, }) => { + const activeStarredMode = starredReposMode || "dedicated-org"; + const showStarredReposOrgInput = activeStarredMode === "dedicated-org"; + const showDestinationOrgInput = strategy === "single-org" || strategy === "mixed"; + return (
@@ -49,38 +57,83 @@ export const OrganizationConfiguration: React.FC
- {/* First row - Organization inputs with consistent layout */} -
- {/* Left column - always shows starred repos org */} -
- - onStarredReposOrgChange(e.target.value)} - placeholder="starred" - className="" - /> -

- Keep starred repos organized separately -

+
+ +
+ +
+
- {/* Right column - shows destination org for single-org/mixed, personal repos org for preserve, empty div for others */} - {strategy === "single-org" || strategy === "mixed" ? ( + {/* First row - Organization inputs */} + {(showStarredReposOrgInput || showDestinationOrgInput) && ( +
+ {showStarredReposOrgInput ? ( +
+ + onStarredReposOrgChange(e.target.value)} + placeholder="starred" + className="" + /> +

+ Keep starred repos organized separately +

+
+ ) : ( +
+ )} + + {showDestinationOrgInput ? (
- ) : ( -
- )} -
+ ) : ( +
+ )} +
+ )} {/* Second row - Organization Visibility (always shown) */}
@@ -172,4 +226,3 @@ export const OrganizationConfiguration: React.FC
); }; - diff --git a/src/components/config/OrganizationStrategy.tsx b/src/components/config/OrganizationStrategy.tsx index c6f0315..af62636 100644 --- a/src/components/config/OrganizationStrategy.tsx +++ b/src/components/config/OrganizationStrategy.tsx @@ -8,6 +8,7 @@ import { HoverCardTrigger, } from "@/components/ui/hover-card"; import { cn } from "@/lib/utils"; +import type { StarredReposMode } from "@/types/config"; export type MirrorStrategy = "preserve" | "single-org" | "flat-user" | "mixed"; @@ -15,6 +16,7 @@ interface OrganizationStrategyProps { strategy: MirrorStrategy; destinationOrg?: string; starredReposOrg?: string; + starredReposMode?: StarredReposMode; onStrategyChange: (strategy: MirrorStrategy) => void; githubUsername?: string; giteaUsername?: string; @@ -76,13 +78,18 @@ const MappingPreview: React.FC<{ config: typeof strategyConfig.preserve; destinationOrg?: string; starredReposOrg?: string; + starredReposMode?: StarredReposMode; githubUsername?: string; giteaUsername?: string; -}> = ({ strategy, config, destinationOrg, starredReposOrg, githubUsername, giteaUsername }) => { +}> = ({ strategy, config, destinationOrg, starredReposOrg, starredReposMode, githubUsername, giteaUsername }) => { const displayGithubUsername = githubUsername || ""; const displayGiteaUsername = giteaUsername || ""; const isGithubPlaceholder = !githubUsername; const isGiteaPlaceholder = !giteaUsername; + const starredDestination = + (starredReposMode || "dedicated-org") === "preserve-owner" + ? "awesome/starred-repo" + : `${starredReposOrg || "starred"}/starred-repo`; if (strategy === "preserve") { return ( @@ -122,7 +129,7 @@ const MappingPreview: React.FC<{
- {starredReposOrg || "starred"}/starred-repo + {starredDestination}
@@ -168,7 +175,7 @@ const MappingPreview: React.FC<{
- {starredReposOrg || "starred"}/starred-repo + {starredDestination}
@@ -214,7 +221,7 @@ const MappingPreview: React.FC<{
- {starredReposOrg || "starred"}/starred-repo + {starredDestination}
@@ -260,7 +267,7 @@ const MappingPreview: React.FC<{
- {starredReposOrg || "starred"}/starred-repo + {starredDestination}
@@ -275,6 +282,7 @@ export const OrganizationStrategy: React.FC = ({ strategy, destinationOrg, starredReposOrg, + starredReposMode, onStrategyChange, githubUsername, giteaUsername, @@ -339,7 +347,7 @@ export const OrganizationStrategy: React.FC = ({ Starred Repositories

- Always go to the configured starred repos organization and cannot be overridden. + Follow your starred-repo mode and cannot be overridden per repository.

@@ -415,6 +423,7 @@ export const OrganizationStrategy: React.FC = ({ config={config} destinationOrg={destinationOrg} starredReposOrg={starredReposOrg} + starredReposMode={starredReposMode} githubUsername={githubUsername} giteaUsername={giteaUsername} /> @@ -434,4 +443,4 @@ export const OrganizationStrategy: React.FC = ({ ); -}; \ No newline at end of file +}; diff --git a/src/components/repositories/InlineDestinationEditor.tsx b/src/components/repositories/InlineDestinationEditor.tsx index 8a9dc2a..846c4e5 100644 --- a/src/components/repositories/InlineDestinationEditor.tsx +++ b/src/components/repositories/InlineDestinationEditor.tsx @@ -28,9 +28,16 @@ export function InlineDestinationEditor({ // Determine the default destination based on repository properties and config const getDefaultDestination = () => { - // Starred repos always go to the configured starredReposOrg - if (repository.isStarred && giteaConfig?.starredReposOrg) { - return giteaConfig.starredReposOrg; + // Starred repos can use either dedicated org or preserved source owner + if (repository.isStarred) { + const starredReposMode = giteaConfig?.starredReposMode || "dedicated-org"; + if (starredReposMode === "preserve-owner") { + return repository.organization || repository.owner; + } + if (giteaConfig?.starredReposOrg) { + return giteaConfig.starredReposOrg; + } + return "starred"; } // Check mirror strategy @@ -60,7 +67,7 @@ export function InlineDestinationEditor({ const defaultDestination = getDefaultDestination(); const currentDestination = repository.destinationOrg || defaultDestination; const hasOverride = repository.destinationOrg && repository.destinationOrg !== defaultDestination; - const isStarredRepo = repository.isStarred && giteaConfig?.starredReposOrg; + const isStarredRepo = repository.isStarred; useEffect(() => { if (isEditing && inputRef.current) { @@ -184,4 +191,4 @@ export function InlineDestinationEditor({ ); -} \ No newline at end of file +} diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index 20fb0a2..7fb4e37 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -25,6 +25,7 @@ export const githubConfigSchema = z.object({ includePublic: z.boolean().default(true), includeOrganizations: z.array(z.string()).default([]), starredReposOrg: z.string().optional(), + starredReposMode: z.enum(["dedicated-org", "preserve-owner"]).default("dedicated-org"), mirrorStrategy: z.enum(["preserve", "single-org", "flat-user", "mixed"]).default("preserve"), defaultOrg: z.string().optional(), starredCodeOnly: z.boolean().default(false), diff --git a/src/lib/env-config-loader.ts b/src/lib/env-config-loader.ts index 635b3ab..5184bd6 100644 --- a/src/lib/env-config-loader.ts +++ b/src/lib/env-config-loader.ts @@ -23,6 +23,7 @@ interface EnvConfig { onlyMirrorOrgs?: boolean; starredCodeOnly?: boolean; starredReposOrg?: string; + starredReposMode?: 'dedicated-org' | 'preserve-owner'; mirrorStrategy?: 'preserve' | 'single-org' | 'flat-user' | 'mixed'; }; gitea: { @@ -112,6 +113,7 @@ function parseEnvConfig(): EnvConfig { onlyMirrorOrgs: process.env.ONLY_MIRROR_ORGS === 'true', starredCodeOnly: process.env.SKIP_STARRED_ISSUES === 'true', starredReposOrg: process.env.STARRED_REPOS_ORG, + starredReposMode: process.env.STARRED_REPOS_MODE as 'dedicated-org' | 'preserve-owner', mirrorStrategy: process.env.MIRROR_STRATEGY as 'preserve' | 'single-org' | 'flat-user' | 'mixed', }, gitea: { @@ -256,6 +258,7 @@ export async function initializeConfigFromEnv(): Promise { includePublic: envConfig.github.publicRepositories ?? existingConfig?.[0]?.githubConfig?.includePublic ?? true, includeOrganizations: envConfig.github.mirrorOrganizations ? [] : (existingConfig?.[0]?.githubConfig?.includeOrganizations ?? []), starredReposOrg: envConfig.github.starredReposOrg || existingConfig?.[0]?.githubConfig?.starredReposOrg || 'starred', + starredReposMode: envConfig.github.starredReposMode || existingConfig?.[0]?.githubConfig?.starredReposMode || 'dedicated-org', mirrorStrategy, defaultOrg: envConfig.gitea.organization || existingConfig?.[0]?.githubConfig?.defaultOrg || 'github-mirrors', starredCodeOnly: envConfig.github.starredCodeOnly ?? existingConfig?.[0]?.githubConfig?.starredCodeOnly ?? false, diff --git a/src/lib/gitea.test.ts b/src/lib/gitea.test.ts index 7b5711d..cb5a1c3 100644 --- a/src/lib/gitea.test.ts +++ b/src/lib/gitea.test.ts @@ -342,6 +342,8 @@ describe("getGiteaRepoOwner - Organization Override Tests", () => { mirrorPublicOrgs: false, publicOrgs: [], starredCodeOnly: false, + starredReposOrg: "starred", + starredReposMode: "dedicated-org", mirrorStrategy: "preserve" }, giteaConfig: { @@ -350,7 +352,6 @@ describe("getGiteaRepoOwner - Organization Override Tests", () => { token: "gitea-token", organization: "github-mirrors", visibility: "public", - starredReposOrg: "starred", preserveVisibility: false } }; @@ -390,8 +391,8 @@ describe("getGiteaRepoOwner - Organization Override Tests", () => { const repo = { ...baseRepo, isStarred: true }; const configWithoutStarredOrg = { ...baseConfig, - giteaConfig: { - ...baseConfig.giteaConfig, + githubConfig: { + ...baseConfig.githubConfig, starredReposOrg: undefined } }; @@ -399,6 +400,34 @@ describe("getGiteaRepoOwner - Organization Override Tests", () => { expect(result).toBe("starred"); }); + test("starred repos preserve owner/org when starredReposMode is preserve-owner", () => { + const repo = { ...baseRepo, isStarred: true, owner: "FOO", organization: "FOO", fullName: "FOO/BAR" }; + const configWithPreserveStarred = { + ...baseConfig, + githubConfig: { + ...baseConfig.githubConfig!, + starredReposMode: "preserve-owner" as const, + }, + }; + + const result = getGiteaRepoOwner({ config: configWithPreserveStarred, repository: repo }); + expect(result).toBe("FOO"); + }); + + test("starred personal repos preserve owner when starredReposMode is preserve-owner", () => { + const repo = { ...baseRepo, isStarred: true, owner: "alice", organization: undefined, fullName: "alice/demo" }; + const configWithPreserveStarred = { + ...baseConfig, + githubConfig: { + ...baseConfig.githubConfig!, + starredReposMode: "preserve-owner" as const, + }, + }; + + const result = getGiteaRepoOwner({ config: configWithPreserveStarred, repository: repo }); + expect(result).toBe("alice"); + }); + // Removed test for personalReposOrg as this field no longer exists test("preserve strategy: personal repos fallback to username when no override", () => { @@ -492,4 +521,24 @@ describe("getGiteaRepoOwner - Organization Override Tests", () => { expect(result).toBe("custom-org"); }); + + test("getGiteaRepoOwnerAsync preserves starred owner when preserve-owner mode is enabled", async () => { + const configWithUser: Partial = { + ...baseConfig, + userId: "user-id", + githubConfig: { + ...baseConfig.githubConfig!, + starredReposMode: "preserve-owner", + }, + }; + + const repo = { ...baseRepo, isStarred: true, owner: "FOO", organization: "FOO", fullName: "FOO/BAR" }; + + const result = await getGiteaRepoOwnerAsync({ + config: configWithUser, + repository: repo, + }); + + expect(result).toBe("FOO"); + }); }); diff --git a/src/lib/gitea.ts b/src/lib/gitea.ts index bca0b55..a4166a3 100644 --- a/src/lib/gitea.ts +++ b/src/lib/gitea.ts @@ -77,8 +77,12 @@ export const getGiteaRepoOwnerAsync = async ({ throw new Error("User ID is required for organization overrides."); } - // Check if repository is starred - starred repos always go to starredReposOrg (highest priority) + // Check if repository is starred if (repository.isStarred) { + const starredReposMode = config.githubConfig.starredReposMode || "dedicated-org"; + if (starredReposMode === "preserve-owner") { + return repository.organization || repository.owner; + } return config.githubConfig.starredReposOrg || "starred"; } @@ -122,8 +126,12 @@ export const getGiteaRepoOwner = ({ throw new Error("Gitea username is required."); } - // Check if repository is starred - starred repos always go to starredReposOrg + // Check if repository is starred if (repository.isStarred) { + const starredReposMode = config.githubConfig.starredReposMode || "dedicated-org"; + if (starredReposMode === "preserve-owner") { + return repository.organization || repository.owner; + } return config.githubConfig.starredReposOrg || "starred"; } @@ -372,7 +380,11 @@ export const mirrorGithubRepoToGitea = async ({ // Determine the actual repository name to use (handle duplicates for starred repos) let targetRepoName = repository.name; - if (repository.isStarred && config.githubConfig) { + if ( + repository.isStarred && + config.githubConfig && + (config.githubConfig.starredReposMode || "dedicated-org") === "dedicated-org" + ) { // Extract GitHub owner from full_name (format: owner/repo) const githubOwner = repository.fullName.split('/')[0]; @@ -990,7 +1002,11 @@ export async function mirrorGitHubRepoToGiteaOrg({ // Determine the actual repository name to use (handle duplicates for starred repos) let targetRepoName = repository.name; - if (repository.isStarred && config.githubConfig) { + if ( + repository.isStarred && + config.githubConfig && + (config.githubConfig.starredReposMode || "dedicated-org") === "dedicated-org" + ) { // Extract GitHub owner from full_name (format: owner/repo) const githubOwner = repository.fullName.split('/')[0]; diff --git a/src/lib/starred-repos-handler.ts b/src/lib/starred-repos-handler.ts index 7341423..f17058b 100644 --- a/src/lib/starred-repos-handler.ts +++ b/src/lib/starred-repos-handler.ts @@ -92,8 +92,13 @@ async function preCreateOrganizations({ // Get unique organization names const orgNames = new Set(); - // Add starred repos org - if (config.githubConfig?.starredReposOrg) { + const starredReposMode = config.githubConfig?.starredReposMode || "dedicated-org"; + + if (starredReposMode === "preserve-owner") { + for (const repo of repositories) { + orgNames.add(repo.organization || repo.owner); + } + } else if (config.githubConfig?.starredReposOrg) { orgNames.add(config.githubConfig.starredReposOrg); } else { orgNames.add("starred"); @@ -129,7 +134,11 @@ async function processStarredRepository({ octokit: Octokit; strategyConfig: ReturnType; }): Promise { - const starredOrg = config.githubConfig?.starredReposOrg || "starred"; + const starredReposMode = config.githubConfig?.starredReposMode || "dedicated-org"; + const starredOrg = + starredReposMode === "preserve-owner" + ? repository.organization || repository.owner + : config.githubConfig?.starredReposOrg || "starred"; // Check if repository exists in Gitea const existingRepo = await getGiteaRepoInfo({ @@ -257,7 +266,11 @@ export async function syncStarredRepositories({ if (error instanceof Error && error.message.includes("not a mirror")) { console.warn(`Repository ${repository.name} is not a mirror, handling...`); - const starredOrg = config.githubConfig?.starredReposOrg || "starred"; + const starredReposMode = config.githubConfig?.starredReposMode || "dedicated-org"; + const starredOrg = + starredReposMode === "preserve-owner" + ? repository.organization || repository.owner + : config.githubConfig?.starredReposOrg || "starred"; const repoInfo = await getGiteaRepoInfo({ config, owner: starredOrg, @@ -287,4 +300,4 @@ export async function syncStarredRepositories({ }, } ); -} \ No newline at end of file +} diff --git a/src/lib/utils/config-defaults.ts b/src/lib/utils/config-defaults.ts index 43db60d..13fb6c4 100644 --- a/src/lib/utils/config-defaults.ts +++ b/src/lib/utils/config-defaults.ts @@ -71,6 +71,7 @@ export async function createDefaultConfig({ userId, envOverrides = {} }: Default includePublic: true, includeOrganizations: [], starredReposOrg: "starred", + starredReposMode: "dedicated-org", mirrorStrategy: "preserve", defaultOrg: "github-mirrors", }, diff --git a/src/lib/utils/config-mapper.ts b/src/lib/utils/config-mapper.ts index 7d02fdc..9be2594 100644 --- a/src/lib/utils/config-mapper.ts +++ b/src/lib/utils/config-mapper.ts @@ -48,6 +48,7 @@ export function mapUiToDbConfig( // Starred repos organization starredReposOrg: giteaConfig.starredReposOrg, + starredReposMode: giteaConfig.starredReposMode || "dedicated-org", // Mirror strategy mirrorStrategy: giteaConfig.mirrorStrategy || "preserve", @@ -131,6 +132,7 @@ export function mapDbToUiConfig(dbConfig: any): { organization: dbConfig.githubConfig?.defaultOrg || "github-mirrors", // Get from GitHub config visibility: dbConfig.giteaConfig?.visibility === "default" ? "public" : dbConfig.giteaConfig?.visibility || "public", starredReposOrg: dbConfig.githubConfig?.starredReposOrg || "starred", // Get from GitHub config + starredReposMode: dbConfig.githubConfig?.starredReposMode || "dedicated-org", // Get from GitHub config preserveOrgStructure: dbConfig.giteaConfig?.preserveVisibility || false, // Map preserveVisibility mirrorStrategy: dbConfig.githubConfig?.mirrorStrategy || "preserve", // Get from GitHub config personalReposOrg: undefined, // Not stored in current schema diff --git a/src/pages/api/job/mirror-repo.ts b/src/pages/api/job/mirror-repo.ts index af14932..185927f 100644 --- a/src/pages/api/job/mirror-repo.ts +++ b/src/pages/api/job/mirror-repo.ts @@ -108,15 +108,14 @@ export const POST: APIRoute = async ({ request }) => { console.log(`Repository ${repo.name} will be mirrored to owner: ${owner}`); - // For single-org and starred repos strategies, or when mirroring to an org, - // always use the org mirroring function to ensure proper organization handling + // For single-org strategy, or when mirroring to an org, + // use the org mirroring function to ensure proper organization handling const mirrorStrategy = config.githubConfig?.mirrorStrategy || - (config.githubConfig?.preserveOrgStructure ? "preserve" : "flat-user"); + (config.giteaConfig?.preserveOrgStructure ? "preserve" : "flat-user"); const shouldUseOrgMirror = owner !== config.giteaConfig?.defaultOwner || // Different owner means org - mirrorStrategy === "single-org" || // Single-org strategy always uses org - repoData.isStarred; // Starred repos always go to org + mirrorStrategy === "single-org"; // Single-org strategy always uses org if (shouldUseOrgMirror) { await mirrorGitHubOrgRepoToGiteaOrg({ @@ -222,4 +221,4 @@ export const POST: APIRoute = async ({ request }) => { return createSecureErrorResponse(error, "mirror-repo API", 500); } -}; \ No newline at end of file +}; diff --git a/src/pages/api/job/retry-repo.ts b/src/pages/api/job/retry-repo.ts index 81192e6..f6e698d 100644 --- a/src/pages/api/job/retry-repo.ts +++ b/src/pages/api/job/retry-repo.ts @@ -142,15 +142,14 @@ export const POST: APIRoute = async ({ request }) => { console.log(`Importing repo: ${repo.name} to owner: ${owner}`); - // For single-org and starred repos strategies, or when mirroring to an org, - // always use the org mirroring function to ensure proper organization handling + // For single-org strategy, or when mirroring to an org, + // use the org mirroring function to ensure proper organization handling const mirrorStrategy = config.githubConfig?.mirrorStrategy || (config.githubConfig?.preserveOrgStructure ? "preserve" : "flat-user"); const shouldUseOrgMirror = owner !== config.giteaConfig?.defaultOwner || // Different owner means org - mirrorStrategy === "single-org" || // Single-org strategy always uses org - repoData.isStarred; // Starred repos always go to org + mirrorStrategy === "single-org"; // Single-org strategy always uses org if (shouldUseOrgMirror) { await mirrorGitHubOrgRepoToGiteaOrg({ diff --git a/src/types/config.ts b/src/types/config.ts index 2db9f81..d49f6b3 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -2,6 +2,7 @@ import { type Config as ConfigType } from "@/lib/db/schema"; export type GiteaOrgVisibility = "public" | "private" | "limited"; export type MirrorStrategy = "preserve" | "single-org" | "flat-user" | "mixed"; +export type StarredReposMode = "dedicated-org" | "preserve-owner"; export interface GiteaConfig { url: string; @@ -10,6 +11,7 @@ export interface GiteaConfig { organization: string; visibility: GiteaOrgVisibility; starredReposOrg: string; + starredReposMode?: StarredReposMode; preserveOrgStructure: boolean; mirrorStrategy?: MirrorStrategy; // New field for the strategy personalReposOrg?: string; // Override destination for personal repos @@ -46,6 +48,7 @@ export interface GitHubConfig { privateRepositories: boolean; mirrorStarred: boolean; starredDuplicateStrategy?: DuplicateNameStrategy; + starredReposMode?: StarredReposMode; } export interface MirrorOptions {