Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/github/gh-repo-transfer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -eo pipefail

# Authentication and configuration
# The script follows the project architecture, locating the base directory to load utilities.
BASE_DIR=$(dirname "$(readlink -f "$0")")/..
source "${BASE_DIR}/utils/settings.sh"

# Ensures GITHUB_TOKEN is available, loading auth.sh if necessary
# This allows the script to run standalone
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
if [[ -f "${BASE_DIR}/utils/auth.sh" ]]; then
source "${BASE_DIR}/utils/auth.sh"
fi
fi
Comment on lines +11 to +15

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


# Function to transfer a repository on GitHub
function transfer_github_repo() {
local target_repo
local new_owner
local endpoint
local payload
local response

# Target repository in owner/repo format
target_repo="$1"
# New owner (user or organization) for the repository
new_owner="$2"

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

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 hardcoded
# Doc: POST /repos/{owner}/{repo}/transfer
# https://docs.github.com/en/rest/repos/repos?apiVersion=2026-03-10#transfer-a-repository
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}')


# Performs the transfer using curl.
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
Comment on lines +45 to +54

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

}

# Script execution with parameters passed via CLI
transfer_github_repo "$1" "$2"
Loading