mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2025-12-09 21:16:48 +03:00
updates
This commit is contained in:
@@ -57,7 +57,7 @@ export function GitHubMirrorSettings({
|
|||||||
onGitHubConfigChange({ ...githubConfig, [field]: value });
|
onGitHubConfigChange({ ...githubConfig, [field]: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMirrorChange = (field: keyof MirrorOptions, value: boolean) => {
|
const handleMirrorChange = (field: keyof MirrorOptions, value: boolean | number) => {
|
||||||
onMirrorOptionsChange({ ...mirrorOptions, [field]: value });
|
onMirrorOptionsChange({ ...mirrorOptions, [field]: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -313,6 +313,8 @@ export function GitHubMirrorSettings({
|
|||||||
onCheckedChange={(checked) => handleMirrorChange('mirrorReleases', !!checked)}
|
onCheckedChange={(checked) => handleMirrorChange('mirrorReleases', !!checked)}
|
||||||
/>
|
/>
|
||||||
<div className="space-y-0.5 flex-1">
|
<div className="space-y-0.5 flex-1">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex-1">
|
||||||
<Label
|
<Label
|
||||||
htmlFor="mirror-releases"
|
htmlFor="mirror-releases"
|
||||||
className="text-sm font-normal cursor-pointer flex items-center gap-2"
|
className="text-sm font-normal cursor-pointer flex items-center gap-2"
|
||||||
@@ -324,6 +326,29 @@ export function GitHubMirrorSettings({
|
|||||||
Include GitHub releases, tags, and associated assets
|
Include GitHub releases, tags, and associated assets
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
{mirrorOptions.mirrorReleases && (
|
||||||
|
<div className="flex items-center gap-2 ml-4">
|
||||||
|
<label htmlFor="release-limit" className="text-xs text-muted-foreground">
|
||||||
|
Latest
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="release-limit"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="100"
|
||||||
|
value={mirrorOptions.releaseLimit || 10}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = parseInt(e.target.value) || 10;
|
||||||
|
const clampedValue = Math.min(100, Math.max(1, value));
|
||||||
|
handleMirrorChange('releaseLimit', clampedValue);
|
||||||
|
}}
|
||||||
|
className="w-16 px-2 py-1 text-xs border border-input rounded bg-background text-foreground"
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-muted-foreground">releases</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-start space-x-3">
|
<div className="flex items-start space-x-3">
|
||||||
@@ -452,6 +477,31 @@ export function GitHubMirrorSettings({
|
|||||||
>
|
>
|
||||||
<GitPullRequest className="h-3.5 w-3.5 text-muted-foreground" />
|
<GitPullRequest className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
Pull Requests
|
Pull Requests
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Info className="h-3 w-3 text-muted-foreground" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="right" className="max-w-sm">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<p className="font-semibold">Pull Requests are mirrored as issues</p>
|
||||||
|
<p className="text-xs">
|
||||||
|
Due to Gitea API limitations, PRs cannot be created as actual pull requests.
|
||||||
|
Instead, they are mirrored as issues with:
|
||||||
|
</p>
|
||||||
|
<ul className="text-xs space-y-1 ml-3">
|
||||||
|
<li>• [PR #number] prefix in title</li>
|
||||||
|
<li>• Full PR description and metadata</li>
|
||||||
|
<li>• Commit history (up to 10 commits)</li>
|
||||||
|
<li>• File changes summary</li>
|
||||||
|
<li>• Diff preview (first 5 files)</li>
|
||||||
|
<li>• Review comments preserved</li>
|
||||||
|
<li>• Merge/close status tracking</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,282 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
||||||
import { Checkbox } from "../ui/checkbox";
|
|
||||||
import type { MirrorOptions } from "@/types/config";
|
|
||||||
import { RefreshCw, Info } from "lucide-react";
|
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger
|
|
||||||
} from "../ui/tooltip";
|
|
||||||
|
|
||||||
interface MirrorOptionsFormProps {
|
|
||||||
config: MirrorOptions;
|
|
||||||
setConfig: React.Dispatch<React.SetStateAction<MirrorOptions>>;
|
|
||||||
onAutoSave?: (config: MirrorOptions) => Promise<void>;
|
|
||||||
isAutoSaving?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MirrorOptionsForm({
|
|
||||||
config,
|
|
||||||
setConfig,
|
|
||||||
onAutoSave,
|
|
||||||
isAutoSaving = false,
|
|
||||||
}: MirrorOptionsFormProps) {
|
|
||||||
const handleChange = (name: string, checked: boolean) => {
|
|
||||||
let newConfig = { ...config };
|
|
||||||
|
|
||||||
if (name === "mirrorMetadata") {
|
|
||||||
newConfig.mirrorMetadata = checked;
|
|
||||||
// If disabling metadata, also disable all components
|
|
||||||
if (!checked) {
|
|
||||||
newConfig.metadataComponents = {
|
|
||||||
issues: false,
|
|
||||||
pullRequests: false, // Keep for backwards compatibility but not shown in UI
|
|
||||||
labels: false,
|
|
||||||
milestones: false,
|
|
||||||
wiki: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else if (name.startsWith("metadataComponents.")) {
|
|
||||||
const componentName = name.split(".")[1] as keyof typeof config.metadataComponents;
|
|
||||||
newConfig.metadataComponents = {
|
|
||||||
...config.metadataComponents,
|
|
||||||
[componentName]: checked,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
newConfig = {
|
|
||||||
...config,
|
|
||||||
[name]: checked,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
setConfig(newConfig);
|
|
||||||
|
|
||||||
// Auto-save
|
|
||||||
if (onAutoSave) {
|
|
||||||
onAutoSave(newConfig);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card className="self-start">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-lg font-semibold flex items-center justify-between">
|
|
||||||
Mirror Options
|
|
||||||
{isAutoSaving && (
|
|
||||||
<div className="flex items-center text-sm text-muted-foreground">
|
|
||||||
<RefreshCw className="h-3 w-3 animate-spin mr-1" />
|
|
||||||
<span className="text-xs">Auto-saving...</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-6">
|
|
||||||
{/* Repository Content */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<h4 className="text-sm font-medium text-foreground">Repository Content</h4>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="mirror-releases"
|
|
||||||
checked={config.mirrorReleases}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
handleChange("mirrorReleases", Boolean(checked))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="mirror-releases"
|
|
||||||
className="ml-2 text-sm select-none flex items-center"
|
|
||||||
>
|
|
||||||
Mirror releases
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<span className="ml-1 cursor-pointer text-muted-foreground">
|
|
||||||
<Info size={14} />
|
|
||||||
</span>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="right" className="max-w-xs text-xs">
|
|
||||||
Include GitHub releases and tags in the mirror
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="mirror-lfs"
|
|
||||||
checked={config.mirrorLFS}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
handleChange("mirrorLFS", Boolean(checked))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="mirror-lfs"
|
|
||||||
className="ml-2 text-sm select-none flex items-center"
|
|
||||||
>
|
|
||||||
Mirror LFS (Large File Storage)
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<span className="ml-1 cursor-pointer text-muted-foreground">
|
|
||||||
<Info size={14} />
|
|
||||||
</span>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="right" className="max-w-xs text-xs">
|
|
||||||
Mirror Git LFS objects. Requires LFS to be enabled on your Gitea server and Git v2.1.2+
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="mirror-metadata"
|
|
||||||
checked={config.mirrorMetadata}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
handleChange("mirrorMetadata", Boolean(checked))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="mirror-metadata"
|
|
||||||
className="ml-2 text-sm select-none flex items-center"
|
|
||||||
>
|
|
||||||
Mirror metadata
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<span className="ml-1 cursor-pointer text-muted-foreground">
|
|
||||||
<Info size={14} />
|
|
||||||
</span>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="right" className="max-w-xs text-xs">
|
|
||||||
Include issues, pull requests, labels, milestones, and wiki
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Metadata Components */}
|
|
||||||
{config.mirrorMetadata && (
|
|
||||||
<div className="ml-6 space-y-3 border-l-2 border-muted pl-4">
|
|
||||||
<h5 className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
|
||||||
Metadata Components
|
|
||||||
</h5>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-2">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="metadata-issues"
|
|
||||||
checked={config.metadataComponents.issues}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
handleChange("metadataComponents.issues", Boolean(checked))
|
|
||||||
}
|
|
||||||
disabled={!config.mirrorMetadata}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="metadata-issues"
|
|
||||||
className="ml-2 text-sm select-none"
|
|
||||||
>
|
|
||||||
Issues
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="metadata-pullRequests"
|
|
||||||
checked={config.metadataComponents.pullRequests}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
handleChange("metadataComponents.pullRequests", Boolean(checked))
|
|
||||||
}
|
|
||||||
disabled={!config.mirrorMetadata}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="metadata-pullRequests"
|
|
||||||
className="ml-2 text-sm select-none"
|
|
||||||
>
|
|
||||||
Pull Requests (as issues)
|
|
||||||
</label>
|
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger>
|
|
||||||
<Info className="h-3 w-3 ml-1 text-muted-foreground" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="right" className="max-w-sm">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<p className="font-semibold">Pull Requests are mirrored as issues</p>
|
|
||||||
<p className="text-xs">
|
|
||||||
Due to Gitea API limitations, PRs cannot be created as actual pull requests.
|
|
||||||
Instead, they are mirrored as issues with:
|
|
||||||
</p>
|
|
||||||
<ul className="text-xs space-y-1 ml-3">
|
|
||||||
<li>• [PR #number] prefix in title</li>
|
|
||||||
<li>• Full PR description and metadata</li>
|
|
||||||
<li>• Commit history (up to 10 commits)</li>
|
|
||||||
<li>• File changes summary</li>
|
|
||||||
<li>• Diff preview (first 5 files)</li>
|
|
||||||
<li>• Review comments preserved</li>
|
|
||||||
<li>• Merge/close status tracking</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="metadata-labels"
|
|
||||||
checked={config.metadataComponents.labels}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
handleChange("metadataComponents.labels", Boolean(checked))
|
|
||||||
}
|
|
||||||
disabled={!config.mirrorMetadata}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="metadata-labels"
|
|
||||||
className="ml-2 text-sm select-none"
|
|
||||||
>
|
|
||||||
Labels
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="metadata-milestones"
|
|
||||||
checked={config.metadataComponents.milestones}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
handleChange("metadataComponents.milestones", Boolean(checked))
|
|
||||||
}
|
|
||||||
disabled={!config.mirrorMetadata}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="metadata-milestones"
|
|
||||||
className="ml-2 text-sm select-none"
|
|
||||||
>
|
|
||||||
Milestones
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Checkbox
|
|
||||||
id="metadata-wiki"
|
|
||||||
checked={config.metadataComponents.wiki}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
handleChange("metadataComponents.wiki", Boolean(checked))
|
|
||||||
}
|
|
||||||
disabled={!config.mirrorMetadata}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="metadata-wiki"
|
|
||||||
className="ml-2 text-sm select-none"
|
|
||||||
>
|
|
||||||
Wiki
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -53,6 +53,7 @@ export const giteaConfigSchema = z.object({
|
|||||||
.default("reference"),
|
.default("reference"),
|
||||||
// Mirror options
|
// Mirror options
|
||||||
mirrorReleases: z.boolean().default(false),
|
mirrorReleases: z.boolean().default(false),
|
||||||
|
releaseLimit: z.number().default(10),
|
||||||
mirrorMetadata: z.boolean().default(false),
|
mirrorMetadata: z.boolean().default(false),
|
||||||
mirrorIssues: z.boolean().default(false),
|
mirrorIssues: z.boolean().default(false),
|
||||||
mirrorPullRequests: z.boolean().default(false),
|
mirrorPullRequests: z.boolean().default(false),
|
||||||
|
|||||||
@@ -1399,12 +1399,16 @@ export async function mirrorGitHubReleasesToGitea({
|
|||||||
throw new Error(`Repository ${repository.name} does not exist in Gitea at ${repoOwner}. Please ensure the repository is mirrored first.`);
|
throw new Error(`Repository ${repository.name} does not exist in Gitea at ${repoOwner}. Please ensure the repository is mirrored first.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get release limit from config (default to 10)
|
||||||
|
const releaseLimit = config.giteaConfig?.releaseLimit || 10;
|
||||||
|
|
||||||
const releases = await octokit.rest.repos.listReleases({
|
const releases = await octokit.rest.repos.listReleases({
|
||||||
owner: repository.owner,
|
owner: repository.owner,
|
||||||
repo: repository.name,
|
repo: repository.name,
|
||||||
|
per_page: releaseLimit, // Only fetch the latest N releases
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`[Releases] Found ${releases.data.length} releases to mirror for ${repository.fullName}`);
|
console.log(`[Releases] Found ${releases.data.length} releases (limited to latest ${releaseLimit}) to mirror for ${repository.fullName}`);
|
||||||
|
|
||||||
if (releases.data.length === 0) {
|
if (releases.data.length === 0) {
|
||||||
console.log(`[Releases] No releases to mirror for ${repository.fullName}`);
|
console.log(`[Releases] No releases to mirror for ${repository.fullName}`);
|
||||||
@@ -1414,7 +1418,12 @@ export async function mirrorGitHubReleasesToGitea({
|
|||||||
let mirroredCount = 0;
|
let mirroredCount = 0;
|
||||||
let skippedCount = 0;
|
let skippedCount = 0;
|
||||||
|
|
||||||
for (const release of releases.data) {
|
// Sort releases by created_at to ensure we get the most recent ones
|
||||||
|
const sortedReleases = releases.data.sort((a, b) =>
|
||||||
|
new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
|
||||||
|
).slice(0, releaseLimit);
|
||||||
|
|
||||||
|
for (const release of sortedReleases) {
|
||||||
try {
|
try {
|
||||||
// Check if release already exists
|
// Check if release already exists
|
||||||
const existingReleasesResponse = await httpGet(
|
const existingReleasesResponse = await httpGet(
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ export function mapUiToDbConfig(
|
|||||||
|
|
||||||
// Mirror options from UI
|
// Mirror options from UI
|
||||||
mirrorReleases: mirrorOptions.mirrorReleases,
|
mirrorReleases: mirrorOptions.mirrorReleases,
|
||||||
|
releaseLimit: mirrorOptions.releaseLimit || 10,
|
||||||
mirrorMetadata: mirrorOptions.mirrorMetadata,
|
mirrorMetadata: mirrorOptions.mirrorMetadata,
|
||||||
mirrorIssues: mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.issues,
|
mirrorIssues: mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.issues,
|
||||||
mirrorPullRequests: mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.pullRequests,
|
mirrorPullRequests: mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.pullRequests,
|
||||||
@@ -135,6 +136,7 @@ export function mapDbToUiConfig(dbConfig: any): {
|
|||||||
// Map mirror options from various database fields
|
// Map mirror options from various database fields
|
||||||
const mirrorOptions: MirrorOptions = {
|
const mirrorOptions: MirrorOptions = {
|
||||||
mirrorReleases: dbConfig.giteaConfig?.mirrorReleases || false,
|
mirrorReleases: dbConfig.giteaConfig?.mirrorReleases || false,
|
||||||
|
releaseLimit: dbConfig.giteaConfig?.releaseLimit || 10,
|
||||||
mirrorLFS: dbConfig.giteaConfig?.lfs || false,
|
mirrorLFS: dbConfig.giteaConfig?.lfs || false,
|
||||||
mirrorMetadata: dbConfig.giteaConfig?.mirrorMetadata || false,
|
mirrorMetadata: dbConfig.giteaConfig?.mirrorMetadata || false,
|
||||||
metadataComponents: {
|
metadataComponents: {
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export interface GitHubConfig {
|
|||||||
|
|
||||||
export interface MirrorOptions {
|
export interface MirrorOptions {
|
||||||
mirrorReleases: boolean;
|
mirrorReleases: boolean;
|
||||||
|
releaseLimit?: number; // Limit number of releases to mirror (default: 10)
|
||||||
mirrorLFS: boolean; // Mirror Git LFS objects
|
mirrorLFS: boolean; // Mirror Git LFS objects
|
||||||
mirrorMetadata: boolean;
|
mirrorMetadata: boolean;
|
||||||
metadataComponents: {
|
metadataComponents: {
|
||||||
|
|||||||
Reference in New Issue
Block a user