provider

package
v0.37.0 Latest Latest
Warning

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

Go to latest
Published: Aug 12, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DefaultPage    = 1
	DefaultPerPage = 20
	MaxPerPage     = 100
)

Variables

View Source
var (
	ErrNotFound             = errors.New("resource not found")
	ErrAuthentication       = errors.New("authentication failed")
	ErrRateLimited          = errors.New("rate limited")
	ErrForbidden            = errors.New("forbidden")
	ErrConflict             = errors.New("conflict")
	ErrNotImplemented       = errors.New("not implemented")
	ErrInvalidInput         = errors.New("invalid input")
	ErrWebhookValidation    = errors.New("webhook validation failed")
	ErrConnectionFailed     = errors.New("connection failed")
	ErrPlatformNotSupported = errors.New("platform not supported")
)

Sentinel errors classify transport and provider failures so callers can branch on category without inspecting status codes or wrapped causes.

Functions

func BuildRawDiff

func BuildRawDiff(files []*ChangedFile) string

BuildRawDiff constructs a raw diff string from a list of ChangedFiles.

func ClassifyStatus

func ClassifyStatus(statusCode int) error

ClassifyStatus maps an HTTP status code to a sentinel error. Exposed so transports and platform implementations can produce consistent error categories without importing each other.

func CountDiffLines

func CountDiffLines(diff string) (additions, deletions int)

CountDiffLines counts additions and deletions in a unified diff string.

func ExtractOwnerFromFullName

func ExtractOwnerFromFullName(fullName string) string

ExtractOwnerFromFullName returns just the owner portion of "owner/repo".

func HashToken

func HashToken(token string) string

HashToken returns the first 16 hex characters of SHA-256(token). It is the default token hasher used by Manager and is exported so callers can compute cache keys for diagnostics or external caches.

An empty token hashes to an empty string (no anonymous cache entries).

func IsAuthentication

func IsAuthentication(err error) bool

IsAuthentication reports whether err wraps ErrAuthentication (HTTP 401).

func IsConflict

func IsConflict(err error) bool

IsConflict reports whether err wraps ErrConflict (HTTP 409).

func IsForbidden

func IsForbidden(err error) bool

IsForbidden reports whether err wraps ErrForbidden (HTTP 403).

func IsInvalidInput

func IsInvalidInput(err error) bool

IsInvalidInput reports whether err wraps ErrInvalidInput.

func IsNotFound

func IsNotFound(err error) bool

IsNotFound reports whether err wraps ErrNotFound (HTTP 404).

func IsNotImplemented

func IsNotImplemented(err error) bool

IsNotImplemented reports whether err wraps ErrNotImplemented.

func IsPlatformNotSupported

func IsPlatformNotSupported(err error) bool

IsPlatformNotSupported reports whether err wraps ErrPlatformNotSupported.

func IsRateLimited

func IsRateLimited(err error) bool

IsRateLimited reports whether err wraps ErrRateLimited (HTTP 429).

func IsRegistered

func IsRegistered(p Platform) bool

IsRegistered checks if a platform has been registered.

func IsWebhookValidation

func IsWebhookValidation(err error) bool

IsWebhookValidation reports whether err wraps ErrWebhookValidation.

func New

func New(platform Platform, op string, status int, body string) error

New builds a ProviderError directly from the given status code.

func NormalizePageOpts

func NormalizePageOpts(page, perPage int) (int, int)

NormalizePageOpts applies default values for page/perPage.

func ParseTotalCountHeader

func ParseTotalCountHeader(headers http.Header, fallback int) int

ParseTotalCountHeader reads X-Total-Count or X-Total from response headers. Falls back to the provided default if neither header is present or valid.

func ReadAndRestoreBody

func ReadAndRestoreBody(r *http.Request) ([]byte, error)

ReadAndRestoreBody reads the full body of r and replaces it with a fresh NopCloser so that downstream readers (signature verification, JSON decoding) can still consume it. Use it at the entry point of webhook handlers that need to inspect the raw bytes.

func Register

func Register(p Platform, ctor ProviderConstructor)

Register registers a provider constructor for a platform. This is typically called from init() functions in platform implementation files.

func ResolveMRSHAs

func ResolveMRSHAs(diffRefsHead, diffRefsBase, diffRefsStart, mergeCommitSHA, lastCommitID string) (head, base, start string)

ResolveMRSHAs derives the (head, base, start) SHAs for a GitLab-style merge request from the raw webhook fields. This encodes the shared priority used by the GitLab, TencentCode (and future GitCode) backends:

  • head: diff_refs.head_sha when present, otherwise last_commit.id
  • base: merge_commit_sha when present, otherwise diff_refs.base_sha
  • start: diff_refs.start_sha (may be empty)

Keeping this in one place guarantees identical fallback semantics across the GitLab-family backends and avoids behavioural drift.

func SplitFullName

func SplitFullName(fullName string) (owner, name string)

SplitFullName splits "owner/repo" into (owner, repo). If the input doesn't contain "/", owner is empty.

func SumDiffStats

func SumDiffStats(files []*ChangedFile) (additions, deletions int)

SumDiffStats returns total additions and deletions from a list of ChangedFiles.

func Wrap

func Wrap(platform Platform, op string, err error) error

Wrap creates a ProviderError from a raw error, classifying it when the cause is a transport error with a known status code. Use this in platform implementations to convert transport errors into the unified shape.

func WrapStatusError

func WrapStatusError(err error, statusCode int) error

WrapStatusError wraps an error with an explicit HTTP status code. This is the preferred way to attach status codes to third-party SDK errors instead of relying on reflection-based detection.

func Wrapf

func Wrapf(platform Platform, op, format string, args ...any) error

