#!/bin/bash # Claude Code OAuth Installer Script # Author: Guillaume Raille set -e # Color definitions RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' MAGENTA='\033[0;35m' CYAN='\033[0;36m' WHITE='\033[1;37m' BOLD='\033[1m' NC='\033[0m' # No Color # Logging functions log_info() { echo -e "${CYAN}ℹ ${WHITE}$1${NC}" } log_success() { echo -e "${GREEN}✓ ${WHITE}$1${NC}" } log_warning() { echo -e "${YELLOW}⚠ ${WHITE}$1${NC}" } log_error() { echo -e "${RED}✗ ${WHITE}$1${NC}" } log_step() { echo -e "${MAGENTA}${BOLD}▶ $1${NC}" } # Helper functions for reading input when piped from curl read_from_tty() { local prompt="$1" local input if [ -t 0 ]; then # Running interactively read -p "$prompt" input else # Running from pipe, use /dev/tty printf "%s" "$prompt" >/dev/tty read input /dev/null; then log_error "GitHub CLI (gh) is not installed or not in PATH" echo log_info "Please install GitHub CLI first:" echo " • Visit: https://cli.github.com/" echo " • Or use: brew install gh (macOS) or apt install gh (Ubuntu)" exit 1 fi log_success "GitHub CLI is installed" # Check jq installation if ! command -v jq &> /dev/null; then log_error "jq is not installed or not in PATH" echo log_info "Please install jq first:" echo " • Visit: https://jqlang.github.io/jq/" echo " • Or use: brew install jq (macOS) or apt install jq (Ubuntu)" exit 1 fi log_success "jq is installed" # Step 2: Get GitHub username log_step "STEP 2: Getting GitHub Username" GITHUB_USERNAME=$(gh api user | jq -r '.login' 2>/dev/null) if [ $? -ne 0 ] || [ "$GITHUB_USERNAME" = "null" ] || [ -z "$GITHUB_USERNAME" ]; then log_error "Failed to get GitHub username. Please ensure you're logged in to GitHub CLI" echo log_info "Run: gh auth login" exit 1 fi log_success "Authenticated as: $GITHUB_USERNAME" # Step 3: Repository detection/selection log_step "STEP 3: Repository Setup" if [ -n "$REPO_ARG" ]; then REPO_NAME="$REPO_ARG" log_info "Using repository from --repo flag: $REPO_NAME" else # Try to get current repo set +e # Temporarily disable exit on error CURRENT_REPO=$(gh repo view --json name -q ".name" 2>/dev/null) GH_REPO_EXIT_CODE=$? set -e # Re-enable exit on error # Check if gh repo view failed due to no git repository if [ $GH_REPO_EXIT_CODE -ne 0 ]; then set +e # Temporarily disable exit on error GH_ERROR=$(gh repo view 2>&1) set -e # Re-enable exit on error # Temporarily disable set -e for grep commands set +e echo "$GH_ERROR" | grep -q "not a git repository" NOT_GIT_REPO=$? echo "$GH_ERROR" | grep -q "no git remotes found" NO_REMOTES=$? set -e if [ $NOT_GIT_REPO -eq 0 ]; then log_error "Not in a git repository" echo log_info "This directory is not a git repository. Initialize it first:" echo -e " ${CYAN}git init${NC}" echo log_info "Then create and publish it to GitHub:" echo -e " ${CYAN}gh repo create $GITHUB_USERNAME/your-repo-name --source=. --private${NC}" echo -e " ${CYAN}# or --public for a public repository${NC}" exit 1 elif [ $NO_REMOTES -eq 0 ]; then log_warning "Local git repository exists but not published to GitHub" echo log_info "This repository hasn't been published to GitHub yet." echo -e "${BOLD}Would you like to create it on GitHub now? (y/N):${NC} " CREATE_REPO=$(read_from_tty "") if [[ "$CREATE_REPO" =~ ^[Yy]$ ]]; then echo echo -e "${BOLD}Enter repository name (without owner):${NC}" echo -e "${CYAN}Example: claude-code-login${NC}" REPO_NAME_ONLY=$(read_from_tty "Repository name: ") if [ -z "$REPO_NAME_ONLY" ]; then log_error "Repository name cannot be empty" exit 1 fi echo -e "${BOLD}Make repository public? (y/N):${NC} " IS_PUBLIC=$(read_from_tty "") if [[ "$IS_PUBLIC" =~ ^[Yy]$ ]]; then VISIBILITY="--public" else VISIBILITY="--private" fi log_info "Creating repository on GitHub..." if gh repo create "$GITHUB_USERNAME/$REPO_NAME_ONLY" --source=. $VISIBILITY; then log_success "Repository created and published to GitHub" REPO_NAME="$GITHUB_USERNAME/$REPO_NAME_ONLY" else log_error "Failed to create repository on GitHub" exit 1 fi else log_info "Create and publish your repository to GitHub first:" echo -e " ${CYAN}gh repo create $GITHUB_USERNAME/your-repo-name --source=. --private${NC}" echo -e " ${CYAN}# or --public for a public repository${NC}" exit 1 fi else # Other error - proceed to manual input log_warning "Could not detect current repository" echo echo -e "${BOLD}Please enter repository name (format: owner/repo-name):${NC}" echo -e "${CYAN}Example: $GITHUB_USERNAME/claude-code-login${NC}" REPO_NAME=$(read_from_tty "Repository: ") fi elif [ -n "$CURRENT_REPO" ]; then set +e # Temporarily disable exit on error REPO_OWNER=$(gh repo view --json owner -q ".owner.login" 2>/dev/null) set -e # Re-enable exit on error REPO_NAME="${REPO_OWNER}/${CURRENT_REPO}" log_success "Found current repository: $REPO_NAME" else log_warning "No current repository found" echo echo -e "${BOLD}Please enter repository name (format: owner/repo-name):${NC}" echo -e "${CYAN}Example: $GITHUB_USERNAME/claude-code-login${NC}" REPO_NAME=$(read_from_tty "Repository: ") if [ -z "$REPO_NAME" ]; then log_error "Repository name cannot be empty" exit 1 fi # Validate repository format if [[ ! "$REPO_NAME" =~ ^[^/]+/[^/]+$ ]]; then log_error "Invalid repository format. Use: owner/repo-name" exit 1 fi fi fi # Verify repository exists log_info "Verifying repository access: $REPO_NAME" if ! gh repo view "$REPO_NAME" &>/dev/null; then log_error "Cannot access repository: $REPO_NAME" echo log_info "Please ensure:" echo " • The repository exists" echo " • You have access to the repository" echo " • You're authenticated with the correct GitHub account" exit 1 fi log_success "Repository verified: $REPO_NAME" # Step 4: Check for existing secret log_step "STEP 4: Checking Repository Secrets" SECRET_EXISTS=false # Temporarily disable set -e for grep set +e gh secret list --repo "$REPO_NAME" | grep -q "SECRETS_ADMIN_PAT" SECRET_CHECK=$? set -e if [ $SECRET_CHECK -eq 0 ]; then SECRET_EXISTS=true log_success "SECRETS_ADMIN_PAT already exists" else log_warning "SECRETS_ADMIN_PAT not found" echo echo -e "${BOLD}You need to provide a Personal Access Token (PAT) for secrets management.${NC}" echo log_info "How to create a SECRETS_ADMIN_PAT:" echo " • Visit: https://github.com/grll/claude-code-login?tab=readme-ov-file#prerequisites-setting-up-secrets_admin_pat" echo echo -e "${BOLD}Enter your SECRETS_ADMIN_PAT (input will be hidden):${NC}" PAT_TOKEN=$(read_secret_from_tty) echo if [ -z "$PAT_TOKEN" ]; then log_error "Personal Access Token cannot be empty" exit 1 fi # Set the secret log_info "Setting SECRETS_ADMIN_PAT secret..." if echo "$PAT_TOKEN" | gh secret set SECRETS_ADMIN_PAT --repo "$REPO_NAME"; then log_success "SECRETS_ADMIN_PAT secret has been set" else log_error "Failed to set SECRETS_ADMIN_PAT secret" exit 1 fi fi # Step 5: Git repository setup log_step "STEP 5: Setting up Git Repository" # Save current branch and any uncommitted changes ORIGINAL_BRANCH=$(git branch --show-current) if [ -z "$ORIGINAL_BRANCH" ]; then # If detached HEAD or no branch, get the commit SHA ORIGINAL_BRANCH=$(git rev-parse HEAD 2>/dev/null || echo "main") IS_DETACHED=true else IS_DETACHED=false fi log_info "Current branch/commit: $ORIGINAL_BRANCH" # Stash any existing changes (including untracked files) log_info "Stashing existing changes..." set +e # Temporarily disable exit on error STASH_RESULT=$(git stash push -u -m "Pre-Claude-OAuth-setup stash" 2>&1) STASH_EXIT_CODE=$? set -e # Re-enable exit on error # Check stash result set +e echo "$STASH_RESULT" | grep -q "No local changes to save" NO_CHANGES=$? echo "$STASH_RESULT" | grep -q "You do not have the initial commit yet" NO_INITIAL_COMMIT=$? set -e if [ $NO_CHANGES -eq 0 ] || [ $NO_INITIAL_COMMIT -eq 0 ]; then STASH_CREATED=false if [ $NO_INITIAL_COMMIT -eq 0 ]; then log_info "No initial commit yet - skipping stash" else log_info "No existing changes to stash" fi elif [ $STASH_EXIT_CODE -eq 0 ]; then STASH_CREATED=true log_success "Existing changes stashed" else STASH_CREATED=false log_warning "Failed to stash changes: $STASH_RESULT" fi # Check if main branch exists locally if git show-ref --verify --quiet refs/heads/main; then MAIN_BRANCH="main" elif git show-ref --verify --quiet refs/heads/master; then MAIN_BRANCH="master" else # Create main branch if it doesn't exist log_info "Creating main branch..." git checkout -b main MAIN_BRANCH="main" CREATED_MAIN=true fi # Switch to main branch if [ "$ORIGINAL_BRANCH" != "$MAIN_BRANCH" ]; then log_info "Switching to $MAIN_BRANCH branch..." git checkout "$MAIN_BRANCH" fi # Step 6: Create workflows log_step "STEP 6: Creating GitHub Workflows" mkdir -p .github/workflows # Create claude_code_login.yml log_info "Creating claude_code_login.yml..." cat > .github/workflows/claude_code_login.yml << 'EOF' name: Claude OAuth on: workflow_dispatch: inputs: code: description: 'Authorization code (leave empty for step 1)' required: false permissions: actions: write # Required for cache management contents: read # Required for basic repository access jobs: auth: runs-on: ubuntu-latest steps: - uses: grll/claude-code-login@v1 with: code: ${{ inputs.code }} secrets_admin_pat: ${{ secrets.SECRETS_ADMIN_PAT }} EOF log_success "Created claude_code_login.yml" # Create claude_code.yml with username replacement log_info "Creating claude_code.yml..." cat > .github/workflows/claude_code.yml << EOF name: Claude PR Assistant - Authorized Users Only on: issue_comment: types: [created] pull_request_review_comment: types: [created] issues: types: [opened, assigned] pull_request_review: types: [submitted] jobs: claude-code-action: # Only respond to @claude mentions from $GITHUB_USERNAME if: | ( (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || (github.event_name == 'issues' && contains(github.event.issue.body, '@claude')) ) && github.actor == '$GITHUB_USERNAME' runs-on: ubuntu-latest permissions: contents: read pull-requests: read issues: read id-token: write actions: write steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 1 - name: Run Claude PR Action uses: grll/claude-code-action@beta with: use_oauth: true claude_access_token: \${{ secrets.CLAUDE_ACCESS_TOKEN }} claude_refresh_token: \${{ secrets.CLAUDE_REFRESH_TOKEN }} claude_expires_at: \${{ secrets.CLAUDE_EXPIRES_AT }} secrets_admin_pat: \${{ secrets.SECRETS_ADMIN_PAT }} timeout_minutes: "60" EOF log_success "Created claude_code.yml" # Step 7: Commit and push workflows log_step "STEP 7: Committing and Pushing Workflows" # Add the workflow files log_info "Adding workflow files to git..." git add .github/workflows/claude_code_login.yml .github/workflows/claude_code.yml # Check if there are changes to commit if git diff --cached --quiet; then log_warning "No changes to commit (workflows may already exist)" COMMITTED=false USER_CONSENTED=true # No need to ask since nothing to commit else # Ask for user consent before committing echo echo -e "${BOLD}${YELLOW}⚠️ Permission Required${NC}" echo -e "${BOLD}The installer needs to commit and push workflow files to the $MAIN_BRANCH branch.${NC}" echo echo -e "${GREEN}✓ Recommended:${NC} Give consent for a smoother experience" echo -e " • We'll automatically switch to $MAIN_BRANCH branch" echo -e " • Commit the workflow files" echo -e " • Push to remote repository" echo -e " • ${BOLD}Return you to your current branch/state${NC}" echo echo -e "${YELLOW}✗ Without consent:${NC}" echo -e " • You'll need to manually commit and push the workflows" echo -e " • The setup instructions will include these manual steps" echo echo -e "${BOLD}Allow automatic commit and push to $MAIN_BRANCH? (y/N):${NC} " CONSENT=$(read_from_tty "") if [[ "$CONSENT" =~ ^[Yy]$ ]]; then USER_CONSENTED=true log_success "Consent granted. Proceeding with automatic commit and push." # Commit the changes log_info "Committing workflow files to $MAIN_BRANCH branch..." git commit -m "Add Claude Code OAuth workflows - claude_code_login.yml: OAuth authentication workflow - claude_code.yml: PR assistant workflow for @$GITHUB_USERNAME 🤖 Generated with Claude OAuth Installer Co-authored-by: grll " log_success "Workflows committed to $MAIN_BRANCH branch" COMMITTED=true # Push to remote log_info "Pushing to remote repository..." # Check if we have a remote set +e git remote | grep -q origin REMOTE_CHECK=$? set -e if [ $REMOTE_CHECK -ne 0 ]; then log_warning "No remote 'origin' found. You may need to set up a remote and push manually." log_info "To set up remote: git remote add origin https://github.com/$REPO_NAME.git" PUSHED=false else # Try to push main branch if git push origin "$MAIN_BRANCH"; then log_success "Workflows pushed to remote repository ($MAIN_BRANCH branch)" PUSHED=true else log_warning "Failed to push. You may need to push manually:" echo " git push origin $MAIN_BRANCH" PUSHED=false fi fi else USER_CONSENTED=false COMMITTED=false PUSHED=false log_warning "Consent not granted. You'll need to manually commit and push the workflows." # Reset the staged files git reset HEAD .github/workflows/claude_code_login.yml .github/workflows/claude_code.yml fi fi # Return to original branch/commit if [ "$ORIGINAL_BRANCH" != "$MAIN_BRANCH" ]; then log_info "Returning to original branch/commit: $ORIGINAL_BRANCH" if [ "$IS_DETACHED" = true ]; then git checkout "$ORIGINAL_BRANCH" 2>/dev/null || log_warning "Could not return to original commit" else git checkout "$ORIGINAL_BRANCH" fi fi # Pop stashed changes if we created a stash if [ "$STASH_CREATED" = true ]; then log_info "Restoring stashed changes..." if git stash pop; then log_success "Stashed changes restored" else log_warning "Failed to restore stashed changes. Check 'git stash list'" fi fi # Step 8: Final instructions log_step "SETUP COMPLETE! Next Steps:" echo # Show manual commit instructions if user didn't consent if [ "$USER_CONSENTED" = false ]; then echo -e "${RED}${BOLD}⚠️ MANUAL ACTION REQUIRED ⚠️${NC}" echo -e "${BOLD}You chose not to automatically commit the workflows. Please run these commands:${NC}" echo echo -e "${CYAN}# 1. Switch to $MAIN_BRANCH branch${NC}" echo -e " git checkout $MAIN_BRANCH" echo echo -e "${CYAN}# 2. Add and commit the workflow files${NC}" echo -e " git add .github/workflows/claude_code_login.yml .github/workflows/claude_code.yml" echo -e " git commit -m \"Add Claude Code OAuth workflows\"" echo echo -e "${CYAN}# 3. Push to remote${NC}" echo -e " git push origin $MAIN_BRANCH" echo echo -e "${CYAN}# 4. Return to your branch (optional)${NC}" echo -e " git checkout $ORIGINAL_BRANCH" echo echo -e "${YELLOW}Complete these steps before continuing with the instructions below!${NC}" echo fi echo -e "${RED}${BOLD}⚠️ IMPORTANT: GitHub App Required ⚠️${NC}" echo -e "${BOLD}For @claude to work, you MUST install the official Anthropic GitHub App:${NC}" echo -e " • Install here: ${CYAN}https://github.com/apps/claude${NC}" echo -e " • Grant access to: ${GREEN}$REPO_NAME${NC}" echo -e " • Without this app, @claude mentions will fail!" echo echo -e "${BOLD}1. Run the OAuth workflow (without code):${NC}" echo -e " • Go to: https://github.com/$REPO_NAME/actions/workflows/claude_code_login.yml" echo -e " • Click ${GREEN}'Run workflow'${NC} → ${GREEN}'Run workflow'${NC} (leave code empty)" echo echo -e "${BOLD}2. Get the login URL:${NC}" echo -e " • Wait for the workflow to complete" echo -e " • Click on the workflow run" echo -e " • Look for the login URL in the action summary" echo echo -e "${BOLD}3. Authenticate with Claude:${NC}" echo -e " • Visit the generated URL" echo -e " • Log in to Claude" echo -e " • Copy the authorization code" echo echo -e "${BOLD}4. Complete setup:${NC}" echo -e " • Run the workflow again with the authorization code" echo -e " • Go to: https://github.com/$REPO_NAME/actions/workflows/claude_code_login.yml" echo -e " • Click ${GREEN}'Run workflow'${NC} → paste your code → ${GREEN}'Run workflow'${NC}" echo echo -e "${BOLD}5. Start using Claude:${NC}" echo -e " • Create issues or PRs and mention ${CYAN}@claude${NC}" echo -e " • Only ${GREEN}$GITHUB_USERNAME${NC} can trigger Claude responses" echo -e " • Customize your workflow in ${CYAN}claude_code.yml${NC}" echo log_success "Installation complete! 🎉" echo echo -e "${CYAN}For more information and troubleshooting:${NC}" echo -e "https://github.com/grll/claude-code-login" echo -e "https://github.com/grll/claude-code-action"