Help us shape the future of Go! Take the 2026 Go Developer Survey

githubingest

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Oct 12, 2026 License: BSD-2-Clause Imports: 10 Imported by: 0

Documentation

Overview

Package githubingest reads individual file contents from a GitHub repository via the GitHub Contents API, authenticated as a GitHub App installation.

This package is optional and separately packaged from pkg/semantics: pkg/githubingest depends on github.com/google/go-github and github.com/bradleyfalzon/ghinstallation/v2, and pkg/semantics never imports pkg/githubingest or these dependencies. Consumers that only analyze raw source bytes never need to build or vendor a GitHub client.

ReadFile issues two Contents API requests per call: the direct file fetch, plus a listing of the file's parent directory so it can detect an in-repo symlink that GitHub's Contents API would otherwise resolve transparently and report as a plain file (see reader.go's rejectIfPathIsSymlink). The second request lists only the immediate parent directory, not a whole-repository tree, so its cost does not scale with repository size.

That directory listing is itself capped at GitHub's documented limit of 1,000 entries per directory: in a parent directory larger than that, a symlink entry can be absent from the (truncated) listing and pass the check undetected, unlike the Git Trees API, which at least reports truncation via a Truncated flag -- the Contents API listing gives no such signal. This is a narrower version of the same class of blind spot as a whole-repository tree walk would have (per-directory, 1,000+ files, rather than whole-repo size), and considered acceptable for v1.

Index

Examples

Constants

View Source
const DefaultCredentialResolverHTTPTimeout = 10 * time.Second

DefaultCredentialResolverHTTPTimeout bounds outbound App-JWT-authenticated HTTP calls (installation resolution, token minting) issued by a CredentialResolver.

View Source
const DefaultGitHubFileReaderHTTPTimeout = 10 * time.Second

DefaultGitHubFileReaderHTTPTimeout bounds outbound Contents API HTTP calls issued by GitHubFileReader (ReadFile and parent-directory listings). Matches DefaultCredentialResolverHTTPTimeout so App-authenticated paths share one production hang bound.

Variables

View Source
var (
	// ErrAuth indicates the GitHub API rejected the request as unauthorized
	// or forbidden (HTTP 401/403).
	ErrAuth = errors.New("githubingest: authentication failed")

	// ErrNotFound indicates the requested path does not exist at the given
	// ref (HTTP 404).
	ErrNotFound = errors.New("githubingest: file not found")

	// ErrUnsupportedContent indicates the requested path resolved to
	// something other than a regular file (a directory, symlink, or
	// submodule).
	ErrUnsupportedContent = errors.New("githubingest: unsupported content type")

	// ErrEmptyContent indicates the file exists but decodes to zero bytes.
	ErrEmptyContent = errors.New("githubingest: file content is empty")

	// ErrTooLarge indicates the file exceeds the GitHub Contents API's
	// 1 MiB size limit.
	ErrTooLarge = errors.New("githubingest: file exceeds maximum supported size")
)

Sentinel errors returned by GitHubFileReader. Callers should use errors.Is to test for these.

Functions

This section is empty.

Types

type CredentialResolver added in v0.2.0

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

CredentialResolver mints GitHub App installation tokens: the single seam ADR-002 rule 5 requires, shared by GitHubFileReader-style fixed-installation reads and internal/authz's dynamic owner/repo -> installation resolution. It never appears without a valid AppID/PrivateKey; it holds no long-lived installation token itself (a fresh mint per InstallationToken call, no caching -- matches ADR-003's "no caching in v1").

func NewCredentialResolver added in v0.2.0

func NewCredentialResolver(cfg CredentialResolverConfig) (*CredentialResolver, error)

NewCredentialResolver builds a CredentialResolver authenticated as the GitHub App described by cfg.

func (*CredentialResolver) InstallationToken added in v0.2.0

func (c *CredentialResolver) InstallationToken(ctx context.Context, installationID int64) (string, error)

InstallationToken mints a fresh installation access token scoped to installationID via POST /app/installations/{id}/access_tokens. Maps errors the same way as ResolveInstallationID.

func (*CredentialResolver) ResolveInstallationID added in v0.2.0

func (c *CredentialResolver) ResolveInstallationID(ctx context.Context, owner, repo string) (int64, error)

ResolveInstallationID resolves the installation governing owner/repo via GET /repos/{owner}/{repo}/installation (App JWT auth). Returns ErrNotFound on GitHub 404 (deliberately covers both "no such repo" and "App not installed there" -- ADR-003) and ErrAuth on 401/403. Any other failure is returned wrapped, unmatched to a sentinel (caller treats as transient/503).

type CredentialResolverConfig added in v0.2.0

type CredentialResolverConfig struct {
	AppID      int64
	PrivateKey []byte            // PEM (PKCS#1), never logged
	BaseURL    string            // optional; GitHub Enterprise
	Transport  http.RoundTripper // optional; base transport (tests)
}

CredentialResolverConfig is GitHub App authentication for a CredentialResolver.

type FileFilter added in v0.2.0

type FileFilter func(path string) bool

