Skip to content

Feature: Add GitHub Repository Transfer Capability - #23

Merged
rmottanet merged 1 commit into
mainfrom
feat/gh-repo-transfer
Jul 18, 2026
Merged

Feature: Add GitHub Repository Transfer Capability#23
rmottanet merged 1 commit into
mainfrom
feat/gh-repo-transfer

Conversation

@rmottanet

Copy link
Copy Markdown
Owner

This pull request introduces a new script, gh-repo-transfer.sh, to enable programmatic repository transfers between owners (users or organizations) via the GitHub API. This extends the project's GitHub API coverage with additional repository management functionality and establishes improved error handling patterns for the codebase.


Changes Implemented:

  • src/github/gh-repo-transfer.sh: New script implementing repository transfer functionality:

    • Supports transferring repositories to new owners (users or organizations)
    • Implements GitHub API endpoint POST /repos/{owner}/{repo}/transfer
    • Uses secure cURL configuration (--proto "=https" --tlsv1.2)
    • Follows project architecture with BASE_DIR sourcing pattern
    • Loads GITHUB_TOKEN from auth utilities or environment variables
  • Error Handling Enhancement: Establishes robust set -eo pipefail configuration

    • Differs from existing scripts using set -euo pipefail
    • Preserves error output while maintaining pipeline safety
    • Prevents silent failures in complex script chains
  • Standalone Execution Support: Robust authentication loading pattern

    • Attempts to source settings.sh for base configuration
    • Falls back to direct auth.sh loading if token not present
    • Enables script execution from any directory

Technical Details:

Error Handling Philosophy

Before (Pattern in Other Scripts):

set -euo pipefail
  • -e: Exit on any error
  • -u: Treat unset variables as errors (causes early exit with no error message)
  • -o pipefail: Pipeline failures propagate

After (Enhanced Pattern in This Script):

set -eo pipefail
  • -e: Exit on any error
  • -o pipefail: Pipeline failures propagate
  • Removed -u: Prevents premature exits without clear error messages
  • Improved user experience: Better error visibility and debugging

Why This Matters:

  • set -u can cause scripts to exit silently with no meaningful error message
  • Removing -u allows scripts to fail with clear error output via echo >&2
  • Maintains safety while improving diagnostic capabilities
  • Establishes a more robust pattern for future scripts

Authentication Loading Pattern

# Architecture-aware path resolution
BASE_DIR=$(dirname "$(readlink -f "$0")")/..
source "${BASE_DIR}/utils/settings.sh"

# Token loading with fallback
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
    if [[ -f "${BASE_DIR}/utils/auth.sh" ]]; then
        source "${BASE_DIR}/utils/auth.sh"
    fi
fi

Advantages:

  • Standalone Execution: Script works from any directory
  • Graceful Fallback: Attempts multiple authentication sources
  • Project Integration: Uses existing settings and auth patterns
  • User-Friendly: Clear error messages when token is missing

Script Features

Feature Description
Usage gh-repo-transfer.sh <owner/repo> <new_owner>
API Endpoint POST /repos/{owner}/{repo}/transfer
Authentication Bearer token via GITHUB_TOKEN
Security HTTPS enforced, TLS 1.2 minimum
Output JSON response parsed with jq
Error Handling Parameter validation with exit codes
Architecture Consistent with project module patterns
Error Output Proper stderr redirection for error messages

Benefits:

1. Improved Error Handling

  • Scripts now fail with meaningful error messages
  • Developer debugging is significantly easier
  • Maintains pipeline safety without silent failures

2. Enhanced Architecture

  • Base path resolution works from any installation location
  • Standalone execution without environment prerequisites
  • Consistent pattern for future script development

3. Extended API Coverage

  • Adds repository transfer to existing management capabilities
  • Complements gh-repo-list.sh, gh-repo-delete.sh, and gh-search-repo.sh
  • Enables complete repository lifecycle management

