mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2026-04-04 20:18:24 +03:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1dd3dea231 | ||
|
|
db783c4225 | ||
|
|
8a4716bdbd | ||
|
|
9d37966c10 | ||
|
|
ac16ae56ea | ||
|
|
df3e665978 | ||
|
|
8a26764d2c |
4
.github/workflows/astro-build-test.yml
vendored
4
.github/workflows/astro-build-test.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
build-and-test:
|
build-and-test:
|
||||||
name: Build and Test Astro Project
|
name: Build and Test Astro Project
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 25
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v1
|
uses: oven-sh/setup-bun@v1
|
||||||
with:
|
with:
|
||||||
bun-version: '1.3.6'
|
bun-version: '1.3.10'
|
||||||
|
|
||||||
- name: Check lockfile and install dependencies
|
- name: Check lockfile and install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
4
.github/workflows/docker-build.yml
vendored
4
.github/workflows/docker-build.yml
vendored
@@ -36,7 +36,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 25
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -253,7 +253,7 @@ jobs:
|
|||||||
|
|
||||||
# Upload security scan results to GitHub Security tab
|
# Upload security scan results to GitHub Security tab
|
||||||
- name: Upload Docker Scout scan results to GitHub Security tab
|
- name: Upload Docker Scout scan results to GitHub Security tab
|
||||||
uses: github/codeql-action/upload-sarif@v3
|
uses: github/codeql-action/upload-sarif@v4
|
||||||
if: always()
|
if: always()
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
|
|||||||
4
.github/workflows/e2e-tests.yml
vendored
4
.github/workflows/e2e-tests.yml
vendored
@@ -40,13 +40,13 @@ env:
|
|||||||
FAKE_GITHUB_PORT: 4580
|
FAKE_GITHUB_PORT: 4580
|
||||||
GIT_SERVER_PORT: 4590
|
GIT_SERVER_PORT: 4590
|
||||||
APP_PORT: 4321
|
APP_PORT: 4321
|
||||||
BUN_VERSION: "1.3.6"
|
BUN_VERSION: "1.3.10"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
e2e-tests:
|
e2e-tests:
|
||||||
name: E2E Integration Tests
|
name: E2E Integration Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 25
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
|
|||||||
4
.github/workflows/helm-test.yml
vendored
4
.github/workflows/helm-test.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
yamllint:
|
yamllint:
|
||||||
name: Lint YAML
|
name: Lint YAML
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 25
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
helm-template:
|
helm-template:
|
||||||
name: Helm lint & template
|
name: Helm lint & template
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 25
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Helm
|
- name: Setup Helm
|
||||||
|
|||||||
22
.github/workflows/nix-build.yml
vendored
22
.github/workflows/nix-build.yml
vendored
@@ -5,18 +5,18 @@ on:
|
|||||||
branches: [main, nix]
|
branches: [main, nix]
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- 'v*'
|
||||||
paths-ignore:
|
paths:
|
||||||
- 'README.md'
|
- 'flake.nix'
|
||||||
- 'docs/**'
|
- 'flake.lock'
|
||||||
- 'www/**'
|
- 'bun.nix'
|
||||||
- 'helm/**'
|
- '.github/workflows/nix-build.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths-ignore:
|
paths:
|
||||||
- 'README.md'
|
- 'flake.nix'
|
||||||
- 'docs/**'
|
- 'flake.lock'
|
||||||
- 'www/**'
|
- 'bun.nix'
|
||||||
- 'helm/**'
|
- '.github/workflows/nix-build.yml'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -24,7 +24,7 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 45
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# syntax=docker/dockerfile:1.4
|
# syntax=docker/dockerfile:1.4
|
||||||
|
|
||||||
FROM oven/bun:1.3.9-debian AS base
|
FROM oven/bun:1.3.10-debian AS base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
python3 make g++ gcc wget sqlite3 openssl ca-certificates \
|
python3 make g++ gcc wget sqlite3 openssl ca-certificates \
|
||||||
@@ -26,7 +26,7 @@ COPY bun.lock* ./
|
|||||||
RUN bun install --production --omit=peer --frozen-lockfile
|
RUN bun install --production --omit=peer --frozen-lockfile
|
||||||
|
|
||||||
# ----------------------------
|
# ----------------------------
|
||||||
FROM oven/bun:1.3.9-debian AS runner
|
FROM oven/bun:1.3.10-debian AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
git git-lfs wget sqlite3 openssl ca-certificates \
|
git git-lfs wget sqlite3 openssl ca-certificates \
|
||||||
|
|||||||
@@ -139,16 +139,29 @@ fi
|
|||||||
|
|
||||||
# Initialize configuration from environment variables if provided
|
# Initialize configuration from environment variables if provided
|
||||||
echo "Checking for environment configuration..."
|
echo "Checking for environment configuration..."
|
||||||
if [ -f "dist/scripts/startup-env-config.js" ]; then
|
|
||||||
echo "Loading configuration from environment variables..."
|
# Only run the env config script if relevant env vars are set
|
||||||
bun dist/scripts/startup-env-config.js
|
# This avoids spawning a heavy Bun process on memory-constrained systems
|
||||||
ENV_CONFIG_EXIT_CODE=$?
|
HAS_ENV_CONFIG=false
|
||||||
elif [ -f "scripts/startup-env-config.ts" ]; then
|
if [ -n "$GITHUB_USERNAME" ] || [ -n "$GITHUB_TOKEN" ] || [ -n "$GITEA_URL" ] || [ -n "$GITEA_USERNAME" ] || [ -n "$GITEA_TOKEN" ]; then
|
||||||
echo "Loading configuration from environment variables..."
|
HAS_ENV_CONFIG=true
|
||||||
bun scripts/startup-env-config.ts
|
fi
|
||||||
ENV_CONFIG_EXIT_CODE=$?
|
|
||||||
|
if [ "$HAS_ENV_CONFIG" = "true" ]; then
|
||||||
|
if [ -f "dist/scripts/startup-env-config.js" ]; then
|
||||||
|
echo "Loading configuration from environment variables..."
|
||||||
|
bun dist/scripts/startup-env-config.js || ENV_CONFIG_EXIT_CODE=$?
|
||||||
|
ENV_CONFIG_EXIT_CODE=${ENV_CONFIG_EXIT_CODE:-0}
|
||||||
|
elif [ -f "scripts/startup-env-config.ts" ]; then
|
||||||
|
echo "Loading configuration from environment variables..."
|
||||||
|
bun scripts/startup-env-config.ts || ENV_CONFIG_EXIT_CODE=$?
|
||||||
|
ENV_CONFIG_EXIT_CODE=${ENV_CONFIG_EXIT_CODE:-0}
|
||||||
|
else
|
||||||
|
echo "Environment configuration script not found. Skipping."
|
||||||
|
ENV_CONFIG_EXIT_CODE=0
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "Environment configuration script not found. Skipping."
|
echo "No GitHub/Gitea environment variables found, skipping env config initialization."
|
||||||
ENV_CONFIG_EXIT_CODE=0
|
ENV_CONFIG_EXIT_CODE=0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -161,17 +174,15 @@ fi
|
|||||||
|
|
||||||
# Run startup recovery to handle any interrupted jobs
|
# Run startup recovery to handle any interrupted jobs
|
||||||
echo "Running startup recovery..."
|
echo "Running startup recovery..."
|
||||||
|
RECOVERY_EXIT_CODE=0
|
||||||
if [ -f "dist/scripts/startup-recovery.js" ]; then
|
if [ -f "dist/scripts/startup-recovery.js" ]; then
|
||||||
echo "Running startup recovery using compiled script..."
|
echo "Running startup recovery using compiled script..."
|
||||||
bun dist/scripts/startup-recovery.js --timeout=30000
|
bun dist/scripts/startup-recovery.js --timeout=30000 || RECOVERY_EXIT_CODE=$?
|
||||||
RECOVERY_EXIT_CODE=$?
|
|
||||||
elif [ -f "scripts/startup-recovery.ts" ]; then
|
elif [ -f "scripts/startup-recovery.ts" ]; then
|
||||||
echo "Running startup recovery using TypeScript script..."
|
echo "Running startup recovery using TypeScript script..."
|
||||||
bun scripts/startup-recovery.ts --timeout=30000
|
bun scripts/startup-recovery.ts --timeout=30000 || RECOVERY_EXIT_CODE=$?
|
||||||
RECOVERY_EXIT_CODE=$?
|
|
||||||
else
|
else
|
||||||
echo "Warning: Startup recovery script not found. Skipping recovery."
|
echo "Warning: Startup recovery script not found. Skipping recovery."
|
||||||
RECOVERY_EXIT_CODE=0
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Log recovery result
|
# Log recovery result
|
||||||
@@ -185,17 +196,15 @@ fi
|
|||||||
|
|
||||||
# Run repository status repair to fix any inconsistent mirroring states
|
# Run repository status repair to fix any inconsistent mirroring states
|
||||||
echo "Running repository status repair..."
|
echo "Running repository status repair..."
|
||||||
|
REPAIR_EXIT_CODE=0
|
||||||
if [ -f "dist/scripts/repair-mirrored-repos.js" ]; then
|
if [ -f "dist/scripts/repair-mirrored-repos.js" ]; then
|
||||||
echo "Running repository repair using compiled script..."
|
echo "Running repository repair using compiled script..."
|
||||||
bun dist/scripts/repair-mirrored-repos.js --startup
|
bun dist/scripts/repair-mirrored-repos.js --startup || REPAIR_EXIT_CODE=$?
|
||||||
REPAIR_EXIT_CODE=$?
|
|
||||||
elif [ -f "scripts/repair-mirrored-repos.ts" ]; then
|
elif [ -f "scripts/repair-mirrored-repos.ts" ]; then
|
||||||
echo "Running repository repair using TypeScript script..."
|
echo "Running repository repair using TypeScript script..."
|
||||||
bun scripts/repair-mirrored-repos.ts --startup
|
bun scripts/repair-mirrored-repos.ts --startup || REPAIR_EXIT_CODE=$?
|
||||||
REPAIR_EXIT_CODE=$?
|
|
||||||
else
|
else
|
||||||
echo "Warning: Repository repair script not found. Skipping repair."
|
echo "Warning: Repository repair script not found. Skipping repair."
|
||||||
REPAIR_EXIT_CODE=0
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Log repair result
|
# Log repair result
|
||||||
|
|||||||
14
flake.nix
14
flake.nix
@@ -49,6 +49,20 @@
|
|||||||
bunNix = ./bun.nix;
|
bunNix = ./bun.nix;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# bun2nix defaults to isolated installs on Linux, which can be
|
||||||
|
# very slow in CI for larger dependency trees and may appear stuck.
|
||||||
|
# Use hoisted linker and fail fast on lockfile drift.
|
||||||
|
bunInstallFlags = if pkgs.stdenv.hostPlatform.isDarwin then [
|
||||||
|
"--linker=hoisted"
|
||||||
|
"--backend=copyfile"
|
||||||
|
"--frozen-lockfile"
|
||||||
|
"--no-progress"
|
||||||
|
] else [
|
||||||
|
"--linker=hoisted"
|
||||||
|
"--frozen-lockfile"
|
||||||
|
"--no-progress"
|
||||||
|
];
|
||||||
|
|
||||||
# Let the bun2nix hook handle dependency installation via the
|
# Let the bun2nix hook handle dependency installation via the
|
||||||
# pre-fetched cache, but skip its default build/check/install
|
# pre-fetched cache, but skip its default build/check/install
|
||||||
# phases since we have custom ones.
|
# phases since we have custom ones.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "gitea-mirror",
|
"name": "gitea-mirror",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "3.12.1",
|
"version": "3.12.3",
|
||||||
"engines": {
|
"engines": {
|
||||||
"bun": ">=1.2.9"
|
"bun": ">=1.2.9"
|
||||||
},
|
},
|
||||||
@@ -119,5 +119,5 @@
|
|||||||
"tsx": "^4.21.0",
|
"tsx": "^4.21.0",
|
||||||
"vitest": "^4.0.18"
|
"vitest": "^4.0.18"
|
||||||
},
|
},
|
||||||
"packageManager": "bun@1.3.3"
|
"packageManager": "bun@1.3.10"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,10 +72,21 @@ mock.module("./gitea", () => {
|
|||||||
const mirrorStrategy =
|
const mirrorStrategy =
|
||||||
config?.githubConfig?.mirrorStrategy ||
|
config?.githubConfig?.mirrorStrategy ||
|
||||||
(config?.giteaConfig?.preserveOrgStructure ? "preserve" : "flat-user");
|
(config?.giteaConfig?.preserveOrgStructure ? "preserve" : "flat-user");
|
||||||
|
const configuredGitHubOwner =
|
||||||
|
(config?.githubConfig?.owner || config?.githubConfig?.username || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
|
const repoOwner = repository?.owner?.trim().toLowerCase();
|
||||||
|
|
||||||
switch (mirrorStrategy) {
|
switch (mirrorStrategy) {
|
||||||
case "preserve":
|
case "preserve":
|
||||||
return repository?.organization || config?.giteaConfig?.defaultOwner || "giteauser";
|
if (repository?.organization) {
|
||||||
|
return repository.organization;
|
||||||
|
}
|
||||||
|
if (configuredGitHubOwner && repoOwner && repoOwner !== configuredGitHubOwner) {
|
||||||
|
return repository.owner;
|
||||||
|
}
|
||||||
|
return config?.giteaConfig?.defaultOwner || "giteauser";
|
||||||
case "single-org":
|
case "single-org":
|
||||||
return config?.giteaConfig?.organization || config?.giteaConfig?.defaultOwner || "giteauser";
|
return config?.giteaConfig?.organization || config?.giteaConfig?.defaultOwner || "giteauser";
|
||||||
case "mixed":
|
case "mixed":
|
||||||
@@ -99,7 +110,7 @@ mock.module("./gitea", () => {
|
|||||||
return mockDbSelectResult[0].destinationOrg;
|
return mockDbSelectResult[0].destinationOrg;
|
||||||
}
|
}
|
||||||
|
|
||||||
return config?.giteaConfig?.defaultOwner || "giteauser";
|
return mockGetGiteaRepoOwner({ config, repository });
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
isRepoPresentInGitea: mockIsRepoPresentInGitea,
|
isRepoPresentInGitea: mockIsRepoPresentInGitea,
|
||||||
@@ -376,6 +387,7 @@ describe("Gitea Repository Mirroring", () => {
|
|||||||
describe("getGiteaRepoOwner - Organization Override Tests", () => {
|
describe("getGiteaRepoOwner - Organization Override Tests", () => {
|
||||||
const baseConfig: Partial<Config> = {
|
const baseConfig: Partial<Config> = {
|
||||||
githubConfig: {
|
githubConfig: {
|
||||||
|
owner: "testuser",
|
||||||
username: "testuser",
|
username: "testuser",
|
||||||
token: "token",
|
token: "token",
|
||||||
preserveOrgStructure: false,
|
preserveOrgStructure: false,
|
||||||
@@ -484,6 +496,18 @@ describe("getGiteaRepoOwner - Organization Override Tests", () => {
|
|||||||
expect(result).toBe("giteauser");
|
expect(result).toBe("giteauser");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("preserve strategy: personal repos owned by another user keep source owner namespace", () => {
|
||||||
|
const repo = {
|
||||||
|
...baseRepo,
|
||||||
|
owner: "nice-user",
|
||||||
|
fullName: "nice-user/test-repo",
|
||||||
|
organization: undefined,
|
||||||
|
isForked: true,
|
||||||
|
};
|
||||||
|
const result = getGiteaRepoOwner({ config: baseConfig, repository: repo });
|
||||||
|
expect(result).toBe("nice-user");
|
||||||
|
});
|
||||||
|
|
||||||
test("preserve strategy: org repos go to same org name", () => {
|
test("preserve strategy: org repos go to same org name", () => {
|
||||||
const repo = { ...baseRepo, organization: "myorg" };
|
const repo = { ...baseRepo, organization: "myorg" };
|
||||||
const result = getGiteaRepoOwner({ config: baseConfig, repository: repo });
|
const result = getGiteaRepoOwner({ config: baseConfig, repository: repo });
|
||||||
@@ -589,4 +613,26 @@ describe("getGiteaRepoOwner - Organization Override Tests", () => {
|
|||||||
|
|
||||||
expect(result).toBe("FOO");
|
expect(result).toBe("FOO");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("getGiteaRepoOwnerAsync preserves external personal owner for preserve strategy", async () => {
|
||||||
|
const configWithUser: Partial<Config> = {
|
||||||
|
...baseConfig,
|
||||||
|
userId: "user-id",
|
||||||
|
};
|
||||||
|
|
||||||
|
const repo = {
|
||||||
|
...baseRepo,
|
||||||
|
owner: "nice-user",
|
||||||
|
fullName: "nice-user/test-repo",
|
||||||
|
organization: undefined,
|
||||||
|
isForked: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await getGiteaRepoOwnerAsync({
|
||||||
|
config: configWithUser,
|
||||||
|
repository: repo,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe("nice-user");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -138,14 +138,35 @@ export const getGiteaRepoOwner = ({
|
|||||||
// Get the mirror strategy - use preserveOrgStructure for backward compatibility
|
// Get the mirror strategy - use preserveOrgStructure for backward compatibility
|
||||||
const mirrorStrategy = config.githubConfig.mirrorStrategy ||
|
const mirrorStrategy = config.githubConfig.mirrorStrategy ||
|
||||||
(config.giteaConfig.preserveOrgStructure ? "preserve" : "flat-user");
|
(config.giteaConfig.preserveOrgStructure ? "preserve" : "flat-user");
|
||||||
|
const configuredGitHubOwner =
|
||||||
|
(
|
||||||
|
config.githubConfig.owner ||
|
||||||
|
(config.githubConfig as typeof config.githubConfig & { username?: string }).username ||
|
||||||
|
""
|
||||||
|
)
|
||||||
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
switch (mirrorStrategy) {
|
switch (mirrorStrategy) {
|
||||||
case "preserve":
|
case "preserve":
|
||||||
// Keep GitHub structure - org repos go to same org, personal repos to user (or override)
|
// Keep GitHub structure:
|
||||||
|
// - org repos stay in the same org
|
||||||
|
// - personal repos owned by other users keep their source owner namespace
|
||||||
|
// - personal repos owned by the configured account go to defaultOwner
|
||||||
if (repository.organization) {
|
if (repository.organization) {
|
||||||
return repository.organization;
|
return repository.organization;
|
||||||
}
|
}
|
||||||
// Use personal repos override if configured, otherwise use username
|
|
||||||
|
const normalizedRepoOwner = repository.owner.trim().toLowerCase();
|
||||||
|
if (
|
||||||
|
normalizedRepoOwner &&
|
||||||
|
configuredGitHubOwner &&
|
||||||
|
normalizedRepoOwner !== configuredGitHubOwner
|
||||||
|
) {
|
||||||
|
return repository.owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Personal repos from the configured GitHub account go to the configured default owner
|
||||||
return config.giteaConfig.defaultOwner;
|
return config.giteaConfig.defaultOwner;
|
||||||
|
|
||||||
case "single-org":
|
case "single-org":
|
||||||
@@ -376,6 +397,23 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
|
|
||||||
// Get the correct owner based on the strategy (with organization overrides)
|
// Get the correct owner based on the strategy (with organization overrides)
|
||||||
let repoOwner = await getGiteaRepoOwnerAsync({ config, repository });
|
let repoOwner = await getGiteaRepoOwnerAsync({ config, repository });
|
||||||
|
const mirrorStrategy = config.githubConfig.mirrorStrategy ||
|
||||||
|
(config.giteaConfig.preserveOrgStructure ? "preserve" : "flat-user");
|
||||||
|
const configuredGitHubOwner = (
|
||||||
|
config.githubConfig.owner ||
|
||||||
|
(config.githubConfig as typeof config.githubConfig & { username?: string }).username ||
|
||||||
|
""
|
||||||
|
)
|
||||||
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
|
const normalizedRepoOwner = repository.owner.trim().toLowerCase();
|
||||||
|
const isExternalPersonalRepoInPreserveMode =
|
||||||
|
mirrorStrategy === "preserve" &&
|
||||||
|
!repository.organization &&
|
||||||
|
!repository.isStarred &&
|
||||||
|
normalizedRepoOwner !== "" &&
|
||||||
|
configuredGitHubOwner !== "" &&
|
||||||
|
normalizedRepoOwner !== configuredGitHubOwner;
|
||||||
|
|
||||||
// Determine the actual repository name to use (handle duplicates for starred repos)
|
// Determine the actual repository name to use (handle duplicates for starred repos)
|
||||||
let targetRepoName = repository.name;
|
let targetRepoName = repository.name;
|
||||||
@@ -520,6 +558,13 @@ export const mirrorGithubRepoToGitea = async ({
|
|||||||
(orgError.message.includes('Permission denied') ||
|
(orgError.message.includes('Permission denied') ||
|
||||||
orgError.message.includes('Authentication failed') ||
|
orgError.message.includes('Authentication failed') ||
|
||||||
orgError.message.includes('does not have permission'))) {
|
orgError.message.includes('does not have permission'))) {
|
||||||
|
if (isExternalPersonalRepoInPreserveMode) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot create/access namespace "${repoOwner}" for ${repository.fullName}. ` +
|
||||||
|
`Refusing fallback to "${config.giteaConfig.defaultOwner}" in preserve mode to avoid cross-owner overwrite.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
console.warn(`[Fallback] Organization creation/access failed. Attempting to mirror to user account instead.`);
|
console.warn(`[Fallback] Organization creation/access failed. Attempting to mirror to user account instead.`);
|
||||||
|
|
||||||
// Update the repository owner to use the user account
|
// Update the repository owner to use the user account
|
||||||
|
|||||||
Reference in New Issue
Block a user