Skip to content

Conversation

@gotalab
Copy link
Owner

@gotalab gotalab commented Dec 20, 2025

Summary

  • Add automatic GitHub authentication that works out of the box for users with GitHub CLI installed
  • No manual token configuration needed for private repository access
  • Improve error messages to be context-aware

Authentication Fallback Chain

  1. GH_TOKEN environment variable
  2. GITHUB_TOKEN environment variable
  3. gh auth token (GitHub CLI)

Error Message Improvements

Scenario Message
Token exists but 404 Suggests checking scopes/permissions/collaborator status
No token + gh installed Suggests gh auth login
No token + no gh Shows both options (gh CLI or env var)

Changes

  • New: src/skillport/shared/auth.py - Token resolution with pluggable resolvers
  • Updated: github.py - Use new auth module, context-aware error messages
  • Updated: update.py - Use new auth module
  • Docs: README.md, guide/cli.md, guide/configuration.md

Test plan

  • uv run pytest tests/ -x -q - All tests pass
  • uv run verify_server.py - Server verification passes
  • Manual test with private repository - Works with gh auth login

🤖 Generated with Claude Code

gotalab and others added 3 commits December 21, 2025 00:17
Add automatic GitHub authentication that works out of the box for users
with GitHub CLI installed. No manual token configuration needed.

Authentication fallback chain:
1. GH_TOKEN environment variable
2. GITHUB_TOKEN environment variable
3. gh auth token (GitHub CLI)

Also improves error messages to be context-aware:
- With token: suggests checking scopes/permissions
- Without token + gh installed: suggests `gh auth login`
- Without token + no gh: shows both options

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copilot AI review requested due to automatic review settings December 20, 2025 15:18
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds automatic GitHub authentication detection that works seamlessly with GitHub CLI, eliminating the need for manual token configuration. The implementation uses a well-designed fallback chain (GH_TOKEN → GITHUB_TOKEN → gh CLI) and provides context-aware error messages that guide users to the appropriate authentication solution based on their setup.

Key changes:

  • New authentication module with pluggable token resolvers and automatic fallback
  • Context-aware error messages that adapt based on token availability and gh CLI presence
  • Updated GitHub integration to use the new auth module throughout

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/skillport/shared/auth.py New authentication module implementing token resolution with fallback chain and gh CLI integration
src/skillport/shared/__init__.py Exports new auth module functions and types
src/skillport/modules/skills/internal/github.py Integrates new auth module; adds context-aware error messages for 404/403 responses
src/skillport/modules/skills/public/update.py Updates to use new auth module instead of direct environment variable access
src/skillport/modules/skills/internal/validation.py Minor formatting improvement (blank line addition)
src/skillport/interfaces/cli/commands/validate.py Code style improvements (list comprehension formatting)
guide/configuration.md Documents new authentication fallback chain with examples
guide/cli.md Adds comprehensive GitHub authentication section with fallback chain documentation
README.md Adds example showing private repo access with gh CLI
uv.lock Version bump to 0.5.2

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +122
"""GitHub authentication token resolution with fallback chain.

Design: Function-based with pluggable resolvers for easy extension.

Fallback chain (in order):
1. GH_TOKEN environment variable (fine-grained PAT recommended by GitHub)
2. GITHUB_TOKEN environment variable (classic, widely used)
3. gh CLI auth token (for local development)

Usage:
from skillport.shared.auth import resolve_github_token

result = resolve_github_token()
if result.token:
headers["Authorization"] = f"Bearer {result.token}"
"""

from __future__ import annotations

import os
import subprocess
from collections.abc import Callable
from dataclasses import dataclass

# Type alias for resolver functions
TokenResolver = Callable[[], "TokenResult | None"]


@dataclass(frozen=True)
class TokenResult:
"""Result of token resolution with source information."""

token: str | None
source: str | None # e.g., "GH_TOKEN", "GITHUB_TOKEN", "gh_cli"

@property
def has_token(self) -> bool:
return bool(self.token)

def __bool__(self) -> bool:
return self.has_token


# --- Token Resolvers (each returns TokenResult or None) ---


def _resolve_from_gh_token_env() -> TokenResult | None:
"""Resolve from GH_TOKEN (preferred for fine-grained PAT)."""
if token := os.getenv("GH_TOKEN"):
return TokenResult(token=token, source="GH_TOKEN")
return None


def _resolve_from_github_token_env() -> TokenResult | None:
"""Resolve from GITHUB_TOKEN (classic, widely used)."""
if token := os.getenv("GITHUB_TOKEN"):
return TokenResult(token=token, source="GITHUB_TOKEN")
return None


def _resolve_from_gh_cli() -> TokenResult | None:
"""Resolve from gh CLI auth token."""
try:
result = subprocess.run(
["gh", "auth", "token"],
capture_output=True,
text=True,
timeout=5,
)
if result.returncode == 0 and (token := result.stdout.strip()):
return TokenResult(token=token, source="gh_cli")
except FileNotFoundError:
# gh CLI not installed
pass
except subprocess.TimeoutExpired:
# gh CLI timed out
pass
except Exception:
# Any other error (permissions, etc.)
pass
return None


# --- Fallback Chain Configuration ---

# Order matters: first match wins
# To customize, modify this list or use resolve_github_token(resolvers=[...])
DEFAULT_RESOLVERS: list[TokenResolver] = [
_resolve_from_gh_token_env,
_resolve_from_github_token_env,
_resolve_from_gh_cli,
]


def resolve_github_token(
resolvers: list[TokenResolver] | None = None,
) -> TokenResult:
"""Resolve GitHub token using fallback chain.

Args:
resolvers: Custom list of resolver functions. Defaults to DEFAULT_RESOLVERS.

Returns:
TokenResult with token and source, or empty TokenResult if none found.
"""
for resolver in resolvers or DEFAULT_RESOLVERS:
if result := resolver():
return result
return TokenResult(token=None, source=None)


def is_gh_cli_available() -> bool:
"""Check if gh CLI is installed and available."""
try:
result = subprocess.run(
["gh", "--version"],
capture_output=True,
timeout=5,
)
return result.returncode == 0
except (FileNotFoundError, subprocess.TimeoutExpired, Exception):
return False
Copy link

Copilot AI Dec 20, 2025

Choose a reason for hiding this comment

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

The new authentication module lacks test coverage. Given that the repository has comprehensive tests for other GitHub-related functionality (e.g., test_github_source.py), the authentication resolution logic should also have tests covering:

  • Token resolution from each source (GH_TOKEN, GITHUB_TOKEN, gh CLI)
  • Fallback chain behavior
  • Error handling (gh CLI not installed, timeout, etc.)
  • TokenResult properties and boolean conversion
  • is_gh_cli_available function

Consider adding a test file like tests/unit/test_auth.py to ensure the authentication chain works correctly and handles edge cases.

Copilot uses AI. Check for mistakes.
@gotalab gotalab merged commit 758ac1f into main Dec 20, 2025
9 checks passed
@gotalab gotalab deleted the feat/github-auth-fallback branch December 20, 2025 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants