provider

package
v0.52.0 Latest Latest
Warning

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

Go to latest
Published: Aug 31, 2026 License: MIT Imports: 23 Imported by: 0

Documentation ¶

Index ¶

Constants ¶

View Source
const (
	ReactionPlusOne  = "+1"
	ReactionMinusOne = "-1"
	ReactionLaugh    = "laugh"
	ReactionConfused = "confused"
	ReactionHeart    = "heart"
	ReactionHooray   = "hooray"
	ReactionRocket   = "rocket"
	ReactionEyes     = "eyes"
)

Standard emoji identifiers used across all platforms. GitLab maps these to its award-emoji names internally (e.g. +1 ↔ thumbsup).

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 ExtractMentions ¶ added in v0.52.0

func ExtractMentions(body string) []string

ExtractMentions returns the deduplicated list of @usernames found in body. Email addresses (foo@bar.com) are excluded by the leading non-word-char guard. The returned order follows first occurrence.

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 Ignores ¶ added in v0.45.0

func Ignores(divs []Divergence, method, field string) bool

Ignores reports whether the ledger registers an ignore of field on method.

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 Stubs ¶ added in v0.45.0

func Stubs(divs []Divergence, method string) bool

Stubs reports whether the ledger registers method as a stub.

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.

func (*CRComment) Mentions ¶ added in v0.52.0

func (c *CRComment) Mentions() []string

Mentions returns the deduplicated @usernames found in the comment body.

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 CapabilitySet ¶ added in v0.38.0

type CapabilitySet struct {
	Issues         bool // provider.IssueManager
	Search         bool // provider.SearchManager
	Labels         bool // provider.LabelManager
	Milestones     bool // provider.MilestoneManager
	Reviews        bool // provider.ReviewManager
	CommitStatuses bool // provider.CommitStatusManager
	Notifications  bool // provider.NotificationManager
	Reactions      bool // provider.ReactionManager
}

CapabilitySet statically declares which optional capability interfaces a Provider implements. Values are compile-time constants per backend; no runtime probing is performed. Consumers should route on these flags instead of probing with type assertions:

if p.Capabilities().Labels {
	lm := p.(provider.LabelManager)
	// ...
}

When a new optional capability interface is added to the SDK, add a field here and update every backend's Capabilities method; the contract suite enforces that declarations match implementations.

type ChangeRequest ¶

type ChangeRequest struct {
	ID           int64   `json:"id"`
	Number       string  `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. Number is the platform's change-request identifier as a string (numeric on every current platform), mirroring Issue.Number.

type ChangeRequestManager ¶

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

ChangeRequestManager handles pull request / merge request lifecycle. Change request numbers are strings (same addressing scheme as IssueManager); numeric platforms parse with strconv and fail with a wrapped "invalid pull request number" error.

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)
}

CommitManager handles commit operations.

Commit statuses are NOT part of CommitManager: they are a CI reporting concern that not every platform exposes (Gitee's public REST API has no commit-status endpoint). See the optional CommitStatusManager capability interface and CapabilitySet.CommitStatuses.

type CommitStatusManager ¶ added in v0.45.0

type CommitStatusManager interface {
	CreateCommitStatus(ctx context.Context, owner, repo, sha string, opts CommitStatusOptions) error
}

CommitStatusManager reports CI statuses on commits. It is an optional capability interface: consumers should gate on Provider.Capabilities().CommitStatuses (or type-assert) before use.

It is deliberately separate from CommitManager: commit statuses are a CI reporting concern that not every platform exposes (Gitee's public REST API has no commit-status endpoint), so absence is expressed by not declaring the capability instead of stubbing the method.

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

	// TokenStyle overrides the default authentication header style.
	// Supported values: "private" (PRIVATE-TOKEN, default for GitLab),
	// "bearer" (Authorization: Bearer). Empty string uses platform default.
	TokenStyle string

	// 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"`
	Milestone string   `json:"milestone,omitempty"` // milestone number/ID as a string; "" = do not set
}

CreateIssueOptions contains options for creating an issue.

type CreateLabelOptions ¶ added in v0.38.0

type CreateLabelOptions struct {
	Name        string `json:"name"`
	Color       string `json:"color"`
	Description string `json:"description,omitempty"`
}

CreateLabelOptions contains options for creating a repository label. Color uses the canonical 6-digit hex form without '#' (e.g. "ff0000").