Wrapf is a convenience for creating a ProviderError with a formatted message. The format is intentionally simple ("%s/%s") so platform code can embed the resource identifier without re-implementing the prefix logic.

Types

type BranchManager

type BranchManager interface {
	ListBranches(ctx context.Context, owner, repo string) ([]*PlatformBranch, error)
	CreateBranch(ctx context.Context, owner, repo, branch, ref string) (*PlatformBranch, error)
	DeleteBranch(ctx context.Context, owner, repo, branch string) error
}

BranchManager handles branch operations.

type CRComment

type CRComment struct {
	ID        int64     `json:"id"`
	Body      string    `json:"body"`
	Author    *CRUser   `json:"author"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

CRComment represents a comment on a change request.

type CRCommit

type CRCommit struct {
	SHA       string    `json:"sha"`
	Message   string    `json:"message"`
	Author    *CRUser   `json:"author"`
	CreatedAt time.Time `json:"created_at"`
}

CRCommit represents a commit in a change request.

type CRState

type CRState string

CRState represents the state of a change request.

const (
	CRStateOpened CRState = "opened"
	CRStateMerged CRState = "merged"
	CRStateClosed CRState = "closed"
)

func MapBoolStateToCR

func MapBoolStateToCR(state string, merged bool) CRState

MapBoolStateToCR is a convenience for platforms with a separate merged boolean (Gitea, Forgejo).

func MapMRStateToCR

func MapMRStateToCR(state string) CRState

MapMRStateToCR is a convenience for platforms with explicit "merged" state (GitLab, Tencent Code).

func MapStateToCR

func MapStateToCR(state string, mergedFn func() bool) CRState

MapStateToCR maps common string state representations to CRState. mergedFn is called when state is "closed" to determine if it was merged. For platforms that use a separate "merged" field (Gitea, Forgejo), pass a non-nil mergedFn. For platforms where "merged" is a distinct state string (GitLab, Tencent Code), pass nil.

type CRUser

type CRUser struct {
	ID        int64  `json:"id"`
	Username  string `json:"username"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
}

CRUser represents a user on a platform.

type ChangeRequest

