github

package
v1.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 13, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package github provides an HTTP client for the GitHub API.

Overview

This package fetches repository metrics from GitHub (https://api.github.com) for metadata enrichment. It provides data used by Nebraska ranking and brittle detection features.

Usage

client, err := github.NewClient(token, 24 * time.Hour)
if err != nil {
    log.Fatal(err)
}

metrics, err := client.Fetch(ctx, "pallets", "flask", false)
if err != nil {
    log.Fatal(err)
}

fmt.Println("Stars:", metrics.Stars)
fmt.Println("Contributors:", metrics.Contributors)

Authentication

A GitHub personal access token is optional but recommended to avoid rate limits. Without a token, the client is limited to 60 requests/hour. With a token, the limit is 5000 requests/hour.

RepoMetrics

[Fetch] returns an integrations.RepoMetrics containing:

  • Stars: Stargazer count
  • Owner: Repository owner login
  • Contributors: Top 5 contributors with commit counts
  • LastCommitAt: Most recent push date
  • LastReleaseAt: Most recent release date
  • Archived: Whether the repository is archived
  • License, Language, Topics: Additional metadata

Caching

Responses are cached to reduce API calls. The cache TTL is set when creating the client. Pass refresh=true to bypass the cache.

URL Extraction

ExtractURL parses GitHub repository URLs from package metadata, handling various URL formats (with/without .git, trailing slashes, etc.).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DetectLanguageFromManifest

func DetectLanguageFromManifest(path string) string

DetectLanguageFromManifest determines the language from a manifest filename.

func ExtractURL

func ExtractURL(urls map[string]string, homepage string) (owner, repo string, ok bool)

ExtractURL extracts GitHub repository owner and name from package URLs.

This function searches through urls map and homepage for GitHub URLs. It looks for patterns like "https://github.com/owner/repo".

Parameters:

  • urls: Map of URL keys to URL values from package metadata (may be nil)
  • homepage: Fallback homepage URL (may be empty)

Returns:

  • owner: Repository owner username (empty if not found)
  • repo: Repository name (empty if not found)
  • ok: true if a GitHub URL was found, false otherwise

This function is safe for concurrent use.

func ParsePrivateKey

func ParsePrivateKey(keyData string) (*rsa.PrivateKey, error)

ParsePrivateKey parses a PEM-encoded RSA private key. The key can be base64 encoded (for env vars) or raw PEM.

func ParseRepoRef

func ParseRepoRef(ref string) (owner, repo string, err error)

ParseRepoRef parses an "owner/repo" string and validates both parts. It also accepts full GitHub URLs in any of these forms:

https://github.com/owner/repo
http://github.com/owner/repo
github.com/owner/repo
owner/repo           (plain)

Trailing slashes and ".git" suffixes are stripped automatically. Returns owner, repo, and any validation error.

func ValidateOwner

func ValidateOwner(owner string) error

ValidateOwner validates a GitHub username or organization name.

func ValidateRepo

func ValidateRepo(repo string) error

ValidateRepo validates a GitHub repository name.

func ValidateRepoRef

func ValidateRepoRef(owner, repo string) error

ValidateRepoRef validates both owner and repo parameters.

Types

type AppClient

type AppClient struct {
	// contains filtered or unexported fields
}

AppClient handles GitHub App authentication and API calls.

func NewAppClient

func NewAppClient(config AppConfig) *AppClient

NewAppClient creates a new GitHub App client.

func (*AppClient) AuthorizationURL

func (c *AppClient) AuthorizationURL(state string, allowPrivate bool) string

AuthorizationURL generates the OAuth authorization URL for the GitHub App. The allowPrivate parameter controls whether to request access to private repos.

func (*AppClient) DeleteInstallation

func (c *AppClient) DeleteInstallation(ctx context.Context, installationID int64) error

DeleteInstallation uninstalls the GitHub App from a user or organization account. This requires App JWT authentication and removes all repository access.

func (*AppClient) ExchangeCode

func (c *AppClient) ExchangeCode(ctx context.Context, code string) (*UserAccessToken, error)

ExchangeCode exchanges an authorization code for a user access token.

func (*AppClient) FetchUser

func (c *AppClient) FetchUser(ctx context.Context, userToken string) (*User, error)

FetchUser retrieves the authenticated user's info using a user access token.

func (*AppClient) GetAppInstallationCount

func (c *AppClient) GetAppInstallationCount(ctx context.Context) (int64, error)

GetAppInstallationCount returns the total number of GitHub App installations. Uses the App's JWT to authenticate and fetch from GET /app/installations.

func (*AppClient) GetInstallationToken

func (c *AppClient) GetInstallationToken(ctx context.Context, installationID int64) (*InstallationToken, error)

GetInstallationToken creates an installation access token for repo access. The token is short-lived (~1 hour) and should be cached and refreshed as needed.

func (*AppClient) GetUserInstallation

func (c *AppClient) GetUserInstallation(ctx context.Context, userToken string) (*Installation, error)

GetUserInstallation finds the installation of this app for the authenticated user. Filters by the app's own ID to avoid picking up installations from other GitHub Apps.

func (*AppClient) GetUserInstallations

func (c *AppClient) GetUserInstallations(ctx context.Context, userToken string) ([]Installation, error)

GetUserInstallations lists all installations accessible to the authenticated user. Uses the user's OAuth token to find installations they can access.

func (*AppClient) PollForToken

func (c *AppClient) PollForToken(ctx context.Context, deviceCode string, interval int) (*UserAccessToken, error)

PollForToken polls GitHub for the access token after user authorization.

func (*AppClient) RefreshAccessToken

func (c *AppClient) RefreshAccessToken(ctx context.Context, refreshToken string) (*UserAccessToken, error)

RefreshAccessToken exchanges a refresh token for a new access token. GitHub App user access tokens expire after 8 hours; refresh tokens expire after 6 months. Returns a new UserAccessToken with fresh access_token and potentially new refresh_token.

func (*AppClient) RequestDeviceCode

func (c *AppClient) RequestDeviceCode(ctx context.Context) (*AppDeviceCodeResponse, error)

RequestDeviceCode initiates the device authorization flow for the GitHub App.

func (*AppClient) RevokeInstallationToken

func (c *AppClient) RevokeInstallationToken(ctx context.Context, token string) error

RevokeInstallationToken revokes an installation access token.

func (*AppClient) RevokeUserToken

func (c *AppClient) RevokeUserToken(ctx context.Context, accessToken string) error

RevokeUserToken revokes a user access token, removing the app's authorization. This removes the app from the user's "Authorized GitHub Apps" list.

type AppConfig

type AppConfig struct {
	AppID           int64
	PrivateKey      *rsa.PrivateKey
	ClientID        string
	ClientSecret    string
	RedirectURI     string
	PrivateKeyBytes []byte // Raw PEM bytes for device flow
}

AppConfig holds GitHub App configuration.

type AppDeviceCodeResponse

type AppDeviceCodeResponse struct {
	DeviceCode      string `json:"device_code"`
	UserCode        string `json:"user_code"`
	VerificationURI string `json:"verification_uri"`
	ExpiresIn       int    `json:"expires_in"`
	Interval        int    `json:"interval"`
}

AppDeviceCodeResponse contains the response from requesting a device code.

type AppInstallation

type AppInstallation struct {
	ID      int64  `json:"id"`
	AppID   int64  `json:"app_id"`
	AppSlug string `json:"app_slug"`
	Account struct {
		Login string `json:"login"`
		Type  string `json:"type"` // "User" or "Organization"
	} `json:"account"`
	RepositorySelection string `json:"repository_selection"` // "all" or "selected"
}

AppInstallation represents a GitHub App installation accessible to the user.

type Branch

type Branch struct {
	Name      string `json:"name"`
	Commit    string `json:"commit"` // SHA of the branch HEAD
	Protected bool   `json:"protected"`
}

Branch represents a Git branch in a repository.

type Client

type Client struct {
	*integrations.Client
	// contains filtered or unexported fields
}

Client provides access to the GitHub API for repository metadata enrichment. It handles HTTP requests with caching, automatic retries, and optional authentication.

All methods are safe for concurrent use by multiple goroutines.

func NewClient

func NewClient(backend cache.Cache, token string, cacheTTL time.Duration) *Client

NewClient creates a GitHub API client with optional authentication and proactive rate limiting.

Parameters:

  • backend: Cache backend for HTTP response caching (use storage.NullBackend{} for no caching)
  • token: GitHub personal access token (empty string for unauthenticated)
  • cacheTTL: How long responses are cached (typical: 1-24 hours)

Rate limits are configured via integrations.DefaultRateLimits:

  • Unauthenticated ("github_unauth"): 60 requests/hour per IP (~0.016 req/s)
  • Authenticated ("github"): 5,000 requests/hour per token (~1.4 req/s)

Authentication is strongly recommended for production use to avoid rate limiting. The returned Client is safe for concurrent use.

func (*Client) Fetch

func (c *Client) Fetch(ctx context.Context, owner, repo string, refresh bool) (*integrations.RepoMetrics, error)

Fetch retrieves repository metrics (stars, maintainers, activity) from GitHub.

Parameters:

  • owner: Repository owner username (e.g., "pallets")
  • repo: Repository name (e.g., "flask")
  • refresh: If true, bypass cache and fetch fresh data

The method performs up to 3 API calls:

  1. Repository metadata (required)
  2. Latest release data (optional, silently ignored if no releases)
  3. Contributors list (optional, top 5, silently ignored on failure)

If refresh is true, the cache is bypassed and a fresh API call is made. If refresh is false, cached data is returned if available and not expired.

Returns:

The returned RepoMetrics pointer is never nil if err is nil. This method is safe for concurrent use.

func (*Client) FetchBatch

func (c *Client) FetchBatch(ctx context.Context, repos []RepoID, refresh bool) (map[string]*integrations.RepoMetrics, error)

FetchBatch retrieves repository metrics for multiple repos in a single GraphQL call. Repos that don't exist or fail are silently omitted from the result. Returns a map keyed by "owner/repo" to RepoMetrics.

If refresh is true, the cache is bypassed for this batch. GraphQL-level errors (partial failures, rate limits) are checked and returned.

func (*Client) FetchContributorsBatch

func (c *Client) FetchContributorsBatch(ctx context.Context, repos []RepoID) map[string][]integrations.Contributor

FetchContributorsBatch fetches contributors for multiple repos concurrently. This uses the REST API (not GraphQL) since contributors aren't available via GraphQL. Repos that fail are silently skipped. Returns a map keyed by "owner/repo".

type CodeSearchResult

type CodeSearchResult struct {
	Name    string   `json:"name"`
	Path    string   `json:"path"`
	Matches []string `json:"matches,omitempty"`
}

CodeSearchResult represents a code search match.

type ContentClient

type ContentClient struct {
	// contains filtered or unexported fields
}

ContentClient provides access to GitHub repository content. Use this for fetching files, listing directories, and user operations.

func NewContentClient

func NewContentClient(token string) *ContentClient

NewContentClient creates a new content client with the given access token.

func (*ContentClient) DetectManifests

func (c *ContentClient) DetectManifests(ctx context.Context, owner, repo, ref string, patterns map[string]string) ([]ManifestFile, error)

DetectManifests finds manifest files in a repository's root directory. The patterns map filename -> language name (e.g., "go.mod" -> "go"). Use deps.SupportedManifests(languages) to get patterns from the deps package. If ref is non-empty, it specifies a branch, tag, or commit SHA.

func (*ContentClient) DetectManifestsRecursive

func (c *ContentClient) DetectManifestsRecursive(ctx context.Context, owner, repo, ref string, patterns map[string]string) ([]ManifestFile, error)

DetectManifestsRecursive finds all manifest files in a repository using the Git tree API. This is useful for monorepos with nested manifest files. The patterns map filename -> language name (e.g., "go.mod" -> "go"). If ref is non-empty, it specifies a branch, tag, or commit SHA.

func (*ContentClient) FetchFile

func (c *ContentClient) FetchFile(ctx context.Context, owner, repo, path, ref string) (*FileContent, error)

FetchFile retrieves the content of a file from a repository. The content is returned as a string (decoded from base64). If ref is non-empty, it specifies a branch, tag, or commit SHA.

func (*ContentClient) FetchFileRaw

func (c *ContentClient) FetchFileRaw(ctx context.Context, owner, repo, path, ref string) (string, error)

FetchFileRaw retrieves the raw content of a file from a repository. This is more efficient for large files as it doesn't use base64 encoding. If ref is non-empty, it specifies a branch, tag, or commit SHA.

func (*ContentClient) FetchUser

func (c *ContentClient) FetchUser(ctx context.Context) (*User, error)

FetchUser retrieves the authenticated user's info.

func (*ContentClient) FetchUserOrgs

func (c *ContentClient) FetchUserOrgs(ctx context.Context) ([]OrgMembership, error)

FetchUserOrgs retrieves the authenticated user's active GitHub organization memberships. Returns only orgs where the membership state is "active" (ignores pending invitations). Each result includes the user's role in the org ("admin" or "member").

func (*ContentClient) FetchUserRepos

func (c *ContentClient) FetchUserRepos(ctx context.Context) ([]Repo, error)

FetchUserRepos retrieves all of the authenticated user's repositories. This includes private repos if the OAuth token has the 'repo' scope. Results are paginated automatically to retrieve all repos.

func (*ContentClient) GetAppInstallations

func (c *ContentClient) GetAppInstallations(ctx context.Context) ([]AppInstallation, error)

GetAppInstallations lists all GitHub App installations accessible to the authenticated user. This can be used to check if the user has installed a specific GitHub App.

func (*ContentClient) GetReadme

func (c *ContentClient) GetReadme(ctx context.Context, owner, repo, ref string) (*FileContent, error)

GetReadme retrieves the README file for a repository. If ref is non-empty, it specifies a branch, tag, or commit SHA. Returns nil, nil if no README is found.

func (*ContentClient) GetRepoInfo

func (c *ContentClient) GetRepoInfo(ctx context.Context, owner, repo string) (*RepoInfo, error)

GetRepoInfo retrieves repository metadata.

func (*ContentClient) GetTree

func (c *ContentClient) GetTree(ctx context.Context, owner, repo, branch string) ([]TreeEntry, error)

GetTree retrieves the full file tree of a repository.

func (*ContentClient) HasAppInstallation

func (c *ContentClient) HasAppInstallation(ctx context.Context, appSlug string) (*AppInstallation, error)

HasAppInstallation checks if the user has installed a GitHub App with the given slug. Returns the installation if found, or nil if not installed.

func (*ContentClient) ListBranches

func (c *ContentClient) ListBranches(ctx context.Context, owner, repo string) ([]Branch, error)

ListBranches retrieves branches for a repository. Results are capped at maxPages * 100 items; large repos may be truncated.

func (*ContentClient) ListContents

func (c *ContentClient) ListContents(ctx context.Context, owner, repo, path, ref string) ([]ContentItem, error)

ListContents lists files and directories in a repository path. If ref is non-empty, it specifies a branch, tag, or commit SHA.

func (*ContentClient) ListTags

func (c *ContentClient) ListTags(ctx context.Context, owner, repo string) ([]Tag, error)

ListTags retrieves tags for a repository. Results are capped at maxPages * 100 items; large repos may be truncated.

func (*ContentClient) ResolveVersionToRef

func (c *ContentClient) ResolveVersionToRef(ctx context.Context, owner, repo, version string) (string, error)

ResolveVersionToRef finds the git ref that best matches a package version. It tries common tag patterns: bare version ("3.1.2"), "v"-prefixed ("v3.1.2"), and repo-prefixed variants ("flask-3.1.2", "flask_3.1.2"). Falls back to the repository's default branch if no tag matches.

This is useful for mapping a package version (from PyPI, npm, etc.) to the corresponding git ref for source code analysis.

Parameters:

  • owner: Repository owner (e.g., "pallets")
  • repo: Repository name (e.g., "flask")
  • version: Package version to match (e.g., "3.1.2")

Returns the matching tag name, or the default branch if no tag matches.

func (*ContentClient) ScanReposForManifests

func (c *ContentClient) ScanReposForManifests(ctx context.Context, manifestPatterns map[string]string, publicOnly bool) ([]RepoWithManifests, error)

ScanReposForManifests fetches repos and detects manifests in parallel. It returns repos sorted by UpdatedAt (most recent first). The manifestPatterns map should be filename -> language (use deps.SupportedManifests). Set publicOnly=true to filter out private repos.

func (*ContentClient) SearchCode

func (c *ContentClient) SearchCode(ctx context.Context, owner, repo, query string) ([]CodeSearchResult, error)

SearchCode searches for code in a repository. Query follows GitHub code search syntax: https://docs.github.com/en/search-github/searching-on-github/searching-code

type ContentItem

type ContentItem struct {
	Name string `json:"name"`
	Path string `json:"path"`
	Type string `json:"type"` // "file" or "dir"
	Size int    `json:"size"`
}

ContentItem represents an item in a repository directory listing.

type DeviceCodeResponse

type DeviceCodeResponse struct {
	DeviceCode      string `json:"device_code"`
	UserCode        string `json:"user_code"`
	VerificationURI string `json:"verification_uri"`
	ExpiresIn       int    `json:"expires_in"`
	Interval        int    `json:"interval"`
}

DeviceCodeResponse contains the response from requesting a device code.

type FileContent

type FileContent struct {
	Path    string `json:"path"`
	Size    int    `json:"size"`
	Content string `json:"content"`
}

FileContent represents the content of a file.

type Installation

type Installation struct {
	ID                  int64               `json:"id"`
	AppID               int64               `json:"app_id"`
	Account             InstallationAccount `json:"account"`
	RepositorySelection string              `json:"repository_selection"` // "all" or "selected"
	Permissions         struct {
		Contents string `json:"contents"` // "read" or "write"
		Metadata string `json:"metadata"` // "read"
	} `json:"permissions"`
}

Installation represents a GitHub App installation.

type InstallationAccount added in v1.6.0

type InstallationAccount struct {
	Login string `json:"login"`
	ID    int64  `json:"id"`
	Type  string `json:"type"` // "User" or "Organization"
}

InstallationAccount identifies the GitHub user or organization that owns an installation.

type InstallationToken

type InstallationToken struct {
	Token     string    `json:"token"`
	ExpiresAt time.Time `json:"expires_at"`
}

InstallationToken represents an installation access token.

type ManifestFile

type ManifestFile struct {
	Path     string `json:"path"`
	Language string `json:"language"`
	Name     string `json:"name"`
}

ManifestFile represents a detected manifest file.

type OAuthClient

type OAuthClient struct {
	// contains filtered or unexported fields
}

OAuthClient handles GitHub OAuth operations using the device flow.

func NewOAuthClient

func NewOAuthClient(config OAuthConfig) *OAuthClient

NewOAuthClient creates a new OAuth client.

func (*OAuthClient) AuthorizationURL

func (c *OAuthClient) AuthorizationURL(state string) string

AuthorizationURL returns the GitHub OAuth authorization URL.

func (*OAuthClient) ExchangeCode

func (c *OAuthClient) ExchangeCode(code string) (*OAuthToken, error)

ExchangeCode exchanges an authorization code for an access token.

func (*OAuthClient) PollForToken

func (c *OAuthClient) PollForToken(ctx context.Context, deviceCode string, interval int) (*OAuthToken, error)

PollForToken polls GitHub for the access token after user authorization. It respects the interval from the device code response. Returns the token when authorized, or an error if expired/denied.

func (*OAuthClient) RequestDeviceCode

func (c *OAuthClient) RequestDeviceCode(ctx context.Context) (*DeviceCodeResponse, error)

RequestDeviceCode initiates the device authorization flow. The user must visit the VerificationURI and enter the UserCode.

func (*OAuthClient) RevokeGrant

func (c *OAuthClient) RevokeGrant(ctx context.Context, accessToken string) error

RevokeGrant revokes the complete OAuth app authorization for a user. This removes the app from the user's "Authorized OAuth Apps" on GitHub, forcing them through the consent flow on next login.

Uses: DELETE /applications/{client_id}/grant Auth: Basic client_id:client_secret Body: {"access_token": "..."}

See: https://docs.github.com/en/rest/apps/oauth-applications#delete-an-app-authorization

type OAuthConfig

type OAuthConfig struct {
	ClientID     string
	ClientSecret string
	RedirectURI  string
}

OAuthConfig holds OAuth configuration.

type OAuthToken

type OAuthToken struct {
	AccessToken string `json:"access_token"`
	TokenType   string `json:"token_type"`
	Scope       string `json:"scope"`
}

OAuthToken represents an OAuth access token response.

type Org

type Org struct {
	ID          int64  `json:"id"`
	Login       string `json:"login"`
	Description string `json:"description"`
	AvatarURL   string `json:"avatar_url"`
}

Org represents a GitHub organization.

type OrgMembership

type OrgMembership struct {
	State string `json:"state"` // "active" or "pending"
	Role  string `json:"role"`  // "admin" or "member"
	Org   Org    `json:"organization"`
}

OrgMembership represents a user's membership in a GitHub organization. Returned by GET /user/memberships/orgs.

type Repo

type Repo struct {
	ID            int64  `json:"id"`
	Name          string `json:"name"`
	FullName      string `json:"full_name"`
	Description   string `json:"description"`
	Private       bool   `json:"private"`
	DefaultBranch string `json:"default_branch"`
	Language      string `json:"language"`
	UpdatedAt     string `json:"updated_at"`
}

Repo represents a GitHub repository.

type RepoID

type RepoID struct {
	Owner string
	Name  string
}

RepoID identifies a GitHub repository for batch fetching.

func (RepoID) Key

func (r RepoID) Key() string

type RepoInfo

type RepoInfo struct {
	Name          string   `json:"name"`
	FullName      string   `json:"full_name"`
	Description   string   `json:"description"`
	Language      string   `json:"language"`
	DefaultBranch string   `json:"default_branch"`
	Stars         int      `json:"stars"`
	Forks         int      `json:"forks"`
	OpenIssues    int      `json:"open_issues"`
	License       string   `json:"license"`
	Topics        []string `json:"topics"`
	Archived      bool     `json:"archived"`
	Private       bool     `json:"private"`
}

RepoInfo contains repository metadata.

type RepoWithManifests

type RepoWithManifests struct {
	Repo      Repo           `json:"repo"`
	Manifests []ManifestFile `json:"manifests,omitempty"`
}

RepoWithManifests holds a repo and its detected manifest files.

type Tag

type Tag struct {
	Name   string `json:"name"`
	Commit string `json:"commit"` // SHA of the tagged commit
}

Tag represents a Git tag in a repository.

type TreeEntry

type TreeEntry struct {
	Path string `json:"path"`
	Type string `json:"type"` // "blob" or "tree"
	Size int    `json:"size,omitempty"`
}

TreeEntry represents a file or directory in the repository tree.

type User

type User struct {
	ID        int64  `json:"id"`
	Login     string `json:"login"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
	Email     string `json:"email"`
}

User represents a GitHub user.

type UserAccessToken

type UserAccessToken struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`
	Scope        string `json:"scope"`
	RefreshToken string `json:"refresh_token,omitempty"`
	ExpiresIn    int    `json:"expires_in,omitempty"`
}

UserAccessToken represents an OAuth token for user authentication.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL