diff --git a/README.md b/README.md index 33bf5c8..120d850 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ All configuration is performed through environment variables. Flags are consider | MIRROR_ISSUES | no | bool | FALSE | If set to `true` the issues of your GitHub repositories will be mirrored to Gitea. Requires `GITHUB_TOKEN`. | | MIRROR_STARRED | no | bool | FALSE | If set to `true` repositories you've starred on GitHub will be mirrored to Gitea. Requires `GITHUB_TOKEN`. | | MIRROR_ORGANIZATIONS | no | bool | FALSE | If set to `true` repositories from organizations you belong to will be mirrored to Gitea. Requires `GITHUB_TOKEN`. | +| USE_SPECIFIC_USER | no | bool | FALSE | If set to `true`, the tool will use public API endpoints to fetch starred repositories and organizations for the specified `GITHUB_USERNAME` instead of the authenticated user. | | INCLUDE_ORGS | no | string | "" | Comma-separated list of GitHub organization names to include when mirroring organizations. If not specified, all organizations will be included. | | EXCLUDE_ORGS | no | string | "" | Comma-separated list of GitHub organization names to exclude when mirroring organizations. Takes precedence over `INCLUDE_ORGS`. | | PRESERVE_ORG_STRUCTURE | no | bool | FALSE | If set to `true`, each GitHub organization will be mirrored to a Gitea organization with the same name. If the organization doesn't exist, it will be created. | diff --git a/debug.sh b/debug.sh index e79dc70..2f199de 100755 --- a/debug.sh +++ b/debug.sh @@ -31,7 +31,7 @@ else fi echo -e "\nTesting GitHub organization access:" -echo "Method 1 - Using /user/orgs endpoint:" +echo "Method 1 - Using /user/orgs endpoint (authenticated user):" ORG_RESPONSE=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/user/orgs") ORG_COUNT=$(echo "$ORG_RESPONSE" | jq '. | length') if [ "$ORG_COUNT" -eq 0 ]; then @@ -41,7 +41,17 @@ else echo "$ORG_RESPONSE" | jq '.[].login' fi -echo "Method 2 - Looking for specific organizations:" +echo -e "\nMethod 2 - Using /users/{username}/orgs endpoint (specific user):" +PUBLIC_USER_ORGS=$(curl -s -H "Authorization: token $GITHUB_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28" "https://api.github.com/users/$GITHUB_USERNAME/orgs") +PUBLIC_ORG_COUNT=$(echo "$PUBLIC_USER_ORGS" | jq '. | length') +if [ "$PUBLIC_ORG_COUNT" -eq 0 ]; then + echo "No public organizations found for $GITHUB_USERNAME via /users/{username}/orgs endpoint." +else + echo "Public organizations found for $GITHUB_USERNAME:" + echo "$PUBLIC_USER_ORGS" | jq '.[].login' +fi + +echo "Method 3 - Looking for specific organizations:" for org in "Gameplex-labs" "Neucruit" "uiastra"; do ORG_DETAILS=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/orgs/$org") if [[ $(echo "$ORG_DETAILS" | jq 'has("login")') == "true" ]]; then @@ -62,6 +72,7 @@ for org in "Gameplex-labs" "Neucruit" "uiastra"; do done echo -e "\nTesting GitHub starred repos access:" +echo "Method 1 - Using /user/starred endpoint (authenticated user):" STARRED_RESPONSE=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/user/starred?per_page=1") STARRED_COUNT=$(echo "$STARRED_RESPONSE" | jq '. | length') if [ "$STARRED_COUNT" -eq 0 ]; then @@ -71,6 +82,60 @@ else echo "Total starred repositories accessible: $(curl -s -H "Authorization: token $GITHUB_TOKEN" -I "https://api.github.com/user/starred" | grep -i "^link:" | grep -o "page=[0-9]*" | sort -r | head -1 | cut -d= -f2 || echo "Unknown")" fi +echo -e "\nMethod 2 - Using /users/{username}/starred endpoint (specific user):" +PUBLIC_STARRED_RESPONSE=$(curl -s -H "Authorization: token $GITHUB_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28" "https://api.github.com/users/$GITHUB_USERNAME/starred?per_page=1") +PUBLIC_STARRED_COUNT=$(echo "$PUBLIC_STARRED_RESPONSE" | jq '. | length') +if [ "$PUBLIC_STARRED_COUNT" -eq 0 ]; then + echo "No public starred repositories found for $GITHUB_USERNAME." +else + echo "First public starred repo for $GITHUB_USERNAME: $(echo "$PUBLIC_STARRED_RESPONSE" | jq '.[0].full_name')" + echo "Total public starred repositories for $GITHUB_USERNAME: $(curl -s -H "Authorization: token $GITHUB_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28" -I "https://api.github.com/users/$GITHUB_USERNAME/starred" | grep -i "^link:" | grep -o "page=[0-9]*" | sort -r | head -1 | cut -d= -f2 || echo "Unknown")" +fi + +echo -e "\nTesting GitHub issues access:" +echo "Checking for issues in your repositories..." + +# Get a list of repositories to check for issues +USER_REPOS=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/user/repos?per_page=5&sort=updated") +REPO_COUNT=$(echo "$USER_REPOS" | jq '. | length') + +echo "Found $REPO_COUNT recently updated repositories to check for issues" + +# Check each repository for issues +for i in $(seq 0 $(($REPO_COUNT - 1))); do + REPO=$(echo "$USER_REPOS" | jq -r ".[$i].full_name") + REPO_HAS_ISSUES=$(echo "$USER_REPOS" | jq -r ".[$i].has_issues") + + if [ "$REPO_HAS_ISSUES" = "true" ]; then + ISSUES_RESPONSE=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/$REPO/issues?state=all&per_page=1") + ISSUES_COUNT=$(curl -s -H "Authorization: token $GITHUB_TOKEN" -I "https://api.github.com/repos/$REPO/issues?state=all" | grep -i "^link:" | grep -o "page=[0-9]*" | sort -r | head -1 | cut -d= -f2 || echo "0") + + if [ -z "$ISSUES_COUNT" ]; then + # If we couldn't get the count from Link header, count the array length + ISSUES_COUNT=$(echo "$ISSUES_RESPONSE" | jq '. | length') + fi + + if [ "$ISSUES_COUNT" -gt 0 ]; then + echo "Repository $REPO has approximately $ISSUES_COUNT issues" + echo "Latest issue: $(echo "$ISSUES_RESPONSE" | jq -r '.[0].title // "No title"')" + else + echo "Repository $REPO has issues enabled but no issues were found" + fi + else + echo "Repository $REPO has issues disabled" + fi +done + +echo -e "\nVerifying GitHub token scopes for issues access:" +SCOPES=$(curl -s -I -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/user" | grep -i "^x-oauth-scopes:" | cut -d ":" -f 2- | tr -d '\r' || echo "None found") + +if [[ "$SCOPES" == *"repo"* ]]; then + echo "Your token has the 'repo' scope, which is required for issues access" +else + echo "WARNING: Your token may not have the 'repo' scope, which is required for full issues access" + echo "Testing issues access directly: $(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/$GITHUB_USERNAME/$(echo "$USER_REPOS" | jq -r '.[0].name')/issues")" +fi + echo -e "\nVerifying GitHub token scopes:" SCOPES=$(curl -s -I -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/user" | grep -i "^x-oauth-scopes:" | cut -d ":" -f 2- | tr -d '\r' || echo "None found") if [ -z "$SCOPES" ] || [ "$SCOPES" = "None found" ]; then @@ -89,5 +154,13 @@ echo "- repo (for repositories and issues)" echo "- read:org (for organization access)" echo "- user (for starred repositories)" +echo -e "\nSpecific user mode tests (USE_SPECIFIC_USER=true):" +echo "This mode uses the following endpoints:" +echo "- GET /users/{username}/orgs" +echo "- GET /users/{username}/starred" +echo "These endpoints are working: $([ "$PUBLIC_ORG_COUNT" -ge 0 ] && [ "$PUBLIC_STARRED_COUNT" -ge 0 ] && echo "YES" || echo "NO")" + echo -e "\nYour environment should now be ready for testing." +echo "To test with the new USE_SPECIFIC_USER feature:" +echo "export USE_SPECIFIC_USER=true" echo "Run ./run-local.sh to start the mirroring process." \ No newline at end of file diff --git a/run-local.sh b/run-local.sh index 1368c4e..29d97e1 100755 --- a/run-local.sh +++ b/run-local.sh @@ -22,6 +22,7 @@ docker container run \ -e MIRROR_ISSUES="true" \ -e MIRROR_STARRED="true" \ -e MIRROR_ORGANIZATIONS="true" \ + -e USE_SPECIFIC_USER="$USE_SPECIFIC_USER" \ -e INCLUDE_ORGS="$INCLUDE_ORGS" \ -e EXCLUDE_ORGS="$EXCLUDE_ORGS" \ -e PRESERVE_ORG_STRUCTURE="$PRESERVE_ORG_STRUCTURE" \ diff --git a/src/configuration.mjs b/src/configuration.mjs index 5fc4ca6..39acd55 100644 --- a/src/configuration.mjs +++ b/src/configuration.mjs @@ -38,6 +38,7 @@ export function configuration() { mirrorIssues: readBoolean("MIRROR_ISSUES"), mirrorStarred: readBoolean("MIRROR_STARRED"), mirrorOrganizations: readBoolean("MIRROR_ORGANIZATIONS"), + useSpecificUser: readBoolean("USE_SPECIFIC_USER"), singleRepo: readEnv("SINGLE_REPO"), includeOrgs: (readEnv("INCLUDE_ORGS") || "") .split(",") diff --git a/src/get-github-repositories.mjs b/src/get-github-repositories.mjs index f6c1137..d5fe1f9 100644 --- a/src/get-github-repositories.mjs +++ b/src/get-github-repositories.mjs @@ -19,7 +19,9 @@ async function getRepositories(octokit, mirrorOptions) { // Fetch starred repos if the option is enabled const starredRepos = mirrorOptions.mirrorStarred - ? await fetchStarredRepositories(octokit) + ? await fetchStarredRepositories(octokit, { + username: mirrorOptions.useSpecificUser ? mirrorOptions.username : undefined + }) : []; // Fetch organization repos if the option is enabled @@ -28,7 +30,8 @@ async function getRepositories(octokit, mirrorOptions) { octokit, mirrorOptions.includeOrgs, mirrorOptions.excludeOrgs, - mirrorOptions.preserveOrgStructure + mirrorOptions.preserveOrgStructure, + { username: mirrorOptions.useSpecificUser ? mirrorOptions.username : undefined } ) : []; @@ -98,16 +101,42 @@ async function fetchPrivateRepositories(octokit) { .then(toRepositoryList); } -async function fetchStarredRepositories(octokit) { +async function fetchStarredRepositories(octokit, options = {}) { + // If a specific username is provided, use the user-specific endpoint + if (options.username) { + return octokit + .paginate("GET /users/{username}/starred", { + username: options.username, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }) + .then(toRepositoryList); + } + + // Default: Get starred repos for the authenticated user (what was previously used) return octokit .paginate("GET /user/starred") .then(toRepositoryList); } -async function fetchOrganizationRepositories(octokit, includeOrgs = [], excludeOrgs = [], preserveOrgStructure = false) { +async function fetchOrganizationRepositories(octokit, includeOrgs = [], excludeOrgs = [], preserveOrgStructure = false, options = {}) { try { - // First get all organizations the user belongs to - const allOrgs = await octokit.paginate("GET /user/orgs"); + // Get all organizations the user belongs to + let allOrgs; + + // If a specific username is provided, use the user-specific endpoint + if (options.username) { + allOrgs = await octokit.paginate("GET /users/{username}/orgs", { + username: options.username, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }); + } else { + // Default: Get organizations for the authenticated user (what was previously used) + allOrgs = await octokit.paginate("GET /user/orgs"); + } // Filter organizations based on include/exclude lists let orgsToProcess = allOrgs;