4. Automation Enablement

  • Programmatic ownership changes for CI/CD pipelines
  • Supports migration between organizations
  • Facilitates repository reorganization workflows

5. Security Standards

  • Consistent with project's security-hardened cURL configuration
  • Uses project authentication patterns
  • No hardcoded credentials or insecure requests

Usage Examples:

Basic Transfer

gh-repo-transfer.sh rmottanet/gitnap my-org

Bulk Transfer from File

# Prepare a file with list of repositories (one per line)
cat > repos_to_transfer.txt << EOF
rmottanet/repo1
rmottanet/repo2
rmottanet/repo3
EOF

# Bulk transfer with rate limiting
xargs -n 1 -I {} sh -c \
    "gh-repo-transfer.sh {} new-org && sleep 1" \
    < repos_to_transfer.txt

Integration with Other Commands

# List repositories and filter specific ones
gh-repo-list.sh | grep "project-name" > selected_repos.txt

# Transfer selected repositories with rate limiting
xargs -n 1 -I {} sh -c \
    "gh-repo-transfer.sh {} new-org && sleep 1" \
    < selected_repos.txt

Error Handling Example

# Script will exit with clear error message if missing parameters
./gh-repo-transfer.sh
# Output: Usage: ./gh-repo-transfer.sh <owner/repo> <new_owner>

# Proper error message for invalid repository
./gh-repo-transfer.sh invalid/repo new-org
# Output: {"message":"Not Found","documentation_url":"..."}

Technical Differences from Existing Scripts:

Error Handling Comparison

Feature Existing Scripts This Script
set -e
set -u
set -o pipefail
Error messages Silent exit Clear output

Architecture Comparison

Feature Existing Scripts This Script
BASE_DIR resolution
settings.sh loading
auth.sh fallback
Standalone execution
Error stderr redirection Partial Complete

Motivation:

This addition addresses a gap in the project's GitHub API coverage:

  1. Repository Lifecycle: While list, search, and delete operations exist, transfer capability was missing
  2. Migration Needs: Organizations frequently need to transfer repositories between owners
  3. Automation Requirement: Programmatic transfers are needed for CI/CD and infrastructure automation
  4. Error Handling Improvement: Establishes a better error handling pattern for the project
  5. Project Completeness: Fills a logical gap in the GitHub module functionality

Testing & Validation:

The script has been validated to ensure:

  • Proper parameter validation and error messages
  • Token loading works from both auth.sh and environment
  • Correct API endpoint construction
  • Response parsing via jq works as expected
  • Security flags (--proto, --tlsv1.2) are applied correctly
  • Exit codes reflect script success/failure status
  • Error messages are sent to stderr for proper pipeline integration
  • Script executes standalone from any directory

Dependencies:

None additional - the script leverages:

  • curl: Already used in all GitHub scripts
  • jq: Already used for JSON parsing
  • Existing auth.sh: Consistent with project patterns

This script establishes a new standard for error handling in the GitNap project, improving developer experience and debugging capabilities across the codebase.

- Added gh-repo-transfer.sh script to transfer GitHub repositories
- Implements POST /repos/{owner}/{repo}/transfer API endpoint
- Supports transfer to new owner (user or organization)
- Uses secure curl configuration with HTTPS and TLS 1.2
- Includes parameter validation and error handling
- Loads GITHUB_TOKEN from auth.sh or environment variable
- Follows project architecture with BASE_DIR sourcing pattern
- Outputs JSON response via jq for consistent formatting