type CreateMilestoneOptions ¶ added in v0.40.0

type CreateMilestoneOptions struct {
	Title       string     `json:"title"`
	Description string     `json:"description,omitempty"`
	DueOn       *time.Time `json:"due_on,omitempty"`
}

CreateMilestoneOptions contains options for creating a repository milestone.

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, number string) (*MergeDiff, error)
	GetCRFiles(ctx context.Context, owner, repo, number string) ([]*ChangedFile, error)
	CreateNote(ctx context.Context, owner, repo, number, body string) (string, error)
	DeleteNote(ctx context.Context, owner, repo, number string, noteID string) error
	CreateDiscussion(ctx context.Context, owner, repo, number string, opts DiscussionOptions) (string, error)
}

DiffManager handles diff and discussion operations. Review operations (CreateReview and friends) live on the optional ReviewManager capability interface; DiffManager itself carries five methods. Change request numbers are strings (same addressing scheme as IssueManager); numeric platforms parse with strconv and fail with a wrapped "invalid pull request number" error.

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 Divergence ¶ added in v0.45.0

type Divergence struct {
	Capability string
	Method     string
	Field      string
	Kind       DivergenceKind
	Reason     string
}

Divergence is one registered entry of a backend's divergence ledger. Capability and Method carry the provider interface and method names; Field names the affected option/result field for ignore and mapping entries (empty when the divergence is method-scoped). Reason is a one-sentence explanation surfaced in docs/divergence-ledger.md.

Backends expose their ledger via a package-level Divergences function and the Provider.Divergences method; the ledger is the machine-readable successor of the former "(spec §4.6)" comment registrations.

func FindByMethod ¶ added in v0.45.0

func FindByMethod(divs []Divergence, method string) []Divergence

FindByMethod returns the ledger entries registered for method.

type DivergenceKind ¶ added in v0.45.0

type DivergenceKind string

DivergenceKind classifies how a backend's behavior departs from the unified provider semantics for a given method.

const (
	// DivergenceStub marks a method the platform cannot serve at all: the
	// call returns an error wrapping ErrNotImplemented and touches no wire.
	DivergenceStub DivergenceKind = "stub"
	// DivergenceIgnore marks a field or parameter that is silently dropped:
	// the call succeeds but the ignored input has no effect.
	DivergenceIgnore DivergenceKind = "ignore"
	// DivergenceMapping marks a semantic mapping: the call succeeds and
	// returns the closest platform equivalent, an approximation of the
	// unified semantics.
	DivergenceMapping DivergenceKind = "mapping"
	// DivergenceDetour marks an implementation detour: the method bypasses
	// the platform's third-party SDK and drives the raw transport client.
	// Behavior is unchanged; the entry exists for maintainers.
	DivergenceDetour DivergenceKind = "detour"
)

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    string        `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 *MilestoneRef `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. Number is the platform's issue identifier as a string (numeric on every current platform except Gitee, whose identifiers are alphanumeric).

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.

func (*IssueComment) Mentions ¶ added in v0.52.0

func (c *IssueComment) Mentions() []string

Mentions returns the deduplicated @usernames found in the comment body.

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, number string) (*Issue, error)
	CreateIssue(ctx context.Context, opts CreateIssueOptions) (*Issue, error)
	UpdateIssue(ctx context.Context, owner, repo, number string, opts UpdateIssueOptions) (*Issue, error)
	CloseIssue(ctx context.Context, owner, repo, number string) (*Issue, error)
	ReopenIssue(ctx context.Context, owner, repo, number string) (*Issue, error)
	// ListIssueComments returns the issue's comments in full, exhausting
	// the platform's pagination — the result is the complete comment list,
	// not a single page, in the platform's default order.
	ListIssueComments(ctx context.Context, owner, repo, number string) ([]*IssueComment, error)
	CreateIssueComment(ctx context.Context, owner, repo, number, body string) (*IssueComment, error)
	// UpdateIssueComment replaces a comment's body wholesale: every
	// platform's edit surface is a body-only PATCH/PUT with no
	// partial-update semantics, mirroring CreateIssueComment. The platform
	// enforces authorship — only the comment's author (typically the token
	// identity, e.g. a review bot updating its own comment) may edit — so
	// the call fails for anyone else's comment. commentID is the
	// IssueComment.ID from CreateIssueComment or ListIssueComments. number
	// is ignored on platforms whose edit endpoint addresses the comment
	// directly (GitHub, Gitea, Forgejo, GitCode, Gitee); GitLab and Tencent
	// 工蜂 route through the issue, so it must carry that issue's number.
	UpdateIssueComment(ctx context.Context, owner, repo, number string, commentID int64, body string) (*IssueComment, error)
	ListIssueLabels(ctx context.Context, owner, repo string) ([]*IssueLabel, error)
	AddIssueLabels(ctx context.Context, owner, repo, number string, labels []string) error
	RemoveIssueLabel(ctx context.Context, owner, repo, number, name string) error
}