FileFilter reports whether path should be included in a ListFiles result. pkg/githubingest never imports pkg/semantics, so callers that want to restrict a listing to specific source languages (e.g. ".go", ".ts", ".tsx") must supply their own filter rather than relying on any built-in language list.

type FileMetadata

type FileMetadata struct {
	Path string `json:"path"`
	Ref  string `json:"ref"`
	SHA  string `json:"sha"`
	Size int    `json:"size"`
}

FileMetadata describes a file read via GitHubFileReader.ReadFile.

type GitHubAppConfig

type GitHubAppConfig struct {
	AppID          int64
	InstallationID int64
	PrivateKey     []byte            // PEM (PKCS#1) as issued by GitHub; never logged
	BaseURL        string            // optional; GitHub Enterprise
	Transport      http.RoundTripper // optional; base transport (tests, future rate limiting)
}

GitHubAppConfig is GitHub App authentication for GitHubFileReader.

type GitHubFileReader

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

GitHubFileReader reads file contents from GitHub repositories, authenticated as a GitHub App installation.

func NewGitHubFileReader

func NewGitHubFileReader(cfg GitHubAppConfig) (*GitHubFileReader, error)

NewGitHubFileReader builds a GitHubFileReader authenticated as the GitHub App installation described by cfg.

Example

ExampleNewGitHubFileReader shows constructing a GitHubFileReader from a GitHub App's credentials. Construction never touches the network - authentication happens lazily on the first ReadFile call. The private key below is generated on the spot purely so this example runs offline and deterministically; a real caller would load their GitHub App's issued PEM key instead.

package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"

	"github.com/lousy-agents/coach/pkg/githubingest"
)

func main() {
	key, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		fmt.Println(err)
		return
	}
	pemKey := pem.EncodeToMemory(&pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(key),
	})

	reader, err := githubingest.NewGitHubFileReader(githubingest.GitHubAppConfig{
		AppID:          123,
		InstallationID: 456,
		PrivateKey:     pemKey,
	})
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(reader != nil)
}
Output:
true

func NewGitHubFileReaderFromToken added in v0.2.0

func NewGitHubFileReaderFromToken(token, baseURL string) (*GitHubFileReader, error)

NewGitHubFileReaderFromToken builds a GitHubFileReader authenticated with a pre-minted installation access token (from CredentialResolver.InstallationToken). baseURL is optional (GitHub Enterprise). Prefer this over NewGitHubFileReader when the caller already holds a token via the ADR-002 CredentialResolver seam.

func (*GitHubFileReader) ListFiles added in v0.2.0

func (r *GitHubFileReader) ListFiles(ctx context.Context, ref GitHubTreeRef, opts TreeListOptions) ([]TreeEntry, error)

ListFiles recursively walks ref's repository tree via the Contents API, returning every regular file matching opts.Filter. An empty repository or an empty matching set returns an empty, non-nil slice and a nil error.

The walk skips symlink and submodule entries entirely rather than following them, mirroring rejectIfPathIsSymlink's concern in reader.go: a symlink or submodule could otherwise be used to escape the repository boundary or double-count content.

func (*GitHubFileReader) ReadFile

func (r *GitHubFileReader) ReadFile(ctx context.Context, ref GitHubFileRef) ([]byte, FileMetadata, error)

ReadFile fetches the raw bytes and metadata of a single file at ref.

func (*GitHubFileReader) ResolveCommitSHA added in v0.2.0

func (r *GitHubFileReader) ResolveCommitSHA(ctx context.Context, owner, repo, ref string) (string, error)

ResolveCommitSHA resolves ref to a commit object SHA for owner/repo. Empty ref resolves the repository default branch tip (not the literal "HEAD"). Branch names, tags, and already-resolved SHAs are accepted via the Commits API.

type GitHubFileRef

type GitHubFileRef struct{ Owner, Repo, Ref, Path string }

GitHubFileRef identifies a single file within a repository at a ref.

type GitHubTreeRef added in v0.2.0

type GitHubTreeRef struct{ Owner, Repo, Ref string }

GitHubTreeRef identifies a repository at a ref whose file tree should be listed, as opposed to GitHubFileRef's single path.

type TreeEntry added in v0.2.0

type TreeEntry struct {
	Path string `json:"path"`
	SHA  string `json:"sha"`
	Size int    `json:"size"`
}

TreeEntry describes one matching file discovered by GitHubFileReader.ListFiles.

type TreeListOptions added in v0.2.0

type TreeListOptions struct {
	// Filter, if non-nil, restricts the result to paths for which it
	// returns true. A nil Filter matches every regular file.
	Filter FileFilter

	// MaxFiles caps the number of matching files collected before the walk
	// aborts with an error wrapping ErrTooLarge. Zero means unlimited.
	MaxFiles int

	// MaxTotalBytes caps the sum of matching files' reported sizes before
	// the walk aborts with an error wrapping ErrTooLarge. Zero means
	// unlimited. Sizes come from the Contents API's directory-listing
	// "size" field, so this budget is checked without fetching any file's
	// content.
	MaxTotalBytes int64
}

TreeListOptions is GitHubFileReader.ListFiles's tree-walk budget and filter.

Jump to

Keyboard shortcuts

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