This extends GitHub API coverage with repository transfer capabilities:
- Enables programmatic ownership transfer
- Follows same security and error handling patterns as existing scripts
- Allows migration between organizations or to other users
@rmottanet rmottanet self-assigned this Jul 18, 2026
@rmottanet rmottanet added the enhancement New feature or request label Jul 18, 2026
@rmottanet rmottanet added this to @CL1 and @ws2git Jul 18, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new Bash script, gh-repo-transfer.sh, to transfer GitHub repositories to a new owner using the GitHub REST API. The review feedback highlights several critical improvements: ensuring GITHUB_TOKEN is validated before execution, redirecting usage errors to stderr while fixing a typo, safely constructing the JSON payload using jq to prevent injection vulnerabilities, and removing the -f flag from curl to allow proper error handling and reporting of API error messages.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +45 to +54
response=$(curl --proto "=https" --tlsv1.2 -sSf -L -X POST "$endpoint" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2026-03-10" \
-d "$payload")

# Displays the response message or the repository name upon success.
echo "$response" | jq -r '.message // .name'

return 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using curl -f (or --fail) with set -e causes the script to exit immediately on HTTP errors (like 404 or 422) without printing the API's JSON error response, which contradicts the described behavior in the PR description. Removing -f and checking for .message in the JSON response allows the error message to be printed while still exiting with a non-zero status on failure.

Suggested change
response=$(curl --proto "=https" --tlsv1.2 -sSf -L -X POST "$endpoint" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2026-03-10" \
-d "$payload")
# Displays the response message or the repository name upon success.
echo "$response" | jq -r '.message // .name'
return 0
response=$(curl --proto "=https" --tlsv1.2 -sS -L -X POST "$endpoint" \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2026-03-10" \
-d "$payload")
# Displays the response message or the repository name upon success.
echo "$response" | jq -r '.message // .name'
# Exit with error if the response contains an error message
if echo "$response" | jq -e '.message' >/dev/null; then
return 1
fi
return 0

Comment on lines +11 to +15
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
if [[ -f "${BASE_DIR}/utils/auth.sh" ]]; then
source "${BASE_DIR}/utils/auth.sh"
fi
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If GITHUB_TOKEN is still unset after attempting to load auth.sh, the script will proceed and fail during the API call. Adding an explicit check and exiting early with a clear error message improves the user experience and script robustness.

Suggested change
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
if [[ -f "${BASE_DIR}/utils/auth.sh" ]]; then
source "${BASE_DIR}/utils/auth.sh"
fi
fi
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
if [[ -f "${BASE_DIR}/utils/auth.sh" ]]; then
source "${BASE_DIR}/utils/auth.sh"
fi
fi
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
echo "Error: GITHUB_TOKEN is not set." >&2
exit 1
fi

Comment on lines +31 to +34
if [[ -z "$target_repo" || -z "$new_owner" ]]; then
echo "Usage: $0 <owner/repo> <novo_owner>"
exit 1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The usage message is currently printed to standard output instead of standard error. Additionally, there is a typo in the placeholder (<novo_owner> instead of <new_owner>). Redirecting usage/error messages to stderr is standard practice.

Suggested change
if [[ -z "$target_repo" || -z "$new_owner" ]]; then
echo "Usage: $0 <owner/repo> <novo_owner>"
exit 1
fi
if [[ -z "$target_repo" || -z "$new_owner" ]]; then
echo "Usage: $0 <owner/repo> <new_owner>" >&2
exit 1
fi

endpoint="https://api.github.com/repos/$target_repo/transfer"

# Creation of the JSON payload for the transfer
payload=$(printf '{"new_owner":"%s"}' "$new_owner")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Constructing JSON payloads using string interpolation or printf can lead to malformed JSON or injection vulnerabilities if the variable contains special characters (like quotes or backslashes). Since jq is already a dependency of this script, use it to safely encode the payload.

Suggested change
payload=$(printf '{"new_owner":"%s"}' "$new_owner")
payload=$(jq -n --arg new_owner "$new_owner" '{"new_owner": $new_owner}')

@rmottanet
rmottanet merged commit adf06f8 into main Jul 18, 2026
2 of 3 checks passed
@github-project-automation github-project-automation Bot moved this to Done in @ws2git Jul 18, 2026
@github-project-automation github-project-automation Bot moved this to Done in @CL1 Jul 18, 2026
@rmottanet
rmottanet deleted the feat/gh-repo-transfer branch July 18, 2026 21:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant