mirror of
https://github.com/RayLabsHQ/gitea-mirror.git
synced 2025-12-06 03:26:44 +03:00
Merge pull request #105 from RayLabsHQ/fix/forgejo-12-private-repos
fix: Forgejo 12 compatibility - use separate auth fields for private repos (#102)
This commit is contained in:
67
.github/workflows/docker-build.yml
vendored
67
.github/workflows/docker-build.yml
vendored
@@ -48,7 +48,6 @@ jobs:
|
||||
|
||||
- name: Log into registry
|
||||
uses: docker/login-action@v3
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -89,6 +88,7 @@ jobs:
|
||||
type=sha,prefix=,suffix=,format=short
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
type=raw,value=${{ steps.tag_version.outputs.VERSION }}
|
||||
type=ref,event=pr,prefix=pr-
|
||||
|
||||
# Build and push Docker image
|
||||
- name: Build and push Docker image
|
||||
@@ -97,20 +97,77 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
load: ${{ github.event_name == 'pull_request' }}
|
||||
tags: ${{ github.event_name == 'pull_request' && 'gitea-mirror:scan' || steps.meta.outputs.tags }}
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
# Load image locally for security scanning (PRs only)
|
||||
- name: Load image for scanning
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64
|
||||
load: true
|
||||
tags: gitea-mirror:scan
|
||||
cache-from: type=gha
|
||||
|
||||
# Wait for image to be available in registry
|
||||
- name: Wait for image availability
|
||||
if: github.event_name != 'pull_request'
|
||||
run: |
|
||||
echo "Waiting for image to be available in registry..."
|
||||
sleep 5
|
||||
|
||||
# Add comment to PR with image details
|
||||
- name: Comment PR with image tag
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const imageTag = `pr-${prNumber}`;
|
||||
const imagePath = `${{ env.REGISTRY }}/${{ env.IMAGE }}:${imageTag}`.toLowerCase();
|
||||
|
||||
const comment = `## 🐳 Docker Image Built Successfully
|
||||
|
||||
Your PR image is available for testing:
|
||||
|
||||
**Image Tag:** \`${imageTag}\`
|
||||
**Full Image Path:** \`${imagePath}\`
|
||||
|
||||
### Pull and Test
|
||||
\`\`\`bash
|
||||
docker pull ${imagePath}
|
||||
docker run -d -p 3000:3000 --name gitea-mirror-test ${imagePath}
|
||||
\`\`\`
|
||||
|
||||
### Docker Compose Testing
|
||||
\`\`\`yaml
|
||||
services:
|
||||
gitea-mirror:
|
||||
image: ${imagePath}
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- BETTER_AUTH_SECRET=your-secret-here
|
||||
\`\`\`
|
||||
|
||||
> 💡 **Note:** PR images are tagged as \`pr-<number>\` and only built for \`linux/amd64\` to speed up CI.
|
||||
> Production images (\`latest\`, version tags) are multi-platform (\`linux/amd64\`, \`linux/arm64\`).
|
||||
|
||||
---
|
||||
📦 View in [GitHub Packages](https://github.com/${{ github.repository }}/pkgs/container/gitea-mirror)`;
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: prNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: comment
|
||||
});
|
||||
|
||||
# Docker Scout comprehensive security analysis
|
||||
- name: Docker Scout - Vulnerability Analysis & Recommendations
|
||||
uses: docker/scout-action@v1
|
||||
|
||||
125
src/lib/gitea.ts
125
src/lib/gitea.ts
@@ -356,21 +356,8 @@ export const mirrorGithubRepoToGitea = async ({
|
||||
status: "mirroring",
|
||||
});
|
||||
|
||||
let cloneAddress = repository.cloneUrl;
|
||||
|
||||
// If the repository is private, inject the GitHub token into the clone URL
|
||||
if (repository.isPrivate) {
|
||||
if (!config.githubConfig.token) {
|
||||
throw new Error(
|
||||
"GitHub token is required to mirror private repositories."
|
||||
);
|
||||
}
|
||||
|
||||
cloneAddress = repository.cloneUrl.replace(
|
||||
"https://",
|
||||
`https://${decryptedConfig.githubConfig.token}@`
|
||||
);
|
||||
}
|
||||
// Use clean clone URL without embedded credentials (Forgejo 12+ security requirement)
|
||||
const cloneAddress = repository.cloneUrl;
|
||||
|
||||
const apiUrl = `${config.giteaConfig.url}/api/v1/repos/migrate`;
|
||||
|
||||
@@ -384,17 +371,17 @@ export const mirrorGithubRepoToGitea = async ({
|
||||
});
|
||||
} catch (orgError) {
|
||||
console.error(`Failed to create/access organization ${repoOwner}: ${orgError instanceof Error ? orgError.message : String(orgError)}`);
|
||||
|
||||
|
||||
// Check if we should fallback to user account
|
||||
if (orgError instanceof Error &&
|
||||
(orgError.message.includes('Permission denied') ||
|
||||
if (orgError instanceof Error &&
|
||||
(orgError.message.includes('Permission denied') ||
|
||||
orgError.message.includes('Authentication failed') ||
|
||||
orgError.message.includes('does not have permission'))) {
|
||||
console.warn(`[Fallback] Organization creation/access failed. Attempting to mirror to user account instead.`);
|
||||
|
||||
|
||||
// Update the repository owner to use the user account
|
||||
repoOwner = config.giteaConfig.defaultOwner;
|
||||
|
||||
|
||||
// Log this fallback in the database
|
||||
await db
|
||||
.update(repositories)
|
||||
@@ -420,7 +407,7 @@ export const mirrorGithubRepoToGitea = async ({
|
||||
|
||||
if (existingRepo && !existingRepo.mirror) {
|
||||
console.log(`Repository ${targetRepoName} exists but is not a mirror. Handling...`);
|
||||
|
||||
|
||||
// Handle the existing non-mirror repository
|
||||
await handleExistingNonMirrorRepo({
|
||||
config,
|
||||
@@ -428,25 +415,42 @@ export const mirrorGithubRepoToGitea = async ({
|
||||
repoInfo: existingRepo,
|
||||
strategy: "delete", // Can be configured: "skip", "delete", or "rename"
|
||||
});
|
||||
|
||||
|
||||
// After handling, proceed with mirror creation
|
||||
console.log(`Proceeding with mirror creation for ${targetRepoName}`);
|
||||
}
|
||||
|
||||
// Prepare migration payload
|
||||
// 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
|
||||
const migratePayload: any = {
|
||||
clone_addr: cloneAddress,
|
||||
repo_name: targetRepoName,
|
||||
mirror: true,
|
||||
mirror_interval: config.giteaConfig?.mirrorInterval || "8h",
|
||||
wiki: config.giteaConfig?.wiki || false,
|
||||
lfs: config.giteaConfig?.lfs || false,
|
||||
private: repository.isPrivate,
|
||||
repo_owner: repoOwner,
|
||||
description: "",
|
||||
service: "git",
|
||||
};
|
||||
|
||||
// Add authentication for private repositories
|
||||
if (repository.isPrivate) {
|
||||
if (!config.githubConfig.token) {
|
||||
throw new Error(
|
||||
"GitHub token is required to mirror private repositories."
|
||||
);
|
||||
}
|
||||
// Use separate auth fields (required for Forgejo 12+ compatibility)
|
||||
migratePayload.auth_username = "oauth2"; // GitHub tokens work with any username
|
||||
migratePayload.auth_token = decryptedConfig.githubConfig.token;
|
||||
}
|
||||
|
||||
const response = await httpPost(
|
||||
apiUrl,
|
||||
{
|
||||
clone_addr: cloneAddress,
|
||||
repo_name: targetRepoName,
|
||||
mirror: true,
|
||||
mirror_interval: config.giteaConfig?.mirrorInterval || "8h", // Set mirror interval
|
||||
wiki: config.giteaConfig?.wiki || false, // will mirror wiki if it exists
|
||||
lfs: config.giteaConfig?.lfs || false, // Enable LFS mirroring if configured
|
||||
private: repository.isPrivate,
|
||||
repo_owner: repoOwner,
|
||||
description: "",
|
||||
service: "git",
|
||||
},
|
||||
migratePayload,
|
||||
{
|
||||
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
|
||||
}
|
||||
@@ -800,20 +804,8 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
||||
`Mirroring repository ${repository.fullName} to organization ${orgName} as ${targetRepoName}`
|
||||
);
|
||||
|
||||
let cloneAddress = repository.cloneUrl;
|
||||
|
||||
if (repository.isPrivate) {
|
||||
if (!config.githubConfig?.token) {
|
||||
throw new Error(
|
||||
"GitHub token is required to mirror private repositories."
|
||||
);
|
||||
}
|
||||
|
||||
cloneAddress = repository.cloneUrl.replace(
|
||||
"https://",
|
||||
`https://${decryptedConfig.githubConfig.token}@`
|
||||
);
|
||||
}
|
||||
// Use clean clone URL without embedded credentials (Forgejo 12+ security requirement)
|
||||
const cloneAddress = repository.cloneUrl;
|
||||
|
||||
// Mark repos as "mirroring" in DB
|
||||
await db
|
||||
@@ -829,18 +821,35 @@ export async function mirrorGitHubRepoToGiteaOrg({
|
||||
|
||||
const apiUrl = `${config.giteaConfig.url}/api/v1/repos/migrate`;
|
||||
|
||||
// Prepare migration payload
|
||||
// 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
|
||||
const migratePayload: any = {
|
||||
clone_addr: cloneAddress,
|
||||
uid: giteaOrgId,
|
||||
repo_name: targetRepoName,
|
||||
mirror: true,
|
||||
mirror_interval: config.giteaConfig?.mirrorInterval || "8h",
|
||||
wiki: config.giteaConfig?.wiki || false,
|
||||
lfs: config.giteaConfig?.lfs || false,
|
||||
private: repository.isPrivate,
|
||||
};
|
||||
|
||||
// Add authentication for private repositories
|
||||
if (repository.isPrivate) {
|
||||
if (!config.githubConfig?.token) {
|
||||
throw new Error(
|
||||
"GitHub token is required to mirror private repositories."
|
||||
);
|
||||
}
|
||||
// Use separate auth fields (required for Forgejo 12+ compatibility)
|
||||
migratePayload.auth_username = "oauth2"; // GitHub tokens work with any username
|
||||
migratePayload.auth_token = decryptedConfig.githubConfig.token;
|
||||
}
|
||||
|
||||
const migrateRes = await httpPost(
|
||||
apiUrl,
|
||||
{
|
||||
clone_addr: cloneAddress,
|
||||
uid: giteaOrgId,
|
||||
repo_name: targetRepoName,
|
||||
mirror: true,
|
||||
mirror_interval: config.giteaConfig?.mirrorInterval || "8h", // Set mirror interval
|
||||
wiki: config.giteaConfig?.wiki || false, // will mirror wiki if it exists
|
||||
lfs: config.giteaConfig?.lfs || false, // Enable LFS mirroring if configured
|
||||
private: repository.isPrivate,
|
||||
},
|
||||
migratePayload,
|
||||
{
|
||||
Authorization: `token ${decryptedConfig.giteaConfig.token}`,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user