IssueManager provides issue CRUD, comments, and label management. Issue numbers are strings: every platform address is representable as a string, and Gitee natively uses alphanumeric identifiers (e.g. "IAINVA"). Backends on numeric platforms parse with strconv.Atoi and fail with a wrapped "invalid issue number" error.

type IssueState ¶

type IssueState string

IssueState represents the state of an issue.

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

type Label ¶ added in v0.38.0

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

Label represents a repository label. Color is canonicalized to 6-digit hex without a leading '#' (e.g. "ff0000"); backends add the '#' when a platform requires it (GitLab, Gitea, Forgejo) and strip it on the way in.

type LabelManager ¶ added in v0.38.0

type LabelManager interface {
	// ListLabels lists the repository's labels.
	ListLabels(ctx context.Context, owner, repo string, opts ListLabelsOptions) ([]*Label, error)
	// CreateLabel creates a repository label.
	CreateLabel(ctx context.Context, owner, repo string, opts CreateLabelOptions) (*Label, error)
	// UpdateLabel updates the label with the given name. Nil fields in opts
	// are left unchanged.
	UpdateLabel(ctx context.Context, owner, repo, name string, opts UpdateLabelOptions) (*Label, error)
	// DeleteLabel deletes the label with the given name.
	DeleteLabel(ctx context.Context, owner, repo, name string) error
}

LabelManager provides repository-level label CRUD. It is an optional capability interface: consumers should gate on Provider.Capabilities() (or type-assert) before use. Labels are addressed by name; backends whose platform API addresses labels by numeric ID (GitLab, Gitea, Forgejo) resolve the name internally. Such backends scan labels with server-side pagination (100 per page, bounded to 50 pages); beyond that bound a label may be reported as not found by UpdateLabel/DeleteLabel even though it exists.

The issue-scoped operations (ListIssueLabels, AddIssueLabels, RemoveIssueLabel) remain on IssueManager because they operate on an issue, not on the repository's label set.

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 ListLabelsOptions ¶ added in v0.38.0

type ListLabelsOptions struct {
	Page    int `json:"page,omitempty"`
	PerPage int `json:"per_page,omitempty"`
}

ListLabelsOptions contains options for listing repository labels.

type ListMilestonesOptions ¶ added in v0.40.0

type ListMilestonesOptions struct {
	State   string `json:"state,omitempty"`
	Page    int    `json:"page,omitempty"`
	PerPage int    `json:"per_page,omitempty"`
}

ListMilestonesOptions contains options for listing repository milestones. State filters by "open" or "closed"; an empty State lists whatever the platform defaults to (GitHub/Gitea/Forgejo/Gitee default to open, GitLab to all). Tencent Code ignores State entirely — gongfeng's list options expose pagination only, so all states are listed.

type ListNotificationsOptions ¶ added in v0.52.0

type ListNotificationsOptions struct {
	All     bool   `json:"all,omitempty"`   // include already-read notifications
	Since   string `json:"since,omitempty"` // RFC3339 timestamp
	Page    int    `json:"page,omitempty"`
	PerPage int    `json:"per_page,omitempty"`
}

ListNotificationsOptions contains options for listing notifications.

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 least recently used entry is evicted before a new one is inserted.

type MarkNotificationsOptions ¶ added in v0.52.0

type MarkNotificationsOptions struct {
	LastReadAt string `json:"last_read_at,omitempty"` // RFC3339; empty = mark all
}

MarkNotificationsOptions contains options for marking notifications as read.

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 Milestone ¶ added in v0.40.0

type Milestone struct {
	Number      string         `json:"number"`
	Title       string         `json:"title"`
	Description string         `json:"description"`
	State       MilestoneState `json:"state"`
	DueOn       *time.Time     `json:"due_on,omitempty"`
}

