Skip to content

VCS

The pkg/vcs/ directory is split into focused subpackages. Each has a distinct responsibility and can be used independently.

Package Overview

Package Import path Purpose
Release pkg/vcs/release Backend-agnostic Provider/Release/ReleaseAsset interfaces, sentinel errors, and the provider registry
Repo pkg/vcs/repo Git repository operations (local and in-memory) via go-git
GitHub pkg/vcs/github GitHub Enterprise API client and GitHub release provider
GitLab pkg/vcs/gitlab GitLab (and self-managed) release provider
Bitbucket pkg/vcs/bitbucket Bitbucket Cloud Downloads-based release provider (filename-pattern version detection)
Gitea / Codeberg pkg/vcs/gitea Gitea, Forgejo, and Codeberg REST API release provider
Direct HTTP pkg/vcs/direct Direct HTTP release provider for arbitrary download servers

The root pkg/vcs package contains only auth.go โ€” the shared ResolveToken helper used by the GitHub and GitLab subpackages.

Provider Registry

All built-in release providers register themselves at package init time. Consuming code looks up a provider by source type string rather than importing platform packages directly:

factory, err := release.Lookup("gitea")
provider, err := factory(src, cfg)

Blank imports in pkg/setup/providers.go wire all built-in providers automatically โ€” no manual registration is required when using setup.NewUpdater. See Release Provider for the full registry API and how to register a custom provider.

Authentication

vcs.ResolveTokenContext(ctx context.Context, cfg config.Containable, fallbackEnv string) string resolves a token from a config subtree in this order:

  1. auth.env โ€” reads the named environment variable
  2. auth.keychain โ€” "<service>/<account>" reference resolved via credentials.Retrieve; silently skipped when no keychain-capable Backend is registered
  3. auth.value โ€” uses the literal value stored in config
  4. fallbackEnv โ€” falls back to a well-known environment variable (e.g. "GITHUB_TOKEN")

Returns an empty string when nothing is found. Callers decide whether that is an error โ€” public repositories can operate without a token; private repositories will receive a 401. The context is propagated to the credentials backend so remote secret stores (Vault, AWS SSM, 1Password Connect) honour the caller's deadline and cancellation.

import (
    "context"

    "gitlab.com/phpboyscout/go-tool-base/pkg/vcs"
)

// Resolve a GitHub token from props.Config.Sub("github") with the
// cobra command's context.
token := vcs.ResolveTokenContext(cmd.Context(), props.Config.Sub("github"), "GITHUB_TOKEN")

The context-free form vcs.ResolveToken(cfg, fallbackEnv) is preserved as a compatibility shim that internally calls ResolveTokenContext(context.Background(), โ€ฆ). Prefer the context-aware variant anywhere a context is already in scope.

Design Goals

Interface segregation
RepoLike (repo operations) and GitHubClient (API operations) are separate interfaces. Most features only need one of them.
Backend agnosticism for releases
All release providers implement release.Provider. Consuming code (e.g. the auto-update command) depends only on that interface and never imports a platform-specific package directly.
Extensibility
Custom providers can be registered at startup via release.Register. The registry is backed by a sync.RWMutex and safe for concurrent use. See the how-to guide for a full walkthrough.
Testability
All public interfaces have generated mocks in mocks/pkg/vcs/. In-memory git storage (SourceMemory) enables offline integration-style tests. HTTP-based providers use httptest.NewServer for network-free unit tests.
afero integration
Repo.AddToFS bridges go-git object storage into any afero.Fs, so file operations are consistent between production (OS filesystem) and tests (memory-mapped filesystem).