type ChangeRequest struct {
	ID           int64   `json:"id"`
	Number       int     `json:"number"`
	Title        string  `json:"title"`
	Description  string  `json:"description"`
	State        CRState `json:"state"`
	SourceBranch string  `json:"source_branch"`
	TargetBranch string  `json:"target_branch"`
	// HeadSHA is the SHA of the source-branch tip.
	// BaseSHA is the diff base: the merge-base (common ancestor) where GitLab/TencentCode
	// expose it via diff_refs.base_sha. GitHub/Gitea do not expose a distinct merge base in
	// webhook payloads, so BaseSHA there is the target-branch tip (equivalent to StartSHA).
	// StartSHA is the SHA of the target-branch tip at event time (GitLab diff_refs.start_sha).
	// On GitHub/Gitea it equals BaseSHA since no separate value is exposed.
	HeadSHA  string `json:"head_sha,omitempty"`
	BaseSHA  string `json:"base_sha,omitempty"`
	StartSHA string `json:"start_sha,omitempty"`
	// Draft reports the work-in-progress / draft state uniformly across platforms
	// (GitHub pr.draft, Gitea draft, GitLab/TencentCode work_in_progress).
	Draft       bool      `json:"draft"`
	Author      *CRUser   `json:"author"`
	Reviewers   []*CRUser `json:"reviewers"`
	Labels      []string  `json:"labels"`
	MergeStatus string    `json:"merge_status"`
	WebURL      string    `json:"web_url"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
}

ChangeRequest represents a pull request or merge request.

type ChangeRequestManager

type ChangeRequestManager interface {
	CreateCR(ctx context.Context, opts CreateCROptions) (*ChangeRequest, error)
	GetCR(ctx context.Context, owner, repo string, number int) (*ChangeRequest, error)
	ListCRs(ctx context.Context, opts ListCROptions) ([]*ChangeRequest, int, error)
	MergeCR(ctx context.Context, owner, repo string, number int, opts MergeCROptions) (*ChangeRequest, error)
	CloseCR(ctx context.Context, owner, repo string, number int) (*ChangeRequest, error)
	ReopenCR(ctx context.Context, owner, repo string, number int) (*ChangeRequest, error)
	UpdateCR(ctx context.Context, owner, repo string, number int, opts UpdateCROptions) (*ChangeRequest, error)
	UpdateCRLabels(ctx context.Context, owner, repo string, number int, labels []string) error
	ListCRComments(ctx context.Context, owner, repo string, number int) ([]*CRComment, error)
	ListCRCommits(ctx context.Context, owner, repo string, number int) ([]*CRCommit, error)
}

ChangeRequestManager handles pull request / merge request lifecycle.

type ChangedFile

type ChangedFile struct {
	OldPath   string `json:"old_path"`
	NewPath   string `json:"new_path"`
	Diff      string `json:"diff"`
	Additions int    `json:"additions"`
	Deletions int    `json:"deletions"`
	IsNew     bool   `json:"new_file"`
	IsDeleted bool   `json:"deleted_file"`
	IsRenamed bool   `json:"renamed_file"`
	IsBinary  bool   `json:"binary"`
}

ChangedFile represents a file changed in a change request.

type CommitInfo

type CommitInfo struct {
	SHA       string    `json:"sha"`
	Message   string    `json:"message"`
	Author    *CRUser   `json:"author"`
	Committer *CRUser   `json:"committer"`
	CreatedAt time.Time `json:"created_at"`
	Additions int       `json:"additions"`
	Deletions int       `json:"deletions"`
}

CommitInfo represents a commit.

type CommitManager

type CommitManager interface {
	GetCommit(ctx context.Context, owner, repo, sha string) (*CommitInfo, error)
	ListCommits(ctx context.Context, owner, repo string, opts ListCommitsOptions) ([]*CommitInfo, error)
	CompareCommits(ctx context.Context, owner, repo, base, head string) (*CompareResult, error)
	CreateCommitStatus(ctx context.Context, owner, repo, sha string, opts CommitStatusOptions) error
}

CommitManager handles commit operations.

type CommitStatusOptions

type CommitStatusOptions struct {
	State       string `json:"state"`
	Context     string `json:"context"`
	Description string `json:"description,omitempty"`
	TargetURL   string `json:"target_url,omitempty"`
}

CommitStatusOptions contains options for creating a commit status.

type CompareResult

type CompareResult struct {
	Commits      []*CommitInfo  `json:"commits"`
	Files        []*ChangedFile `json:"files"`
	TotalCommits int            `json:"total_commits"`
	AheadBy      int            `json:"ahead_by"`
	BehindBy     int            `json:"behind_by"`
}

CompareResult represents the result of comparing two commits.

type Config

type Config struct {
	Platform Platform
	BaseURL  string
	Token    string
	SkipTLS  bool

	// Logger for provider operations. Defaults to a no-op logger.
	Logger Logger
	// RetryConfig for automatic retry on transient failures. nil means no retry.
	RetryConfig *RetryConfig
	// Hooks for request/response lifecycle interception.
	Hooks *Hooks
}

Config holds the configuration for creating a Provider.

type CreateCROptions

type CreateCROptions struct {
	Owner              string   `json:"owner"`
	Repo               string   `json:"repo"`
	Title              string   `json:"title"`
	Description        string   `json:"description,omitempty"`
	SourceBranch       string   `json:"source_branch"`
	TargetBranch       string   `json:"target_branch"`
	Labels             []string `json:"labels,omitempty"`
	RemoveSourceBranch bool     `json:"remove_source_branch,omitempty"`
}

CreateCROptions contains options for creating a change request.

type CreateIssueOptions

type CreateIssueOptions struct {
	Owner     string   `json:"owner"`
	Repo      string   `json:"repo"`
	Title     string   `json:"title"`
	Body      string   `json:"body,omitempty"`
	Assignees []string `json:"assignees,omitempty"`
	Labels    []string `json:"labels,omitempty"`
}

CreateIssueOptions contains options for creating an issue.

type CreateReleaseOptions

type CreateReleaseOptions struct {
	TagName    string `json:"tag_name"`
	Target     string `json:"target,omitempty"`
	Title      string `json:"title"`
	Body       string `json:"body,omitempty"`
	Draft      bool   `json:"draft,omitempty"`
	Prerelease bool   `json:"prerelease,omitempty"`
}

CreateReleaseOptions contains options for creating a release.

type CreateRepoOptions

type CreateRepoOptions struct {
	Name          string `json:"name"`
	Description   string `json:"description,omitempty"`
	Private       bool   `json:"private,omitempty"`
	AutoInit      bool   `json:"auto_init,omitempty"`
	DefaultBranch string `json:"default_branch,omitempty"`
}

CreateRepoOptions contains options for creating a repository.

type CreateReviewOptions

type CreateReviewOptions struct {
	CommitID string          `json:"commit_id,omitempty"`
	Event    string          `json:"event,omitempty"`
	Body     string          `json:"body,omitempty"`
	Comments []ReviewComment `json:"comments,omitempty"`
}

CreateReviewOptions contains options for creating a review.

type CreateWebhookOptions

type CreateWebhookOptions struct {
	Owner  string   `json:"owner"`
	Repo   string   `json:"repo"`
	URL    string   `json:"url"`
	Secret string   `json:"secret,omitempty"`
	Events []string `json:"events,omitempty"`
}

CreateWebhookOptions contains options for creating a webhook.

type DetectResult

type DetectResult struct {
	Platform Platform
	Owner    string
	Repo     string
	BaseURL  string
}

DetectResult holds the platform, owner, repo, and base API URL extracted from a git remote URL by DetectPlatform.

func DetectPlatform

func DetectPlatform(remoteURL string) (*DetectResult, error)

DetectPlatform parses a git remote URL (HTTPS, SSH, or ssh://) and returns the detected platform, owner, repo name, and base API URL. Returns ErrPlatformNotSupported for unrecognized hosts; use NewProvider with explicit Config for self-hosted instances not in the known-host list.

type DiffManager

type DiffManager interface {
	GetCRDiff(ctx context.Context, owner, repo string, number int) (*MergeDiff, error)
	GetCRFiles(ctx context.Context, owner, repo string, number int) ([]*ChangedFile, error)
	CreateNote(ctx context.Context, owner, repo string, number int, body string) (string, error)
	DeleteNote(ctx context.Context, owner, repo string, number int, noteID string) error
	CreateDiscussion(ctx context.Context, owner, repo string, number int, opts DiscussionOptions) (string, error)
	CreateReview(ctx context.Context, owner, repo string, number int, opts CreateReviewOptions) (*ReviewResult, error)
}

DiffManager handles diff, review, and discussion operations.

type DiscussionOptions

type DiscussionOptions struct {
	Body         string `json:"body"`
	FilePath     string `json:"file_path,omitempty"`
	NewLine      int    `json:"new_line,omitempty"`
	OldLine      int    `json:"old_line,omitempty"`
	StartNewLine int    `json:"start_new_line,omitempty"`
	BaseSHA      string `json:"base_sha,omitempty"`
	StartSHA     string `json:"start_sha,omitempty"`
	HeadSHA      string `json:"head_sha,omitempty"`
}

DiscussionOptions contains options for creating a discussion comment.

type EventRepo

type EventRepo struct {
	ID       int64  `json:"id"`
	FullName string `json:"full_name"`
	Owner    string `json:"owner"`
	Name     string `json:"name"`
}

EventRepo represents the repository in a webhook event.

func BuildEventRepo

func BuildEventRepo(fullName string) *EventRepo

BuildEventRepo creates an EventRepo from a full name string.

type FileDeleteOptions

type FileDeleteOptions struct {
	Path    string `json:"path"`
	Message string `json:"message"`
	Branch  string `json:"branch,omitempty"`
	SHA     string `json:"sha,omitempty"`
	Author  string `json:"author,omitempty"`
	Email   string `json:"email,omitempty"`
}

FileDeleteOptions contains options for deleting a file.

type FileManager

type FileManager interface {
	GetFileContent(ctx context.Context, owner, repo, path, ref string) (string, error)
	CreateFile(ctx context.Context, owner, repo string, opts FileOptions) (*FileResult, error)
	UpdateFile(ctx context.Context, owner, repo string, opts FileOptions) (*FileResult, error)
	DeleteFile(ctx context.Context, owner, repo string, opts FileDeleteOptions) (*FileResult, error)
}

FileManager handles file CRUD operations on repositories.

type FileOptions

type FileOptions struct {
	Path    string `json:"path"`
	Content string `json:"content"`
	Message string `json:"message"`
	Branch  string `json:"branch,omitempty"`
	SHA     string `json:"sha,omitempty"`
	Author  string `json:"author,omitempty"`
	Email   string `json:"email,omitempty"`
}

FileOptions contains options for creating or updating a file.

type FileResult

type FileResult struct {
	SHA       string `json:"sha,omitempty"`
	CommitSHA string `json:"commit_sha,omitempty"`
}

FileResult is the result of a file operation.

type ForkRepoOptions

type ForkRepoOptions struct {
	Organization string `json:"organization,omitempty"`
	Name         string `json:"name,omitempty"`
}

ForkRepoOptions contains options for forking a repository.

type HMACSHA256Validator

type HMACSHA256Validator struct {
	Header string
}

HMACSHA256Validator verifies a "Header: sha256=<hex>" style signature using HMAC-SHA256 over the request body. The expected header is configurable so it works for GitHub (X-Hub-Signature-256), Gitea (X-Gitea-Signature), Gitee (X-Gitee-Token) and others.

func (HMACSHA256Validator) Name

func (HMACSHA256Validator) Name() string

Name implements WebhookValidator.

func (HMACSHA256Validator) Validate

func (h HMACSHA256Validator) Validate(r *http.Request, body []byte, secret string) error

Validate implements WebhookValidator. The signature must be exactly "sha256=<hex>" or just "<hex>". Comparison is done in constant time.

type HmacValidator

type HmacValidator struct {
	Header    string
	Algorithm string // "sha1", "sha256", "sha512"
	Prefix    string // expected signature prefix ("sha256=")
}

HmacValidator is a lower-level helper that supports arbitrary hash algorithms. It is intended for use by platform implementations that need something other than SHA-256.

func (*HmacValidator) Name

func (h *HmacValidator) Name() string

Name implements WebhookValidator.

func (*HmacValidator) Validate

func (h *HmacValidator) Validate(r *http.Request, body []byte, secret string) error

Validate implements WebhookValidator.

type Hooks

type Hooks struct {
	Request  []RequestHook
	Response []ResponseHook
}

Hooks holds request and response lifecycle hooks. These are mapped into transport.Hooks by each backend's constructor; direct callers should use the Hooks struct to register hooks via provider.Config.

func (*Hooks) AddRequestHook

func (h *Hooks) AddRequestHook(hook RequestHook)

AddRequestHook appends a request hook.

func (*Hooks) AddResponseHook

func (h *Hooks) AddResponseHook(hook ResponseHook)

AddResponseHook appends a response hook.

type Issue

type Issue struct {
	ID        int64      `json:"id"`
	Number    int        `json:"number"`
	Title     string     `json:"title"`
	Body      string     `json:"body"`
	State     IssueState `json:"state"`
	Author    *CRUser    `json:"author,omitempty"`
	Labels    []string   `json:"labels,omitempty"`
	Assignees []string   `json:"assignees,omitempty"`
	Milestone string     `json:"milestone,omitempty"`
	WebURL    string     `json:"web_url,omitempty"`
	CreatedAt time.Time  `json:"created_at"`
	UpdatedAt time.Time  `json:"updated_at"`
	ClosedAt  *time.Time `json:"closed_at,omitempty"`
}

Issue represents an issue on a platform.

type IssueComment

type IssueComment struct {
	ID        int64     `json:"id"`
	Body      string    `json:"body"`
	Author    *CRUser   `json:"author,omitempty"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

IssueComment represents a comment on an issue.

type IssueLabel

type IssueLabel struct {
	ID    int64  `json:"id"`
	Name  string `json:"name"`
	Color string `json:"color,omitempty"`
}

IssueLabel represents a label on a repository.

type IssueManager

type IssueManager interface {
	ListIssues(ctx context.Context, opts ListIssuesOptions) ([]*Issue, int, error)
	GetIssue(ctx context.Context, owner, repo string, number int) (*Issue, error)
	CreateIssue(ctx context.Context, opts CreateIssueOptions) (*Issue, error)
	UpdateIssue(ctx context.Context, owner, repo string, number int, opts UpdateIssueOptions) (*Issue, error)
	CloseIssue(ctx context.Context, owner, repo string, number int) (*Issue, error)
	ReopenIssue(ctx context.Context, owner, repo string, number int) (*Issue, error)
	ListIssueComments(ctx context.Context, owner, repo string, number int) ([]*IssueComment, error)
	CreateIssueComment(ctx context.Context, owner, repo string, number int, body string) (*IssueComment, error)
	ListIssueLabels(ctx context.Context, owner, repo string) ([]*IssueLabel, error)
	AddIssueLabels(ctx context.Context, owner, repo string, number int, labels []string) error
	RemoveIssueLabel(ctx context.Context, owner, repo string, number int, name string) error
}

IssueManager provides issue CRUD, comments, and label management.

type IssueState

type IssueState string

IssueState represents the state of an issue.

const (
	IssueStateOpen   IssueState = "open"
	IssueStateClosed IssueState = "closed"
)

type ListCROptions

type ListCROptions struct {
	Owner        string  `json:"owner"`
	Repo         string  `json:"repo"`
	State        CRState `json:"state,omitempty"`
	SourceBranch string  `json:"source_branch,omitempty"`
	TargetBranch string  `json:"target_branch,omitempty"`
	Page         int     `json:"page,omitempty"`
	PerPage      int     `json:"per_page,omitempty"`
}

ListCROptions contains options for listing change requests.

type ListCommitsOptions

type ListCommitsOptions struct {
	Page    int    `json:"page,omitempty"`
	PerPage int    `json:"per_page,omitempty"`
	Branch  string `json:"branch,omitempty"`
	Since   string `json:"since,omitempty"` // RFC3339
	Until   string `json:"until,omitempty"` // RFC3339
}

ListCommitsOptions contains options for listing commits in a repository.

type ListIssuesOptions

type ListIssuesOptions struct {
	Owner    string     `json:"owner"`
	Repo     string     `json:"repo"`
	State    IssueState `json:"state,omitempty"`
	Assignee string     `json:"assignee,omitempty"`
	Labels   string     `json:"labels,omitempty"`
	Page     int        `json:"page,omitempty"`
	PerPage  int        `json:"per_page,omitempty"`
}

ListIssuesOptions contains options for listing issues.

type ListRepoOptions

type ListRepoOptions struct {
	Owner   string `json:"owner,omitempty"`
	Page    int    `json:"page,omitempty"`
	PerPage int    `json:"per_page,omitempty"`
}

ListRepoOptions contains options for listing repositories on a platform.

type Logger

type Logger interface {
	Debug(msg string, keysAndValues ...any)
	Info(msg string, keysAndValues ...any)
	Warn(msg string, keysAndValues ...any)
	Error(msg string, keysAndValues ...any)
}

Logger is a minimal logging interface compatible with most Go logging libraries. Implementations include slog, zap, zerolog, logrus, etc.

func NewNoopLogger

func NewNoopLogger() Logger

NewNoopLogger returns a Logger that discards all output.

type Manager

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

Manager provides a caching layer over Provider creation.

It automatically detects the platform from clone URLs and reuses existing Provider instances within the TTL window. The cache key is derived from platform + baseURL + a SHA-256 hash of the token, so different tokens map to different entries without leaking the token itself in logs or memory dumps.

func NewManager

func NewManager(ttl time.Duration, opts ...ManagerOption) *Manager

NewManager creates a new Provider Manager with the given TTL. A TTL of 0 means providers never expire (until the process exits).

func (*Manager) Cleanup

func (m *Manager) Cleanup()

Cleanup removes expired entries from the cache. Safe to call manually; also invoked periodically by StartJanitor.

func (*Manager) Get

func (m *Manager) Get(cfg Config) (Provider, error)

Get returns a cached or newly created Provider for the given config. The cache key is platform + baseURL + hash(token), so the same (platform, baseURL) with a different token gets a distinct entry.

func (*Manager) GetByURL

func (m *Manager) GetByURL(cloneURL, token string) (Provider, error)

GetByURL detects the platform from the clone URL and returns a cached or newly created Provider.

func (*Manager) Len

func (m *Manager) Len() int

Len returns the number of cached Providers.

func (*Manager) Purge

func (m *Manager) Purge()

Purge removes all cached Providers.

func (*Manager) Remove

func (m *Manager) Remove(cfg Config)

Remove removes a cached Provider by config.

func (*Manager) ResetStats

func (m *Manager) ResetStats()

ResetStats zeroes the hit/miss/eviction counters. Cache entries are not affected.

func (*Manager) StartJanitor

func (m *Manager) StartJanitor(ctx context.Context, interval time.Duration)

StartJanitor launches a background goroutine that calls Cleanup every interval until ctx is cancelled or Stop is called. Calling StartJanitor more than once without an intervening Stop is a no-op.

func (*Manager) Stats

func (m *Manager) Stats() Stats

Stats returns a snapshot of cache counters. The counters continue to accumulate across calls; reset them with ResetStats.

func (*Manager) Stop

func (m *Manager) Stop()

Stop halts the background janitor and blocks until it has exited.

type ManagerOption

type ManagerOption func(*Manager)

ManagerOption configures a Manager at construction time.

func WithHasher

func WithHasher(h func(token string) string) ManagerOption

WithHasher overrides the default SHA-256 token hasher. Useful for tests that want deterministic, human-readable keys.

func WithMaxSize

func WithMaxSize(n int) ManagerOption

WithMaxSize caps the cache at n entries. When the cap is reached, the oldest entry is evicted before a new one is inserted.

type MergeCROptions

type MergeCROptions struct {
	MergeCommitMessage string `json:"merge_commit_message,omitempty"`
	Squash             bool   `json:"squash,omitempty"`
	RemoveSourceBranch bool   `json:"remove_source_branch,omitempty"`
}

MergeCROptions contains options for merging a change request.

type MergeDiff

type MergeDiff struct {
	Files    []*ChangedFile
	TotalAdd int
	TotalDel int
	RawDiff  string
}

MergeDiff represents the diff of a change request.

type NormalizedEvent

type NormalizedEvent struct {
	ID         string          `json:"id"`
	Type       string          `json:"type"`
	Source     Platform        `json:"source"`
	Timestamp  time.Time       `json:"timestamp"`
	Actor      *CRUser         `json:"actor"`
	Repo       *EventRepo      `json:"repo"`
	CR         *ChangeRequest  `json:"cr,omitempty"`
	Branch     string          `json:"branch,omitempty"`
	Tag        string          `json:"tag,omitempty"`
	CommitSHA  string          `json:"commit_sha,omitempty"`
	Action     string          `json:"action,omitempty"`
	RawPayload json.RawMessage `json:"raw_payload"`
}

NormalizedEvent represents a normalized webhook event from any platform.

type Platform

type Platform string

Platform represents a Git hosting platform.

const (
	PlatformGitLab      Platform = "gitlab"
	PlatformGitHub      Platform = "github"
	PlatformGitea       Platform = "gitea"
	PlatformGitee       Platform = "gitee"
	PlatformForgejo     Platform = "forgejo"
	PlatformTencentCode Platform = "tencent_code"
	PlatformGitCode     Platform = "gitcode"
)

func RegisteredPlatforms

func RegisteredPlatforms() []Platform

RegisteredPlatforms returns a list of all registered platforms.

type PlatformBranch

type PlatformBranch struct {
	Name string `json:"name"`
}

PlatformBranch represents a branch on a platform.

type PlatformRepo

type PlatformRepo struct {
	ID            int64    `json:"id"`
	FullName      string   `json:"full_name"`
	Name          string   `json:"name"`
	Owner         string   `json:"owner"`
	Description   string   `json:"description"`
	CloneURL      string   `json:"clone_url"`
	SSHURL        string   `json:"ssh_url"`
	DefaultBranch string   `json:"default_branch"`
	Private       bool     `json:"private"`
	Platform      Platform `json:"platform"`
}

PlatformRepo represents a repository on a platform.

type PlatformWebhook

type PlatformWebhook struct {
	ID     int64    `json:"id"`
	URL    string   `json:"url"`
	Events []string `json:"events"`
}

PlatformWebhook represents a webhook on a platform.

type Provider

type Provider interface {
	// Platform returns the platform type.
	Platform() Platform
	// TestConnection verifies the connection and checks capabilities.
	TestConnection(ctx context.Context) (*TestConnectionResult, error)

	RepoManager
	ChangeRequestManager
	WebhookManager
	BranchManager
	DiffManager
	CommitManager
	FileManager
	ReleaseManager
}

Provider is the unified interface for all Git hosting platforms. It composes 8 focused sub-interfaces for high cohesion and low coupling.

Consumers can depend on smaller interfaces (e.g., WebhookManager) when they don't need full Provider capabilities.

IssueManager and SearchManager are NOT part of Provider: only some platforms support them. Consumers that need issues or search should type-assert against the optional capability interfaces:

if ism, ok := p.(provider.IssueManager); ok { ... }
if sm, ok := p.(provider.SearchManager); ok { ... }

func NewProvider

func NewProvider(cfg Config) (Provider, error)

NewProvider creates a Provider for the given platform using the registry. Returns ErrPlatformNotSupported if the platform is not registered.

Platform backends are registered via init() functions. Import "github.com/yi-nology/git-platform-sdk/backends/all" with a blank identifier to register every platform shipped with the SDK.

type ProviderConstructor

type ProviderConstructor func(cfg Config) (Provider, error)

ProviderConstructor is a function that creates a Provider from a Config.

type ProviderError

type ProviderError struct {
	Platform   Platform
	Op         string // operation name, e.g., "ListRepos"
	Resource   string // optional resource identifier, e.g., "owner/repo"
	StatusCode int    // 0 when not applicable (e.g. configuration errors)
	Cause      error  // underlying cause; nil when constructed from raw fields
}

ProviderError is a structured error from a provider operation. It carries enough context (platform, op, resource, status code, cause) to support logging, retry, and user-facing messages. It implements errors.Is so the sentinel errors above are matched by Cause, and errors.As for typed access.

func (*ProviderError) Error

func (e *ProviderError) Error() string

Error implements the error interface.

func (*ProviderError) Is

func (e *ProviderError) Is(target error) bool

Is implements errors.Is. It matches when the wrapped Cause equals the target, which lets callers write `errors.Is(err, provider.ErrNotFound)`.

func (*ProviderError) IsClientError

func (e *ProviderError) IsClientError() bool

IsClientError reports 4xx.

func (*ProviderError) IsServerError

func (e *ProviderError) IsServerError() bool

IsServerError reports 5xx.

func (*ProviderError) IsStatus

func (e *ProviderError) IsStatus(code int) bool

IsStatus reports whether the error has the given HTTP status code.

func (*ProviderError) Unwrap

func (e *ProviderError) Unwrap() error

Unwrap implements errors.Unwrap.

type ReleaseInfo

type ReleaseInfo struct {
	ID          int64     `json:"id"`
	TagName     string    `json:"tag_name"`
	Title       string    `json:"title"`
	Body        string    `json:"body"`
	URL         string    `json:"url"`
	Draft       bool      `json:"draft"`
	Prerelease  bool      `json:"prerelease"`
	CreatedAt   time.Time `json:"created_at"`
	PublishedAt time.Time `json:"published_at"`
}

ReleaseInfo represents a release.

type ReleaseManager

type ReleaseManager interface {
	ListTags(ctx context.Context, owner, repo string) ([]*TagInfo, error)
	ListReleases(ctx context.Context, owner, repo string) ([]*ReleaseInfo, error)
	CreateRelease(ctx context.Context, owner, repo string, opts CreateReleaseOptions) (*ReleaseInfo, error)
	GetArchive(ctx context.Context, owner, repo, ref, format string) ([]byte, error)
}

ReleaseManager handles tags, releases, and archives.

type RepoManager

type RepoManager interface {
	ListRepos(ctx context.Context, opts ListRepoOptions) ([]*PlatformRepo, error)
	GetRepo(ctx context.Context, owner, repo string) (*PlatformRepo, error)
	CreateRepo(ctx context.Context, owner string, opts CreateRepoOptions) (*PlatformRepo, error)
	DeleteRepo(ctx context.Context, owner, repo string) error
	UpdateRepo(ctx context.Context, owner, repo string, opts UpdateRepoOptions) (*PlatformRepo, error)
	ForkRepo(ctx context.Context, owner, repo string, opts ForkRepoOptions) (*PlatformRepo, error)
}

RepoManager handles repository CRUD operations.

type RequestHook

type RequestHook func(ctx context.Context, req *http.Request) context.Context

RequestHook is called before an HTTP request is sent. It can modify the context (e.g., add tracing headers) or inspect the request.

type ResponseHook

type ResponseHook func(ctx context.Context, req *http.Request, resp *http.Response, duration time.Duration, err error)

ResponseHook is called after an HTTP response is received.

type RetryConfig

type RetryConfig struct {
	// MaxRetries is the maximum number of retry attempts (not counting the
	// initial request). <= 0 disables retry.
	MaxRetries int
	// BaseDelay is the initial backoff delay before the first retry.
	BaseDelay time.Duration
	// MaxDelay caps the backoff delay. Zero means 30s (the transport
	// default).
	MaxDelay time.Duration
	// RetryOn lists extra HTTP status codes to retry on, in addition to the
	// transport's default set (429 and 5xx).
	RetryOn []int
}

RetryConfig controls automatic retry behavior for HTTP requests issued by the transport layer. It is the public-facing configuration type passed via provider.Config; the transport package has its own internal transport.RetryConfig that this is mapped into.

func DefaultRetryConfig

func DefaultRetryConfig() RetryConfig

DefaultRetryConfig returns a sensible default retry configuration: 3 retries, 500ms base delay, 30s cap, no extra status codes.

type ReviewComment

type ReviewComment struct {
	Path      string `json:"path"`
	Body      string `json:"body"`
	Line      int    `json:"line,omitempty"`
	StartLine int    `json:"start_line,omitempty"`
	EndLine   int    `json:"end_line,omitempty"`
	Side      string `json:"side,omitempty"`
}

ReviewComment is a single inline comment in a code review.

type ReviewCommentResult

type ReviewCommentResult struct {
	Path       string `json:"path,omitempty"`
	Line       int    `json:"line,omitempty"`
	ExternalID string `json:"external_id,omitempty"`
	Error      string `json:"error,omitempty"`
}

ReviewCommentResult is the result of posting a single inline comment.

type ReviewResult

type ReviewResult struct {
	ID       string                `json:"id"`
	Body     string                `json:"body,omitempty"`
	HTMLURL  string                `json:"html_url,omitempty"`
	User     *CRUser               `json:"user,omitempty"`
	Comments []ReviewCommentResult `json:"comments,omitempty"`
}

ReviewResult is the result of a CreateReview call.

type SearchIssueResult

type SearchIssueResult struct {
	Number    int        `json:"number"`
	Title     string     `json:"title"`
	Body      string     `json:"body,omitempty"`
	State     IssueState `json:"state"`
	WebURL    string     `json:"web_url,omitempty"`
	Labels    []string   `json:"labels,omitempty"`
	Comments  int        `json:"comments,omitempty"`
	CreatedAt time.Time  `json:"created_at"`
}

SearchIssueResult is a single result from an issue search.

type SearchIssuesOptions

type SearchIssuesOptions struct {
	Query   string `json:"q"`
	Repo    string `json:"repo,omitempty"`
	State   string `json:"state,omitempty"`
	Sort    string `json:"sort,omitempty"`
	Order   string `json:"order,omitempty"`
	Page    int    `json:"page,omitempty"`
	PerPage int    `json:"per_page,omitempty"`
}

SearchIssuesOptions contains options for searching issues.

type SearchManager

type SearchManager interface {
	SearchRepos(ctx context.Context, opts SearchReposOptions) ([]*SearchRepoResult, int, error)
	SearchIssues(ctx context.Context, opts SearchIssuesOptions) ([]*SearchIssueResult, int, error)
	SearchUsers(ctx context.Context, opts SearchUsersOptions) ([]*SearchUserResult, int, error)
}

SearchManager provides cross-platform search for repositories, issues, and users.

type SearchRepoResult

type SearchRepoResult struct {
	FullName      string `json:"full_name"`
	Description   string `json:"description,omitempty"`
	WebURL        string `json:"web_url,omitempty"`
	Stars         int    `json:"stars,omitempty"`
	Forks         int    `json:"forks,omitempty"`
	DefaultBranch string `json:"default_branch,omitempty"`
	Private       bool   `json:"private,omitempty"`
}

SearchRepoResult is a single result from a repository search.

type SearchReposOptions

type SearchReposOptions struct {
	Query   string `json:"q"`
	Sort    string `json:"sort,omitempty"`
	Order   string `json:"order,omitempty"`
	Page    int    `json:"page,omitempty"`
	PerPage int    `json:"per_page,omitempty"`
}

SearchReposOptions contains options for searching repositories.

type SearchUserResult

type SearchUserResult struct {
	Login     string `json:"login"`
	Name      string `json:"name,omitempty"`
	AvatarURL string `json:"avatar_url,omitempty"`
	WebURL    string `json:"web_url,omitempty"`
}

SearchUserResult is a single result from a user search.

type SearchUsersOptions

type SearchUsersOptions struct {
	Query   string `json:"q"`
	Sort    string `json:"sort,omitempty"`
	Order   string `json:"order,omitempty"`
	Page    int    `json:"page,omitempty"`
	PerPage int    `json:"per_page,omitempty"`
}

SearchUsersOptions contains options for searching users.

type StaticTokenValidator

type StaticTokenValidator struct {
	Header string
}

StaticTokenValidator compares a static token header against the configured secret in constant time. Used for GitLab's X-Gitlab-Token.

func (StaticTokenValidator) Name

Name implements WebhookValidator.

func (StaticTokenValidator) Validate

func (s StaticTokenValidator) Validate(r *http.Request, body []byte, secret string) error

Validate implements WebhookValidator.

type Stats

type Stats struct {
	Hits      int64
	Misses    int64
	Evictions int64
	Size      int
}

Stats reports cache hit/miss counters and the current size. Counters are atomically incremented and safe to read concurrently.

type StatusError

type StatusError struct {
	Status int
	Cause  error
}

StatusError wraps an error with an explicit HTTP status code. Use this in platform backends when you need to attach a status code to an error from a third-party SDK that doesn't implement the statusCoder interface. This avoids the need for the reflection-based fallback in Wrap.

Example:

err := someSDK.DoSomething()
if err != nil {
    return provider.WrapStatusError(err, 404)
}

func (*StatusError) Error

func (e *StatusError) Error() string

func (*StatusError) StatusCode

func (e *StatusError) StatusCode() int

func (*StatusError) Unwrap

func (e *StatusError) Unwrap() error

type TagInfo

type TagInfo struct {
	Name   string `json:"name"`
	Commit string `json:"commit"`
}

TagInfo represents a tag.

type TestConnectionResult

type TestConnectionResult struct {
	Connected    bool   `json:"connected"`
	Platform     string `json:"platform"`
	UserName     string `json:"user_name"`
	Message      string `json:"message,omitempty"`
	CanListRepos bool   `json:"can_list_repos"`
	CanReadCR    bool   `json:"can_read_cr"`
	CanWriteCR   bool   `json:"can_write_cr"`
	CanWebhook   bool   `json:"can_webhook"`
}

TestConnectionResult contains the result of a connection test.

type UpdateCROptions

type UpdateCROptions struct {
	Title        string `json:"title,omitempty"`
	Description  string `json:"description,omitempty"`
	TargetBranch string `json:"target_branch,omitempty"`
}

UpdateCROptions contains options for updating a change request.

type UpdateIssueOptions

type UpdateIssueOptions struct {
	Title     string     `json:"title,omitempty"`
	Body      string     `json:"body,omitempty"`
	State     IssueState `json:"state,omitempty"`
	Assignees []string   `json:"assignees,omitempty"`
	Labels    []string   `json:"labels,omitempty"`
}

UpdateIssueOptions contains options for updating an issue.

type UpdateRepoOptions

type UpdateRepoOptions struct {
	Name          string `json:"name,omitempty"`
	Description   string `json:"description,omitempty"`
	DefaultBranch string `json:"default_branch,omitempty"`
	Private       *bool  `json:"private,omitempty"`
}

UpdateRepoOptions contains options for updating a repository.

type ValidatorFunc

type ValidatorFunc struct {
	N  string
	Fn func(r *http.Request, body []byte, secret string) error
}

ValidatorFunc adapts a plain function into a WebhookValidator.

func (ValidatorFunc) Name

func (v ValidatorFunc) Name() string

Name implements WebhookValidator.

func (ValidatorFunc) Validate

func (v ValidatorFunc) Validate(r *http.Request, body []byte, secret string) error

Validate implements WebhookValidator.

type WebhookManager

type WebhookManager interface {
	CreateWebhook(ctx context.Context, opts CreateWebhookOptions) (*PlatformWebhook, error)
	DeleteWebhook(ctx context.Context, owner, repo string, webhookID int64) error
	ListWebhooks(ctx context.Context, owner, repo string) ([]*PlatformWebhook, error)
	ParseWebhookEvent(r *http.Request, secret string) (*NormalizedEvent, error)
	ValidateWebhookSignature(r *http.Request, secret string) error
}

WebhookManager handles webhook CRUD and event parsing.

type WebhookValidator

type WebhookValidator interface {
	Name() string
	Validate(r *http.Request, body []byte, secret string) error
}

WebhookValidator verifies the authenticity of an incoming webhook request. Implementations are stateless and safe for concurrent use.

The signature scheme varies per platform:

  • GitHub: HMAC-SHA256 of the body, sent in X-Hub-Signature-256.
  • GitLab: static token compared in constant time against X-Gitlab-Token.
  • Gitea / Forgejo: HMAC-SHA256 of the body, sent in X-Gitea-Signature.
  • Gitee / GitCode / Tencent: HMAC-SHA256 of the body, sent in X-Gitee-Token / X-Token.

type WebhookValidatorRegistry

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

WebhookValidatorRegistry indexes WebhookValidator implementations by platform. It is safe for concurrent use; registrations happen during package init and reads happen on the request path.

func DefaultWebhookRegistry

func DefaultWebhookRegistry() *WebhookValidatorRegistry

DefaultWebhookRegistry returns the process-wide registry.

func NewWebhookValidatorRegistry

func NewWebhookValidatorRegistry() *WebhookValidatorRegistry

NewWebhookValidatorRegistry builds an empty registry.

func (*WebhookValidatorRegistry) Get

Get returns the validator for the given platform, or nil if none is registered.

func (*WebhookValidatorRegistry) Register

Register associates a validator with a platform.

func (*WebhookValidatorRegistry) Validate

func (r *WebhookValidatorRegistry) Validate(p Platform, req *http.Request, body []byte, secret string) error

Validate looks up the validator for the given platform and runs it. A nil validator or missing platform produces ErrNotImplemented.

Jump to

Keyboard shortcuts

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