Milestone represents a repository milestone. Number carries the platform's milestone addressing identifier as a string — the same value MilestoneRef.Number uses and MilestoneManager methods accept: the milestone number on GitHub, the platform milestone ID on GitLab, Gitea, Forgejo, GitCode, and Tencent Code, and the milestone serial number on Gitee (see MilestoneManager for the per-platform truth).

type MilestoneManager ¶ added in v0.40.0

type MilestoneManager interface {
	// ListMilestones lists the repository's milestones.
	ListMilestones(ctx context.Context, owner, repo string, opts ListMilestonesOptions) ([]Milestone, error)
	// GetMilestone fetches the milestone with the given number.
	GetMilestone(ctx context.Context, owner, repo, number string) (*Milestone, error)
	// CreateMilestone creates a repository milestone.
	CreateMilestone(ctx context.Context, owner, repo string, opts CreateMilestoneOptions) (*Milestone, error)
	// UpdateMilestone updates the milestone with the given number. Nil
	// fields in opts are left unchanged.
	UpdateMilestone(ctx context.Context, owner, repo, number string, opts UpdateMilestoneOptions) (*Milestone, error)
	// DeleteMilestone deletes the milestone with the given number.
	DeleteMilestone(ctx context.Context, owner, repo, number string) error
}

MilestoneManager provides repository-level milestone CRUD. It is an optional capability interface: consumers should gate on Provider.Capabilities() (or type-assert) before use.

Milestones are addressed by a string `number`, but what that string carries is platform-specific — the same identifier MilestoneRef.Number and Milestone.Number expose:

  • GitHub: the milestone *number* (its per-repo serial number).
  • GitLab, Gitea, Forgejo, GitCode, Tencent Code: the platform milestone *ID* (the write endpoints take exactly that identifier, so per-platform round-trips hold).
  • Gitee: the milestone *serial number* (the "number" field of Gitee's milestone payload — the identifier Gitee's own issue and milestone write endpoints take; the SDK model exposes no id).

Values obtained from MilestoneRef.Number (issue payloads) or Milestone.Number (list/get results) round-trip back into these methods on the platform they came from.

type MilestoneRef ¶ added in v0.39.0

type MilestoneRef struct {
	Number string `json:"number"`
	Title  string `json:"title,omitempty"`
}

MilestoneRef references a milestone from an issue. Number carries the platform's milestone addressing identifier as a string: the milestone *number* on GitHub, the platform milestone *ID* on GitLab, Gitea, Forgejo, GitCode, and Tencent Code (whose write endpoints take exactly that identifier, so per-platform round-trips hold), and Gitee's milestone *serial number* (the "number" field of Gitee's milestone payload — the identifier Gitee's own issue and milestone write endpoints take; the SDK model exposes no id). This is the same identifier Milestone.Number exposes and MilestoneManager methods accept, so refs round-trip through the milestone manager on the platform they came from.

type MilestoneState ¶ added in v0.40.0

type MilestoneState string

MilestoneState represents the state of a milestone.

const (
	MilestoneStateOpen   MilestoneState = "open"
	MilestoneStateClosed MilestoneState = "closed"
)

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 Notification ¶ added in v0.52.0

type Notification struct {
	ID        string              `json:"id"`
	Unread    bool                `json:"unread"`
	Reason    string              `json:"reason"`
	Subject   NotificationSubject `json:"subject"`
	Repo      *EventRepo          `json:"repo,omitempty"`
	UpdatedAt time.Time           `json:"updated_at"`
}

Notification represents a single notification from a user's inbox.

type NotificationManager ¶ added in v0.52.0

type NotificationManager interface {
	// ListNotifications returns the authenticated user's notifications,
	// exhausting the platform's pagination.
	ListNotifications(ctx context.Context, opts ListNotificationsOptions) ([]*Notification, error)
	// ListRepoNotifications returns notifications for a specific repository.
	ListRepoNotifications(ctx context.Context, owner, repo string, opts ListNotificationsOptions) ([]*Notification, error)
	// MarkNotificationRead marks a single notification thread as read.
	MarkNotificationRead(ctx context.Context, threadID string) error
	// MarkNotificationsRead marks all (or filtered) notifications as read.
	MarkNotificationsRead(ctx context.Context, opts MarkNotificationsOptions) error
	// MarkRepoNotificationsRead marks all notifications for a repository as read.
	MarkRepoNotificationsRead(ctx context.Context, owner, repo string, opts MarkNotificationsOptions) error
}

