mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2025-12-09 04:56:45 +03:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e9d54b517 | ||
|
|
7a04665b70 | ||
|
|
3a3ff314e0 | ||
|
|
fed74ee901 | ||
|
|
85ea502276 | ||
|
|
ffb7bd3cb0 | ||
|
|
b39d7a2179 | ||
|
|
bf99a95dc6 | ||
|
|
2ea917fdaa |
2
.github/ci/values-ci.yaml
vendored
2
.github/ci/values-ci.yaml
vendored
@@ -37,7 +37,7 @@ gitea-mirror:
|
|||||||
type: "personal"
|
type: "personal"
|
||||||
privateRepositories: true
|
privateRepositories: true
|
||||||
skipForks: false
|
skipForks: false
|
||||||
skipStarredIssues: false
|
starredCodeOnly: false
|
||||||
gitea:
|
gitea:
|
||||||
url: "https://gitea.example.com"
|
url: "https://gitea.example.com"
|
||||||
token: "not-used-in-template"
|
token: "not-used-in-template"
|
||||||
|
|||||||
8
.github/workflows/docker-build.yml
vendored
8
.github/workflows/docker-build.yml
vendored
@@ -10,6 +10,10 @@ on:
|
|||||||
- 'package.json'
|
- 'package.json'
|
||||||
- 'bun.lock*'
|
- 'bun.lock*'
|
||||||
- '.github/workflows/docker-build.yml'
|
- '.github/workflows/docker-build.yml'
|
||||||
|
- 'docker-entrypoint.sh'
|
||||||
|
- 'drizzle/**'
|
||||||
|
- 'scripts/**'
|
||||||
|
- 'src/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'Dockerfile'
|
- 'Dockerfile'
|
||||||
@@ -17,6 +21,10 @@ on:
|
|||||||
- 'package.json'
|
- 'package.json'
|
||||||
- 'bun.lock*'
|
- 'bun.lock*'
|
||||||
- '.github/workflows/docker-build.yml'
|
- '.github/workflows/docker-build.yml'
|
||||||
|
- 'docker-entrypoint.sh'
|
||||||
|
- 'drizzle/**'
|
||||||
|
- 'scripts/**'
|
||||||
|
- 'src/**'
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 0' # Weekly security scan on Sunday at midnight
|
- cron: '0 0 * * 0' # Weekly security scan on Sunday at midnight
|
||||||
|
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ export async function POST({ request }: APIContext) {
|
|||||||
|
|
||||||
### Advanced Options (UI Fields)
|
### Advanced Options (UI Fields)
|
||||||
- **skipForks**: Skip forked repositories (default: false)
|
- **skipForks**: Skip forked repositories (default: false)
|
||||||
- **skipStarredIssues**: Skip issues for starred repositories (default: false) - enables "Lightweight mode" for starred repos
|
- **starredCodeOnly**: Skip issues for starred repositories (default: false) - enables "Lightweight mode" for starred repos
|
||||||
|
|
||||||
### Repository Statuses
|
### Repository Statuses
|
||||||
Repositories can have the following statuses:
|
Repositories can have the following statuses:
|
||||||
|
|||||||
@@ -120,161 +120,13 @@ fi
|
|||||||
# Dependencies are already installed during the Docker build process
|
# Dependencies are already installed during the Docker build process
|
||||||
|
|
||||||
# Initialize the database if it doesn't exist
|
# Initialize the database if it doesn't exist
|
||||||
|
# Note: Drizzle migrations will be run automatically when the app starts (see src/lib/db/index.ts)
|
||||||
if [ ! -f "/app/data/gitea-mirror.db" ]; then
|
if [ ! -f "/app/data/gitea-mirror.db" ]; then
|
||||||
echo "Initializing database..."
|
echo "Database not found. It will be created and initialized via Drizzle migrations on first app startup..."
|
||||||
if [ -f "dist/scripts/init-db.js" ]; then
|
# Create empty database file so migrations can run
|
||||||
bun dist/scripts/init-db.js
|
touch /app/data/gitea-mirror.db
|
||||||
elif [ -f "dist/scripts/manage-db.js" ]; then
|
|
||||||
bun dist/scripts/manage-db.js init
|
|
||||||
elif [ -f "scripts/manage-db.ts" ]; then
|
|
||||||
bun scripts/manage-db.ts init
|
|
||||||
else
|
|
||||||
echo "Warning: Could not find database initialization scripts in dist/scripts."
|
|
||||||
echo "Creating and initializing database manually..."
|
|
||||||
|
|
||||||
# Create the database file
|
|
||||||
touch /app/data/gitea-mirror.db
|
|
||||||
|
|
||||||
# Initialize the database with required tables
|
|
||||||
sqlite3 /app/data/gitea-mirror.db <<EOF
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
username TEXT NOT NULL,
|
|
||||||
password TEXT NOT NULL,
|
|
||||||
email TEXT NOT NULL,
|
|
||||||
created_at INTEGER NOT NULL,
|
|
||||||
updated_at INTEGER NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS configs (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
user_id TEXT NOT NULL,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
is_active INTEGER NOT NULL DEFAULT 1,
|
|
||||||
github_config TEXT NOT NULL,
|
|
||||||
gitea_config TEXT NOT NULL,
|
|
||||||
include TEXT NOT NULL DEFAULT '["*"]',
|
|
||||||
exclude TEXT NOT NULL DEFAULT '[]',
|
|
||||||
schedule_config TEXT NOT NULL,
|
|
||||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
|
|
||||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS repositories (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
user_id TEXT NOT NULL,
|
|
||||||
config_id TEXT NOT NULL,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
full_name TEXT NOT NULL,
|
|
||||||
url TEXT NOT NULL,
|
|
||||||
clone_url TEXT NOT NULL,
|
|
||||||
owner TEXT NOT NULL,
|
|
||||||
organization TEXT,
|
|
||||||
mirrored_location TEXT DEFAULT '',
|
|
||||||
destination_org TEXT,
|
|
||||||
is_private INTEGER NOT NULL DEFAULT 0,
|
|
||||||
is_fork INTEGER NOT NULL DEFAULT 0,
|
|
||||||
forked_from TEXT,
|
|
||||||
has_issues INTEGER NOT NULL DEFAULT 0,
|
|
||||||
is_starred INTEGER NOT NULL DEFAULT 0,
|
|
||||||
is_archived INTEGER NOT NULL DEFAULT 0,
|
|
||||||
size INTEGER NOT NULL DEFAULT 0,
|
|
||||||
has_lfs INTEGER NOT NULL DEFAULT 0,
|
|
||||||
has_submodules INTEGER NOT NULL DEFAULT 0,
|
|
||||||
language TEXT,
|
|
||||||
description TEXT,
|
|
||||||
default_branch TEXT NOT NULL,
|
|
||||||
visibility TEXT NOT NULL DEFAULT 'public',
|
|
||||||
status TEXT NOT NULL DEFAULT 'imported',
|
|
||||||
last_mirrored INTEGER,
|
|
||||||
error_message TEXT,
|
|
||||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
|
|
||||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id),
|
|
||||||
FOREIGN KEY (config_id) REFERENCES configs(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Uniqueness of (user_id, full_name) for repositories is enforced via drizzle migrations
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS organizations (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
user_id TEXT NOT NULL,
|
|
||||||
config_id TEXT NOT NULL,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
avatar_url TEXT NOT NULL,
|
|
||||||
membership_role TEXT NOT NULL DEFAULT 'member',
|
|
||||||
is_included INTEGER NOT NULL DEFAULT 1,
|
|
||||||
status TEXT NOT NULL DEFAULT 'imported',
|
|
||||||
last_mirrored INTEGER,
|
|
||||||
error_message TEXT,
|
|
||||||
repository_count INTEGER NOT NULL DEFAULT 0,
|
|
||||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
|
|
||||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id),
|
|
||||||
FOREIGN KEY (config_id) REFERENCES configs(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS mirror_jobs (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
user_id TEXT NOT NULL,
|
|
||||||
repository_id TEXT,
|
|
||||||
repository_name TEXT,
|
|
||||||
organization_id TEXT,
|
|
||||||
organization_name TEXT,
|
|
||||||
details TEXT,
|
|
||||||
status TEXT NOT NULL DEFAULT 'imported',
|
|
||||||
message TEXT NOT NULL,
|
|
||||||
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
-- New fields for job resilience
|
|
||||||
job_type TEXT NOT NULL DEFAULT 'mirror',
|
|
||||||
batch_id TEXT,
|
|
||||||
total_items INTEGER,
|
|
||||||
completed_items INTEGER DEFAULT 0,
|
|
||||||
item_ids TEXT, -- JSON array as text
|
|
||||||
completed_item_ids TEXT DEFAULT '[]', -- JSON array as text
|
|
||||||
in_progress INTEGER NOT NULL DEFAULT 0, -- Boolean as integer
|
|
||||||
started_at TIMESTAMP,
|
|
||||||
completed_at TIMESTAMP,
|
|
||||||
last_checkpoint TIMESTAMP,
|
|
||||||
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_mirror_jobs_user_id ON mirror_jobs(user_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_mirror_jobs_batch_id ON mirror_jobs(batch_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_mirror_jobs_in_progress ON mirror_jobs(in_progress);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_mirror_jobs_job_type ON mirror_jobs(job_type);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_mirror_jobs_timestamp ON mirror_jobs(timestamp);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS events (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
user_id TEXT NOT NULL,
|
|
||||||
channel TEXT NOT NULL,
|
|
||||||
payload TEXT NOT NULL,
|
|
||||||
read INTEGER NOT NULL DEFAULT 0,
|
|
||||||
created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_events_user_channel ON events(user_id, channel);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_events_created_at ON events(created_at);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_events_read ON events(read);
|
|
||||||
EOF
|
|
||||||
echo "Database initialized with required tables."
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo "Database already exists, checking for issues..."
|
echo "Database already exists, Drizzle will check for pending migrations on startup..."
|
||||||
if [ -f "dist/scripts/fix-db-issues.js" ]; then
|
|
||||||
bun dist/scripts/fix-db-issues.js
|
|
||||||
elif [ -f "dist/scripts/manage-db.js" ]; then
|
|
||||||
bun dist/scripts/manage-db.js fix
|
|
||||||
elif [ -f "scripts/manage-db.ts" ]; then
|
|
||||||
bun scripts/manage-db.ts fix
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Database exists, checking integrity..."
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extract version from package.json and set as environment variable
|
# Extract version from package.json and set as environment variable
|
||||||
|
|||||||
@@ -1 +1,11 @@
|
|||||||
CREATE UNIQUE INDEX `uniq_repositories_user_full_name` ON `repositories` (`user_id`,`full_name`);
|
-- Step 1: Remove duplicate repositories, keeping the most recently updated one
|
||||||
|
-- This handles cases where users have duplicate entries from before the unique constraint
|
||||||
|
DELETE FROM repositories
|
||||||
|
WHERE rowid NOT IN (
|
||||||
|
SELECT MAX(rowid)
|
||||||
|
FROM repositories
|
||||||
|
GROUP BY user_id, full_name
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
-- Step 2: Now create the unique index safely
|
||||||
|
CREATE UNIQUE INDEX uniq_repositories_user_full_name ON repositories (user_id, full_name);
|
||||||
@@ -175,7 +175,7 @@ These values populate a **ConfigMap** (non-secret) and a **Secret** (for tokens
|
|||||||
| `gitea-mirror.github.type` | `personal` | `GITHUB_TYPE` |
|
| `gitea-mirror.github.type` | `personal` | `GITHUB_TYPE` |
|
||||||
| `gitea-mirror.github.privateRepositories` | `true` | `PRIVATE_REPOSITORIES` |
|
| `gitea-mirror.github.privateRepositories` | `true` | `PRIVATE_REPOSITORIES` |
|
||||||
| `gitea-mirror.github.skipForks` | `false` | `SKIP_FORKS` |
|
| `gitea-mirror.github.skipForks` | `false` | `SKIP_FORKS` |
|
||||||
| `gitea-mirror.github.skipStarredIssues` | `false` | `SKIP_STARRED_ISSUES` |
|
| `gitea-mirror.github.starredCodeOnly` | `false` | `SKIP_STARRED_ISSUES` |
|
||||||
| `gitea-mirror.github.mirrorStarred` | `false` | `MIRROR_STARRED` |
|
| `gitea-mirror.github.mirrorStarred` | `false` | `MIRROR_STARRED` |
|
||||||
|
|
||||||
### Gitea
|
### Gitea
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ data:
|
|||||||
PRIVATE_REPOSITORIES: {{ $gm.github.privateRepositories | quote }}
|
PRIVATE_REPOSITORIES: {{ $gm.github.privateRepositories | quote }}
|
||||||
MIRROR_STARRED: {{ $gm.github.mirrorStarred | quote }}
|
MIRROR_STARRED: {{ $gm.github.mirrorStarred | quote }}
|
||||||
SKIP_FORKS: {{ $gm.github.skipForks | quote }}
|
SKIP_FORKS: {{ $gm.github.skipForks | quote }}
|
||||||
SKIP_STARRED_ISSUES: {{ $gm.github.skipStarredIssues | quote }}
|
SKIP_STARRED_ISSUES: {{ $gm.github.starredCodeOnly | quote }}
|
||||||
# Gitea Config
|
# Gitea Config
|
||||||
GITEA_URL: {{ $gm.gitea.url | quote }}
|
GITEA_URL: {{ $gm.gitea.url | quote }}
|
||||||
GITEA_USERNAME: {{ $gm.gitea.username | quote }}
|
GITEA_USERNAME: {{ $gm.gitea.username | quote }}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ gitea-mirror:
|
|||||||
privateRepositories: true
|
privateRepositories: true
|
||||||
mirrorStarred: false
|
mirrorStarred: false
|
||||||
skipForks: false
|
skipForks: false
|
||||||
skipStarredIssues: false
|
starredCodeOnly: false
|
||||||
|
|
||||||
gitea:
|
gitea:
|
||||||
url: ""
|
url: ""
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "gitea-mirror",
|
"name": "gitea-mirror",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "3.7.1",
|
"version": "3.8.3",
|
||||||
"engines": {
|
"engines": {
|
||||||
"bun": ">=1.2.9"
|
"bun": ">=1.2.9"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -67,21 +67,21 @@ export function AdvancedOptionsForm({
|
|||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="skip-starred-issues"
|
id="starred-code-only"
|
||||||
checked={config.skipStarredIssues}
|
checked={config.starredCodeOnly}
|
||||||
onCheckedChange={(checked) =>
|
onCheckedChange={(checked) =>
|
||||||
handleChange("skipStarredIssues", Boolean(checked))
|
handleChange("starredCodeOnly", Boolean(checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="skip-starred-issues"
|
htmlFor="starred-code-only"
|
||||||
className="ml-2 text-sm select-none"
|
className="ml-2 text-sm select-none"
|
||||||
>
|
>
|
||||||
Don't fetch issues for starred repos
|
Code-only mode for starred repos
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground ml-6">
|
<p className="text-xs text-muted-foreground ml-6">
|
||||||
Skip mirroring issues and pull requests for starred repositories
|
Mirror only source code for starred repositories, skipping all metadata (issues, PRs, labels, milestones, wiki, releases)
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export function ConfigTabs() {
|
|||||||
},
|
},
|
||||||
advancedOptions: {
|
advancedOptions: {
|
||||||
skipForks: false,
|
skipForks: false,
|
||||||
skipStarredIssues: false,
|
starredCodeOnly: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
|||||||
@@ -89,10 +89,10 @@ export function GitHubMirrorSettings({
|
|||||||
// Calculate what content is included for starred repos
|
// Calculate what content is included for starred repos
|
||||||
const starredRepoContent = {
|
const starredRepoContent = {
|
||||||
code: true, // Always included
|
code: true, // Always included
|
||||||
releases: !advancedOptions.skipStarredIssues && mirrorOptions.mirrorReleases,
|
releases: !advancedOptions.starredCodeOnly && mirrorOptions.mirrorReleases,
|
||||||
issues: !advancedOptions.skipStarredIssues && mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.issues,
|
issues: !advancedOptions.starredCodeOnly && mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.issues,
|
||||||
pullRequests: !advancedOptions.skipStarredIssues && mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.pullRequests,
|
pullRequests: !advancedOptions.starredCodeOnly && mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.pullRequests,
|
||||||
wiki: !advancedOptions.skipStarredIssues && mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.wiki,
|
wiki: !advancedOptions.starredCodeOnly && mirrorOptions.mirrorMetadata && mirrorOptions.metadataComponents.wiki,
|
||||||
};
|
};
|
||||||
|
|
||||||
const starredContentCount = Object.entries(starredRepoContent).filter(([key, value]) => key !== 'code' && value).length;
|
const starredContentCount = Object.entries(starredRepoContent).filter(([key, value]) => key !== 'code' && value).length;
|
||||||
@@ -168,7 +168,7 @@ export function GitHubMirrorSettings({
|
|||||||
className="h-8 text-xs font-normal min-w-[140px] md:min-w-[140px] justify-between"
|
className="h-8 text-xs font-normal min-w-[140px] md:min-w-[140px] justify-between"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{advancedOptions.skipStarredIssues ? (
|
{advancedOptions.starredCodeOnly ? (
|
||||||
"Code only"
|
"Code only"
|
||||||
) : starredContentCount === 0 ? (
|
) : starredContentCount === 0 ? (
|
||||||
"Code only"
|
"Code only"
|
||||||
@@ -206,8 +206,8 @@ export function GitHubMirrorSettings({
|
|||||||
<div className="flex items-center space-x-3 py-1 px-1 rounded hover:bg-accent">
|
<div className="flex items-center space-x-3 py-1 px-1 rounded hover:bg-accent">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="starred-lightweight"
|
id="starred-lightweight"
|
||||||
checked={advancedOptions.skipStarredIssues}
|
checked={advancedOptions.starredCodeOnly}
|
||||||
onCheckedChange={(checked) => handleAdvancedChange('skipStarredIssues', !!checked)}
|
onCheckedChange={(checked) => handleAdvancedChange('starredCodeOnly', !!checked)}
|
||||||
/>
|
/>
|
||||||
<Label
|
<Label
|
||||||
htmlFor="starred-lightweight"
|
htmlFor="starred-lightweight"
|
||||||
@@ -222,7 +222,7 @@ export function GitHubMirrorSettings({
|
|||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!advancedOptions.skipStarredIssues && (
|
{!advancedOptions.starredCodeOnly && (
|
||||||
<>
|
<>
|
||||||
<Separator className="my-2" />
|
<Separator className="my-2" />
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ export const githubConfigSchema = z.object({
|
|||||||
starredReposOrg: z.string().optional(),
|
starredReposOrg: z.string().optional(),
|
||||||
mirrorStrategy: z.enum(["preserve", "single-org", "flat-user", "mixed"]).default("preserve"),
|
mirrorStrategy: z.enum(["preserve", "single-org", "flat-user", "mixed"]).default("preserve"),
|
||||||
defaultOrg: z.string().optional(),
|
defaultOrg: z.string().optional(),
|
||||||
skipStarredIssues: z.boolean().default(false),
|
starredCodeOnly: z.boolean().default(false),
|
||||||
|
skipStarredIssues: z.boolean().optional(), // Deprecated: kept for backward compatibility, use starredCodeOnly instead
|
||||||
starredDuplicateStrategy: z.enum(["suffix", "prefix", "owner-org"]).default("suffix").optional(),
|
starredDuplicateStrategy: z.enum(["suffix", "prefix", "owner-org"]).default("suffix").optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ interface EnvConfig {
|
|||||||
mirrorOrganizations?: boolean;
|
mirrorOrganizations?: boolean;
|
||||||
preserveOrgStructure?: boolean;
|
preserveOrgStructure?: boolean;
|
||||||
onlyMirrorOrgs?: boolean;
|
onlyMirrorOrgs?: boolean;
|
||||||
skipStarredIssues?: boolean;
|
starredCodeOnly?: boolean;
|
||||||
starredReposOrg?: string;
|
starredReposOrg?: string;
|
||||||
mirrorStrategy?: 'preserve' | 'single-org' | 'flat-user' | 'mixed';
|
mirrorStrategy?: 'preserve' | 'single-org' | 'flat-user' | 'mixed';
|
||||||
};
|
};
|
||||||
@@ -107,7 +107,7 @@ function parseEnvConfig(): EnvConfig {
|
|||||||
mirrorOrganizations: process.env.MIRROR_ORGANIZATIONS === 'true',
|
mirrorOrganizations: process.env.MIRROR_ORGANIZATIONS === 'true',
|
||||||
preserveOrgStructure: process.env.PRESERVE_ORG_STRUCTURE === 'true',
|
preserveOrgStructure: process.env.PRESERVE_ORG_STRUCTURE === 'true',
|
||||||
onlyMirrorOrgs: process.env.ONLY_MIRROR_ORGS === 'true',
|
onlyMirrorOrgs: process.env.ONLY_MIRROR_ORGS === 'true',
|
||||||
skipStarredIssues: process.env.SKIP_STARRED_ISSUES === 'true',
|
starredCodeOnly: process.env.SKIP_STARRED_ISSUES === 'true',
|
||||||
starredReposOrg: process.env.STARRED_REPOS_ORG,
|
starredReposOrg: process.env.STARRED_REPOS_ORG,
|
||||||
mirrorStrategy: process.env.MIRROR_STRATEGY as 'preserve' | 'single-org' | 'flat-user' | 'mixed',
|
mirrorStrategy: process.env.MIRROR_STRATEGY as 'preserve' | 'single-org' | 'flat-user' | 'mixed',
|
||||||
},
|
},
|
||||||
@@ -253,7 +253,7 @@ export async function initializeConfigFromEnv(): Promise<void> {
|
|||||||
starredReposOrg: envConfig.github.starredReposOrg || existingConfig?.[0]?.githubConfig?.starredReposOrg || 'starred',
|
starredReposOrg: envConfig.github.starredReposOrg || existingConfig?.[0]?.githubConfig?.starredReposOrg || 'starred',
|
||||||
mirrorStrategy,
|
mirrorStrategy,
|
||||||
defaultOrg: envConfig.gitea.organization || existingConfig?.[0]?.githubConfig?.defaultOrg || 'github-mirrors',
|
defaultOrg: envConfig.gitea.organization || existingConfig?.[0]?.githubConfig?.defaultOrg || 'github-mirrors',
|
||||||
skipStarredIssues: envConfig.github.skipStarredIssues ?? existingConfig?.[0]?.githubConfig?.skipStarredIssues ?? false,
|
starredCodeOnly: envConfig.github.starredCodeOnly ?? existingConfig?.[0]?.githubConfig?.starredCodeOnly ?? false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build Gitea config
|
// Build Gitea config
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ describe("getGiteaRepoOwner - Organization Override Tests", () => {
|
|||||||
excludeOrgs: [],
|
excludeOrgs: [],
|
||||||
mirrorPublicOrgs: false,
|
mirrorPublicOrgs: false,
|
||||||
publicOrgs: [],
|
publicOrgs: [],
|
||||||
skipStarredIssues: false,
|
starredCodeOnly: false,
|
||||||
mirrorStrategy: "preserve"
|
mirrorStrategy: "preserve"
|
||||||
},
|
},
|
||||||
giteaConfig: {
|
giteaConfig: {
|
||||||
|
|||||||
@@ -423,12 +423,16 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
// Prepare migration payload
|
// Prepare migration payload
|
||||||
// For private repos, use separate auth fields instead of embedding credentials in URL
|
// For private repos, use separate auth fields instead of embedding credentials in URL
|
||||||
// This is required for Forgejo 12+ which rejects URLs with embedded credentials
|
// This is required for Forgejo 12+ which rejects URLs with embedded credentials
|
||||||
|
// Skip wiki for starred repos if starredCodeOnly is enabled
|
||||||
|
const shouldMirrorWiki = config.giteaConfig?.wiki &&
|
||||||
|
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
||||||
|
|
||||||
const migratePayload: any = {
|
const migratePayload: any = {
|
||||||
clone_addr: cloneAddress,
|
clone_addr: cloneAddress,
|
||||||
repo_name: targetRepoName,
|
repo_name: targetRepoName,
|
||||||
mirror: true,
|
mirror: true,
|
||||||
mirror_interval: config.giteaConfig?.mirrorInterval || "8h",
|
mirror_interval: config.giteaConfig?.mirrorInterval || "8h",
|
||||||
wiki: config.giteaConfig?.wiki || false,
|
wiki: shouldMirrorWiki || false,
|
||||||
lfs: config.giteaConfig?.lfs || false,
|
lfs: config.giteaConfig?.lfs || false,
|
||||||
private: repository.isPrivate,
|
private: repository.isPrivate,
|
||||||
repo_owner: repoOwner,
|
repo_owner: repoOwner,
|
||||||
@@ -457,8 +461,13 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
//mirror releases
|
//mirror releases
|
||||||
console.log(`[Metadata] Release mirroring check: mirrorReleases=${config.giteaConfig?.mirrorReleases}`);
|
// Skip releases for starred repos if starredCodeOnly is enabled
|
||||||
if (config.giteaConfig?.mirrorReleases) {
|
const shouldMirrorReleases = config.giteaConfig?.mirrorReleases &&
|
||||||
|
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
||||||
|
|
||||||
|
console.log(`[Metadata] Release mirroring check: mirrorReleases=${config.giteaConfig?.mirrorReleases}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorReleases=${shouldMirrorReleases}`);
|
||||||
|
|
||||||
|
if (shouldMirrorReleases) {
|
||||||
try {
|
try {
|
||||||
await mirrorGitHubReleasesToGitea({
|
await mirrorGitHubReleasesToGitea({
|
||||||
config,
|
config,
|
||||||
@@ -475,11 +484,11 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// clone issues
|
// clone issues
|
||||||
// Skip issues for starred repos if skipStarredIssues is enabled
|
// Skip issues for starred repos if starredCodeOnly is enabled
|
||||||
const shouldMirrorIssues = config.giteaConfig?.mirrorIssues &&
|
const shouldMirrorIssues = config.giteaConfig?.mirrorIssues &&
|
||||||
!(repository.isStarred && config.githubConfig?.skipStarredIssues);
|
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
||||||
|
|
||||||
console.log(`[Metadata] Issue mirroring check: mirrorIssues=${config.giteaConfig?.mirrorIssues}, isStarred=${repository.isStarred}, skipStarredIssues=${config.githubConfig?.skipStarredIssues}, shouldMirrorIssues=${shouldMirrorIssues}`);
|
console.log(`[Metadata] Issue mirroring check: mirrorIssues=${config.giteaConfig?.mirrorIssues}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorIssues=${shouldMirrorIssues}`);
|
||||||
|
|
||||||
if (shouldMirrorIssues) {
|
if (shouldMirrorIssues) {
|
||||||
try {
|
try {
|
||||||
@@ -498,8 +507,13 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mirror pull requests if enabled
|
// Mirror pull requests if enabled
|
||||||
console.log(`[Metadata] Pull request mirroring check: mirrorPullRequests=${config.giteaConfig?.mirrorPullRequests}`);
|
// Skip pull requests for starred repos if starredCodeOnly is enabled
|
||||||
if (config.giteaConfig?.mirrorPullRequests) {
|
const shouldMirrorPullRequests = config.giteaConfig?.mirrorPullRequests &&
|
||||||
|
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
||||||
|
|
||||||
|
console.log(`[Metadata] Pull request mirroring check: mirrorPullRequests=${config.giteaConfig?.mirrorPullRequests}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorPullRequests=${shouldMirrorPullRequests}`);
|
||||||
|
|
||||||
|
if (shouldMirrorPullRequests) {
|
||||||
try {
|
try {
|
||||||
await mirrorGitRepoPullRequestsToGitea({
|
await mirrorGitRepoPullRequestsToGitea({
|
||||||
config,
|
config,
|
||||||
@@ -516,8 +530,13 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mirror labels if enabled (and not already done via issues)
|
// Mirror labels if enabled (and not already done via issues)
|
||||||
console.log(`[Metadata] Label mirroring check: mirrorLabels=${config.giteaConfig?.mirrorLabels}, shouldMirrorIssues=${shouldMirrorIssues}`);
|
// Skip labels for starred repos if starredCodeOnly is enabled
|
||||||
if (config.giteaConfig?.mirrorLabels && !shouldMirrorIssues) {
|
const shouldMirrorLabels = config.giteaConfig?.mirrorLabels && !shouldMirrorIssues &&
|
||||||
|
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
||||||
|
|
||||||
|
console.log(`[Metadata] Label mirroring check: mirrorLabels=${config.giteaConfig?.mirrorLabels}, shouldMirrorIssues=${shouldMirrorIssues}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorLabels=${shouldMirrorLabels}`);
|
||||||
|
|
||||||
|
if (shouldMirrorLabels) {
|
||||||
try {
|
try {
|
||||||
await mirrorGitRepoLabelsToGitea({
|
await mirrorGitRepoLabelsToGitea({
|
||||||
config,
|
config,
|
||||||
@@ -534,8 +553,13 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mirror milestones if enabled
|
// Mirror milestones if enabled
|
||||||
console.log(`[Metadata] Milestone mirroring check: mirrorMilestones=${config.giteaConfig?.mirrorMilestones}`);
|
// Skip milestones for starred repos if starredCodeOnly is enabled
|
||||||
if (config.giteaConfig?.mirrorMilestones) {
|
const shouldMirrorMilestones = config.giteaConfig?.mirrorMilestones &&
|
||||||
|
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
||||||
|
|
||||||
|
console.log(`[Metadata] Milestone mirroring check: mirrorMilestones=${config.giteaConfig?.mirrorMilestones}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorMilestones=${shouldMirrorMilestones}`);
|
||||||
|
|
||||||
|
if (shouldMirrorMilestones) {
|
||||||
try {
|
try {
|
||||||
await mirrorGitRepoMilestonesToGitea({
|
await mirrorGitRepoMilestonesToGitea({
|
||||||
config,
|
config,
|
||||||
@@ -824,13 +848,17 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
// Prepare migration payload
|
// Prepare migration payload
|
||||||
// For private repos, use separate auth fields instead of embedding credentials in URL
|
// For private repos, use separate auth fields instead of embedding credentials in URL
|
||||||
// This is required for Forgejo 12+ which rejects URLs with embedded credentials
|
// This is required for Forgejo 12+ which rejects URLs with embedded credentials
|
||||||
|
// Skip wiki for starred repos if starredCodeOnly is enabled
|
||||||
|
const shouldMirrorWiki = config.giteaConfig?.wiki &&
|
||||||
|
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
||||||
|
|
||||||
const migratePayload: any = {
|
const migratePayload: any = {
|
||||||
clone_addr: cloneAddress,
|
clone_addr: cloneAddress,
|
||||||
uid: giteaOrgId,
|
uid: giteaOrgId,
|
||||||
repo_name: targetRepoName,
|
repo_name: targetRepoName,
|
||||||
mirror: true,
|
mirror: true,
|
||||||
mirror_interval: config.giteaConfig?.mirrorInterval || "8h",
|
mirror_interval: config.giteaConfig?.mirrorInterval || "8h",
|
||||||
wiki: config.giteaConfig?.wiki || false,
|
wiki: shouldMirrorWiki || false,
|
||||||
lfs: config.giteaConfig?.lfs || false,
|
lfs: config.giteaConfig?.lfs || false,
|
||||||
private: repository.isPrivate,
|
private: repository.isPrivate,
|
||||||
};
|
};
|
||||||
@@ -856,8 +884,13 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
);
|
);
|
||||||
|
|
||||||
//mirror releases
|
//mirror releases
|
||||||
console.log(`[Metadata] Release mirroring check: mirrorReleases=${config.giteaConfig?.mirrorReleases}`);
|
// Skip releases for starred repos if starredCodeOnly is enabled
|
||||||
if (config.giteaConfig?.mirrorReleases) {
|
const shouldMirrorReleases = config.giteaConfig?.mirrorReleases &&
|
||||||
|
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
||||||
|
|
||||||
|
console.log(`[Metadata] Release mirroring check: mirrorReleases=${config.giteaConfig?.mirrorReleases}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorReleases=${shouldMirrorReleases}`);
|
||||||
|
|
||||||
|
if (shouldMirrorReleases) {
|
||||||
try {
|
try {
|
||||||
await mirrorGitHubReleasesToGitea({
|
await mirrorGitHubReleasesToGitea({
|
||||||
config,
|
config,
|
||||||
@@ -874,11 +907,11 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clone issues
|
// Clone issues
|
||||||
// Skip issues for starred repos if skipStarredIssues is enabled
|
// Skip issues for starred repos if starredCodeOnly is enabled
|
||||||
const shouldMirrorIssues = config.giteaConfig?.mirrorIssues &&
|
const shouldMirrorIssues = config.giteaConfig?.mirrorIssues &&
|
||||||
!(repository.isStarred && config.githubConfig?.skipStarredIssues);
|
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
||||||
|
|
||||||
console.log(`[Metadata] Issue mirroring check: mirrorIssues=${config.giteaConfig?.mirrorIssues}, isStarred=${repository.isStarred}, skipStarredIssues=${config.githubConfig?.skipStarredIssues}, shouldMirrorIssues=${shouldMirrorIssues}`);
|
console.log(`[Metadata] Issue mirroring check: mirrorIssues=${config.giteaConfig?.mirrorIssues}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorIssues=${shouldMirrorIssues}`);
|
||||||
|
|
||||||
if (shouldMirrorIssues) {
|
if (shouldMirrorIssues) {
|
||||||
try {
|
try {
|
||||||
@@ -897,8 +930,13 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mirror pull requests if enabled
|
// Mirror pull requests if enabled
|
||||||
console.log(`[Metadata] Pull request mirroring check: mirrorPullRequests=${config.giteaConfig?.mirrorPullRequests}`);
|
// Skip pull requests for starred repos if starredCodeOnly is enabled
|
||||||
if (config.giteaConfig?.mirrorPullRequests) {
|
const shouldMirrorPullRequests = config.giteaConfig?.mirrorPullRequests &&
|
||||||
|
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
||||||
|
|
||||||
|
console.log(`[Metadata] Pull request mirroring check: mirrorPullRequests=${config.giteaConfig?.mirrorPullRequests}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorPullRequests=${shouldMirrorPullRequests}`);
|
||||||
|
|
||||||
|
if (shouldMirrorPullRequests) {
|
||||||
try {
|
try {
|
||||||
await mirrorGitRepoPullRequestsToGitea({
|
await mirrorGitRepoPullRequestsToGitea({
|
||||||
config,
|
config,
|
||||||
@@ -915,8 +953,13 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mirror labels if enabled (and not already done via issues)
|
// Mirror labels if enabled (and not already done via issues)
|
||||||
console.log(`[Metadata] Label mirroring check: mirrorLabels=${config.giteaConfig?.mirrorLabels}, shouldMirrorIssues=${shouldMirrorIssues}`);
|
// Skip labels for starred repos if starredCodeOnly is enabled
|
||||||
if (config.giteaConfig?.mirrorLabels && !shouldMirrorIssues) {
|
const shouldMirrorLabels = config.giteaConfig?.mirrorLabels && !shouldMirrorIssues &&
|
||||||
|
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
||||||
|
|
||||||
|
console.log(`[Metadata] Label mirroring check: mirrorLabels=${config.giteaConfig?.mirrorLabels}, shouldMirrorIssues=${shouldMirrorIssues}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorLabels=${shouldMirrorLabels}`);
|
||||||
|
|
||||||
|
if (shouldMirrorLabels) {
|
||||||
try {
|
try {
|
||||||
await mirrorGitRepoLabelsToGitea({
|
await mirrorGitRepoLabelsToGitea({
|
||||||
config,
|
config,
|
||||||
@@ -933,8 +976,13 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mirror milestones if enabled
|
// Mirror milestones if enabled
|
||||||
console.log(`[Metadata] Milestone mirroring check: mirrorMilestones=${config.giteaConfig?.mirrorMilestones}`);
|
// Skip milestones for starred repos if starredCodeOnly is enabled
|
||||||
if (config.giteaConfig?.mirrorMilestones) {
|
const shouldMirrorMilestones = config.giteaConfig?.mirrorMilestones &&
|
||||||
|
!(repository.isStarred && config.githubConfig?.starredCodeOnly);
|
||||||
|
|
||||||
|
console.log(`[Metadata] Milestone mirroring check: mirrorMilestones=${config.giteaConfig?.mirrorMilestones}, isStarred=${repository.isStarred}, starredCodeOnly=${config.githubConfig?.starredCodeOnly}, shouldMirrorMilestones=${shouldMirrorMilestones}`);
|
||||||
|
|
||||||
|
if (shouldMirrorMilestones) {
|
||||||
try {
|
try {
|
||||||
await mirrorGitRepoMilestonesToGitea({
|
await mirrorGitRepoMilestonesToGitea({
|
||||||
config,
|
config,
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export function mapUiToDbConfig(
|
|||||||
defaultOrg: giteaConfig.organization,
|
defaultOrg: giteaConfig.organization,
|
||||||
|
|
||||||
// Advanced options
|
// Advanced options
|
||||||
skipStarredIssues: advancedOptions.skipStarredIssues,
|
starredCodeOnly: advancedOptions.starredCodeOnly,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map Gitea config to match database schema
|
// Map Gitea config to match database schema
|
||||||
@@ -152,7 +152,8 @@ export function mapDbToUiConfig(dbConfig: any): {
|
|||||||
// Map advanced options
|
// Map advanced options
|
||||||
const advancedOptions: AdvancedOptions = {
|
const advancedOptions: AdvancedOptions = {
|
||||||
skipForks: !(dbConfig.githubConfig?.includeForks ?? true), // Invert includeForks to get skipForks
|
skipForks: !(dbConfig.githubConfig?.includeForks ?? true), // Invert includeForks to get skipForks
|
||||||
skipStarredIssues: dbConfig.githubConfig?.skipStarredIssues || false,
|
// Support both old (skipStarredIssues) and new (starredCodeOnly) field names for backward compatibility
|
||||||
|
starredCodeOnly: dbConfig.githubConfig?.starredCodeOnly ?? (dbConfig.githubConfig as any)?.skipStarredIssues ?? false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export interface MirrorOptions {
|
|||||||
|
|
||||||
export interface AdvancedOptions {
|
export interface AdvancedOptions {
|
||||||
skipForks: boolean;
|
skipForks: boolean;
|
||||||
skipStarredIssues: boolean;
|
starredCodeOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SaveConfigApiRequest {
|
export interface SaveConfigApiRequest {
|
||||||
|
|||||||
Reference in New Issue
Block a user