Allow starred repos to be mirrored preserving structure

This commit is contained in:
Tobeas Arren
2026-02-14 13:08:41 +01:00
parent 2496d6f6e0
commit f4d391b240
18 changed files with 237 additions and 72 deletions

View File

@@ -47,6 +47,7 @@ export function ConfigTabs() {
organization: 'github-mirrors',
visibility: 'public',
starredReposOrg: 'starred',
starredReposMode: 'dedicated-org',
preserveOrgStructure: false,
},
scheduleConfig: {

View File

@@ -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);

View File

@@ -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<OrganizationConfigurationProps>
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 (
<div className="space-y-4">
<div>
@@ -49,38 +57,83 @@ export const OrganizationConfiguration: React.FC<OrganizationConfigurationProps>
</h4>
</div>
{/* First row - Organization inputs with consistent layout */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Left column - always shows starred repos org */}
<div className="space-y-1">
<Label htmlFor="starredReposOrg" className="text-sm font-normal flex items-center gap-2">
<Star className="h-3.5 w-3.5" />
Starred Repos Organization
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Info className="h-3.5 w-3.5 text-muted-foreground" />
</TooltipTrigger>
<TooltipContent>
<p>Starred repositories will be organized separately in this organization</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</Label>
<Input
id="starredReposOrg"
value={starredReposOrg || ""}
onChange={(e) => onStarredReposOrgChange(e.target.value)}
placeholder="starred"
className=""
/>
<p className="text-xs text-muted-foreground mt-1">
Keep starred repos organized separately
</p>
<div className="space-y-2">
<Label className="text-sm font-normal flex items-center gap-2">
Starred Repository Destination
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Info className="h-3.5 w-3.5 text-muted-foreground" />
</TooltipTrigger>
<TooltipContent>
<p>Choose whether starred repos use one org or keep their source owner paths</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</Label>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
<button
type="button"
onClick={() => onStarredReposModeChange("dedicated-org")}
className={cn(
"text-left px-3 py-2 rounded-md border text-sm transition-colors",
activeStarredMode === "dedicated-org"
? "bg-accent border-accent-foreground/20"
: "bg-background hover:bg-accent/50 border-input"
)}
>
Dedicated organization
</button>
<button
type="button"
onClick={() => onStarredReposModeChange("preserve-owner")}
className={cn(
"text-left px-3 py-2 rounded-md border text-sm transition-colors",
activeStarredMode === "preserve-owner"
? "bg-accent border-accent-foreground/20"
: "bg-background hover:bg-accent/50 border-input"
)}
>
Preserve source owner/org
</button>
</div>
</div>
{/* 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) && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{showStarredReposOrgInput ? (
<div className="space-y-1">
<Label htmlFor="starredReposOrg" className="text-sm font-normal flex items-center gap-2">
<Star className="h-3.5 w-3.5" />
Starred Repos Organization
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Info className="h-3.5 w-3.5 text-muted-foreground" />
</TooltipTrigger>
<TooltipContent>
<p>Starred repositories will be organized separately in this organization</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</Label>
<Input
id="starredReposOrg"
value={starredReposOrg || ""}
onChange={(e) => onStarredReposOrgChange(e.target.value)}
placeholder="starred"
className=""
/>
<p className="text-xs text-muted-foreground mt-1">
Keep starred repos organized separately
</p>
</div>
) : (
<div className="hidden md:block" />
)}
{showDestinationOrgInput ? (
<div className="space-y-1">
<Label htmlFor="destinationOrg" className="text-sm font-normal flex items-center gap-2">
{strategy === "mixed" ? "Personal Repos Organization" : "Destination Organization"}
@@ -114,10 +167,11 @@ export const OrganizationConfiguration: React.FC<OrganizationConfigurationProps>
}
</p>
</div>
) : (
<div className="hidden md:block" />
)}
</div>
) : (
<div className="hidden md:block" />
)}
</div>
)}
{/* Second row - Organization Visibility (always shown) */}
<div className="space-y-2">
@@ -172,4 +226,3 @@ export const OrganizationConfiguration: React.FC<OrganizationConfigurationProps>
</div>
);
};

View File

@@ -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 || "<username>";
const displayGiteaUsername = giteaUsername || "<username>";
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<{
</div>
<div className={cn("flex items-center gap-2 p-1.5 rounded text-xs", config.repoColors.bg)}>
<Building2 className={cn("h-3 w-3", config.repoColors.icon)} />
<span>{starredReposOrg || "starred"}/starred-repo</span>
<span>{starredDestination}</span>
</div>
</div>
</div>
@@ -168,7 +175,7 @@ const MappingPreview: React.FC<{
</div>
<div className={cn("flex items-center gap-2 p-1.5 rounded text-xs", config.repoColors.bg)}>
<Building2 className={cn("h-3 w-3", config.repoColors.icon)} />
<span>{starredReposOrg || "starred"}/starred-repo</span>
<span>{starredDestination}</span>
</div>
</div>
</div>
@@ -214,7 +221,7 @@ const MappingPreview: React.FC<{
</div>
<div className={cn("flex items-center gap-2 p-1.5 rounded text-xs", config.repoColors.bg)}>
<Building2 className={cn("h-3 w-3", config.repoColors.icon)} />
<span>{starredReposOrg || "starred"}/starred-repo</span>
<span>{starredDestination}</span>
</div>
</div>
</div>
@@ -260,7 +267,7 @@ const MappingPreview: React.FC<{
</div>
<div className={cn("flex items-center gap-2 p-1.5 rounded text-xs", config.repoColors.bg)}>
<Building2 className={cn("h-3 w-3", config.repoColors.icon)} />
<span>{starredReposOrg || "starred"}/starred-repo</span>
<span>{starredDestination}</span>
</div>
</div>
</div>
@@ -275,6 +282,7 @@ export const OrganizationStrategy: React.FC<OrganizationStrategyProps> = ({
strategy,
destinationOrg,
starredReposOrg,
starredReposMode,
onStrategyChange,
githubUsername,
giteaUsername,
@@ -339,7 +347,7 @@ export const OrganizationStrategy: React.FC<OrganizationStrategyProps> = ({
<span className="text-xs font-medium">Starred Repositories</span>
</div>
<p className="text-xs text-muted-foreground pl-5">
Always go to the configured starred repos organization and cannot be overridden.
Follow your starred-repo mode and cannot be overridden per repository.
</p>
</div>
</div>
@@ -415,6 +423,7 @@ export const OrganizationStrategy: React.FC<OrganizationStrategyProps> = ({
config={config}
destinationOrg={destinationOrg}
starredReposOrg={starredReposOrg}
starredReposMode={starredReposMode}
githubUsername={githubUsername}
giteaUsername={giteaUsername}
/>
@@ -434,4 +443,4 @@ export const OrganizationStrategy: React.FC<OrganizationStrategyProps> = ({
</RadioGroup>
</div>
);
};
};