NotificationManager provides access to a user's notification inbox and per-repository notification streams. It is an optional capability interface: consumers should gate on Provider.Capabilities().Notifications (or type-assert) before use.

Platform support: GitHub, GitCode, Gitea, Forgejo, Gitee. GitLab exposes only notification *settings* (no inbox) and TencentCode has no notification API at all — both report Notifications=false in CapabilitySet.

type NotificationSubject ¶ added in v0.52.0

type NotificationSubject struct {
	Title string `json:"title"`
	Type  string `json:"type"` // "Issue", "PullRequest", "Commit", etc.
	URL   string `json:"url"`
}

NotificationSubject describes the resource that triggered the notification.

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)
	// Capabilities statically declares the optional capability interfaces
	// this provider implements. See CapabilitySet.
	Capabilities() CapabilitySet
	// Divergences statically declares this backend's divergence ledger: the
	// registered places where its behavior departs from the unified
	// semantics (stub / ignore / mapping / detour). Consumers can route on
	// these entries — e.g. provider.Ignores — and docs/divergence-ledger.md
	// is generated from them. Like CapabilitySet this is a compile-time
	// declaration; the contract suite locks ledger entries to behavior.
	Divergences() []Divergence

	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, SearchManager, and CommitStatusManager are NOT part of Provider: only some platforms support them. Consumers that need issues, search, or commit statuses should type-assert against the optional capability interfaces:

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

Caps declares the same information programmatically: consumers can route
on p.Capabilities() instead of probing with type assertions.

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 Reaction ¶ added in v0.52.0

type Reaction struct {
	ID    int64   `json:"id"`
	Emoji string  `json:"emoji"`
	User  *CRUser `json:"user,omitempty"`
}

Reaction represents an emoji reaction on an issue or comment.

type ReactionManager ¶ added in v0.52.0

type ReactionManager interface {
	// ListIssueReactions returns all reactions on an issue.
	ListIssueReactions(ctx context.Context, owner, repo, number string) ([]*Reaction, error)
	// AddIssueReaction adds a reaction emoji to an issue. emoji is one of
	// the Reaction* constants (e.g. ReactionHeart).
	AddIssueReaction(ctx context.Context, owner, repo, number, emoji string) (*Reaction, error)
	// RemoveIssueReaction removes a reaction by its platform ID.
	RemoveIssueReaction(ctx context.Context, owner, repo, number string, reactionID int64) error

	// ListIssueCommentReactions returns all reactions on an issue comment.
	ListIssueCommentReactions(ctx context.Context, owner, repo string, commentID int64) ([]*Reaction, error)
	// AddIssueCommentReaction adds a reaction emoji to an issue comment.
	AddIssueCommentReaction(ctx context.Context, owner, repo string, commentID int64, emoji string) (*Reaction, error)
	// RemoveIssueCommentReaction removes a reaction by its platform ID.
	RemoveIssueCommentReaction(ctx context.Context, owner, repo string, commentID int64, reactionID int64) error

	// ListCRCommentReactions returns all reactions on a change-request
	// comment (PR review comment / MR note).
	ListCRCommentReactions(ctx context.Context, owner, repo string, commentID int64) ([]*Reaction, error)
	// AddCRCommentReaction adds a reaction emoji to a change-request comment.
	AddCRCommentReaction(ctx context.Context, owner, repo string, commentID int64, emoji string) (*Reaction, error)
	// RemoveCRCommentReaction removes a reaction by its platform ID.
	RemoveCRCommentReaction(ctx context.Context, owner, repo string, commentID int64, reactionID int64) error
}

ReactionManager provides emoji reaction CRUD on issues and their comments (both issue comments and change-request comments). It is an optional capability interface: consumers should gate on Provider.Capabilities().Reactions (or type-assert) before use.

Platform support: GitHub, GitCode, GitLab (via award-emoji), Gitea, Forgejo. Gitee and TencentCode have no reaction API — both report Reactions=false in CapabilitySet.

GitLab maps reactions to its "award emoji" API; the SDK normalizes emoji names (e.g. +1 ↔ thumbsup, hooray ↔ tada) so callers use the same constants across all platforms.

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)
	GetReleaseByTag(ctx context.Context, owner, repo, tag string) (*ReleaseInfo, error)
	UpdateRelease(ctx context.Context, owner, repo, tag string, opts UpdateReleaseOptions) (*ReleaseInfo, error)
	DeleteRelease(ctx context.Context, owner, repo, tag string) error
	GetArchive(ctx context.Context, owner, repo, ref, format string) ([]byte, error)
}

ReleaseManager handles tags, releases, and archives. Releases are addressed by tag name across every method: tag names are stable and human-addressable, while the underlying numeric release IDs are not.

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 Review ¶ added in v0.40.0

type Review struct {
	ID          int64       `json:"id"`
	User        string      `json:"user"`
	State       ReviewState `json:"state"`
	Body        string      `json:"body"`
	SubmittedAt time.Time   `json:"submitted_at"`
}

Review represents a code review on a change request (the ReviewManager view; ReviewResult above is the create-call response).

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.

func (*ReviewComment) Mentions ¶ added in v0.52.0

func (c *ReviewComment) Mentions() []string

Mentions returns the deduplicated @usernames found in the comment body.

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 ReviewManager ¶ added in v0.40.0

type ReviewManager interface {
	CreateReview(ctx context.Context, owner, repo, number string, opts CreateReviewOptions) (*ReviewResult, error)
	ListReviews(ctx context.Context, owner, repo, number string) ([]Review, error)
	GetReview(ctx context.Context, owner, repo, number string, reviewID int64) (*Review, error)
	RequestReviewers(ctx context.Context, owner, repo, number string, reviewers []string) error
	DismissReview(ctx context.Context, owner, repo, number string, reviewID int64, message string) error
}

ReviewManager provides code-review operations on change requests. Change request numbers are strings (same addressing scheme as IssueManager); individual reviews are addressed by their numeric platform ID. It is an optional capability: consumers should check Capabilities().Reviews before type-asserting.

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 ReviewState ¶ added in v0.40.0

type ReviewState string

ReviewState is the normalized state of a code review.

const (
	ReviewStateApproved         ReviewState = "approved"
	ReviewStateChangesRequested ReviewState = "changes_requested"
	ReviewStateCommented        ReviewState = "commented"
	ReviewStatePending          ReviewState = "pending"
)

type SearchIssueResult ¶

type SearchIssueResult struct {
	Number    string     `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. Number is the platform's issue addressing identifier as a string, so results feed GetIssue(number string) directly (numeric platforms return "1", Gitee's alphanumeric identifiers return e.g. "IAINVA").

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.

The *int return is the server-side total when the platform reports one, and nil when it does not — callers must not treat a total as guaranteed.

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.

Sort and Order are platform-dependent: each backend forwards them to its platform's own vocabulary (e.g. GitHub's stars/forks/updated with asc/desc), so values valid on one platform may be ignored or rejected on another (gitea/forgejo reject unknown sort/order values with HTTP 422; gitlab's search API exposes no sort/order at all — a registered ignore). Consult the target platform's search documentation for the accepted values.

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"`
	Milestone string     `json:"milestone,omitempty"` // milestone number/ID as a string; "" = leave unchanged
}

UpdateIssueOptions contains options for updating an issue.

type UpdateLabelOptions ¶ added in v0.38.0

type UpdateLabelOptions struct {
	NewName     *string `json:"new_name,omitempty"`
	Color       *string `json:"color,omitempty"`
	Description *string `json:"description,omitempty"`
}

UpdateLabelOptions contains options for updating a repository label. Nil fields are left unchanged.

type UpdateMilestoneOptions ¶ added in v0.40.0

type UpdateMilestoneOptions struct {
	Title       *string        `json:"title,omitempty"`
	Description *string        `json:"description,omitempty"`
	State       MilestoneState `json:"state,omitempty"`
	DueOn       *time.Time     `json:"due_on,omitempty"`
}

UpdateMilestoneOptions contains options for updating a repository milestone. Nil fields are left unchanged.

type UpdateReleaseOptions ¶ added in v0.40.0

type UpdateReleaseOptions struct {
	Name       *string `json:"name,omitempty"`
	Body       *string `json:"body,omitempty"`
	Draft      *bool   `json:"draft,omitempty"`
	Prerelease *bool   `json:"prerelease,omitempty"`
}

UpdateReleaseOptions contains options for updating a release addressed by tag. Nil fields are left unchanged.

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