Documentation
¶
Overview ¶
Package githubsync owns the GitHub integration's application rules: which accounts may follow which repositories, webhook deduplication, coalesced refresh intent and reconciliation. It holds no transport or SQL.
Index ¶
- Variables
- type Admission
- type Connection
- type Follow
- type GitHub
- type Lease
- type Notification
- type Options
- type PullRequestRef
- type Repository
- type Service
- func (s *Service) Complete(ctx context.Context, ownerID, state, code string) error
- func (s *Service) Connect(ctx context.Context, ownerID string) (string, error)
- func (s *Service) Disconnect(ctx context.Context, ownerID string) error
- func (s *Service) Finish(ctx context.Context, result fetchprotocol.Result) error
- func (s *Service) Follow(ctx context.Context, ownerID string, repositoryID int64) error
- func (s *Service) Followers(ctx context.Context, repositoryID int64) ([]string, error)
- func (s *Service) Lease(ctx context.Context) (*fetchprotocol.Job, error)
- func (s *Service) Reconcile(ctx context.Context) error
- func (s *Service) Repositories(ctx context.Context, ownerID string) ([]Repository, error)
- func (s *Service) Resync(ctx context.Context, ownerID string) error
- func (s *Service) SetNotify(ctx context.Context, ownerID string, repositoryID int64, notify bool) error
- func (s *Service) Status(ctx context.Context, ownerID string) (Status, error)
- func (s *Service) Unfollow(ctx context.Context, ownerID string, repositoryID int64) error
- func (s *Service) Webhook(ctx context.Context, event, delivery, signature string, body []byte) error
- type Status
- type Store
Constants ¶
This section is empty.
Variables ¶
var ( ErrForbidden = errors.New("GitHub repository is not accessible to this account") ErrNotConnected = errors.New("GitHub is not connected") ErrReconnect = errors.New("GitHub authorization expired or was revoked; connect again") ErrSignature = errors.New("invalid webhook signature") )
var ErrInvalidResult = errors.New("fetch result does not match the leased pull request")
ErrInvalidResult reports a fetcher snapshot that does not match its lease.
var ErrLeaseLost = errors.New("fetch lease lost")
var ErrNotFound = errors.New("not found")
ErrNotFound reports an absent connection, follow or OAuth state.
Functions ¶
This section is empty.
Types ¶
type Admission ¶
type Admission struct {
OwnerID string
Request ingestion.Request
Notification *Notification
}
Admission is one account's submission of a fetched snapshot. A non-nil Notification is push intent linked to the admitted ingestion job.
type Connection ¶
type Connection struct {
OwnerID string
UserID int64
Login string
AccessToken []byte
AccessExpiresAt int64
RefreshToken []byte
RefreshExpiresAt int64
Status string // active, revoked or expired
ConnectedAt int64
}
Connection is one account's GitHub user authorization. Tokens are sealed; storage never sees plaintext.
type Follow ¶
type Follow struct {
OwnerID string
RepositoryID int64
InstallationID int64
FullName string
Status string // active or lost
VerifiedAt int64
CreatedAt int64
// Muted follows still receive snapshots but no push notifications. Reads
// report it; PutFollow leaves it unchanged.
Muted bool
}
Follow is an account's subscription to a repository's pull requests. Lost follows stay listed but receive nothing until access is verified again.
type GitHub ¶
type GitHub interface {
AppSlug(ctx context.Context) (string, error)
VerifyWebhook(body []byte, signature256 string) bool
AuthorizeURL(state, redirectURI string) string
Exchange(ctx context.Context, code, redirectURI string) (github.UserToken, error)
Refresh(ctx context.Context, refreshToken string) (github.UserToken, error)
User(ctx context.Context, accessToken string) (github.Account, error)
UserRepositories(ctx context.Context, accessToken string) ([]github.Repository, error)
OpenPullRequests(ctx context.Context, installationID int64, fullName string) ([]github.PullRequest, error)
PullRequest(ctx context.Context, installationID int64, fullName string, number int) (github.PullRequest, error)
InstallationToken(ctx context.Context, installationID int64, repositoryIDs []int64, permissions map[string]string) (github.Token, error)
}
GitHub is the adapter subset this service needs.
type Lease ¶
type Lease struct {
Ref PullRequestRef
Token string
Generation int64
RetentionRun string
Attempt int
}
Lease is one claimed refresh of a pull request at a generation.
type Notification ¶ added in v0.2.2
Notification is the text of a pull request push notification. Delivery adds the link once the follower's snapshot is published.
type PullRequestRef ¶
PullRequestRef addresses one pull request through the installation that can read it.
type Repository ¶
type Repository struct {
ID int64 `json:"id"`
FullName string `json:"fullName"`
Private bool `json:"private"`
Followed bool `json:"followed"`
// Lost marks a followed repository the account can no longer read.
Lost bool `json:"lost"`
// Notify reports whether new pull request diffs send push notifications:
// followed and not muted.
Notify bool `json:"notify"`
}
Repository is a repository the account can follow or already follows.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
func (*Service) Complete ¶
Complete exchanges the callback code. A state issued to another account is refused.
func (*Service) Connect ¶
Connect starts authorization bound to the account; only that account's session can complete it.
func (*Service) Disconnect ¶
func (*Service) Finish ¶
Finish applies a fetcher result. A snapshot is published to every account with verified access in one transaction with the refresh; a failure retries with backoff. ErrLeaseLost means another lease superseded this one. When the snapshot announces new work (see announcement), the same transaction records a push notification for every admitted follower that has not muted the repository.
func (*Service) Follow ¶
Follow subscribes the account after verifying access, then requests a refresh of every open pull request so existing work appears immediately.
func (*Service) Followers ¶
Followers returns accounts whose access to the repository is verified within verificationAge, rechecking stale follows first. Accounts that lost access are excluded; their follows stay lost until verified again.
func (*Service) Lease ¶
Lease claims the next pending pull request refresh for the fetcher. It resolves refreshes that need no fetch (deleted pull requests, repositories without followers) itself. Nil means nothing is pending.
func (*Service) Reconcile ¶
Reconcile recovers deliveries GitHub never retries: it rechecks every connected account's access, then requests refreshes for every open or previously open pull request of each actively followed repository.
func (*Service) Repositories ¶
Repositories lists what both the GitHub user and the app can read, plus followed repositories that lost access.
func (*Service) Resync ¶
Resync rechecks the account's access and refreshes every followed repository's pull requests. Its run identity makes each resulting submission fresh, extending retention of pull requests idle since their last change.
func (*Service) SetNotify ¶ added in v0.2.2
func (s *Service) SetNotify(ctx context.Context, ownerID string, repositoryID int64, notify bool) error
SetNotify changes whether a followed repository's new pull request diffs send push notifications to the account. ErrNotFound when not followed.
func (*Service) Webhook ¶
func (s *Service) Webhook(ctx context.Context, event, delivery, signature string, body []byte) error
Webhook verifies and applies one delivery. Duplicate delivery IDs are acknowledged without effect; a delivery is recorded only after it applies, so a failed one can be redelivered. Every effect is idempotent.
type Status ¶
type Status struct {
Connected bool `json:"connected"`
Login string `json:"login,omitempty"`
State string `json:"state,omitempty"`
InstallURL string `json:"installUrl"`
}
Status reports the account's connection and the app installation page.
type Store ¶
type Store interface {
PutOAuthState(ctx context.Context, hash, ownerID string, expiresAt int64) error
// TakeOAuthState consumes an unexpired state exactly once.
TakeOAuthState(ctx context.Context, hash string, now int64) (string, error)
PutConnection(ctx context.Context, connection Connection) error
Connection(ctx context.Context, ownerID string) (Connection, error)
Connections(ctx context.Context) ([]Connection, error)
// DeleteConnection removes the authorization and every follow of the account.
DeleteConnection(ctx context.Context, ownerID string) error
// RevokeUser marks every connection of the GitHub user revoked and their follows lost.
RevokeUser(ctx context.Context, userID int64) error
PutFollow(ctx context.Context, follow Follow) error
DeleteFollow(ctx context.Context, ownerID string, repositoryID int64) error
Follows(ctx context.Context, ownerID string) ([]Follow, error)
RepositoryFollows(ctx context.Context, repositoryID int64) ([]Follow, error)
// LoseInstallation marks follows lost for the installation, or only its listed repositories.
LoseInstallation(ctx context.Context, installationID int64, repositoryIDs []int64) error
// SeenDelivery and RecordDelivery deduplicate webhook deliveries by GUID.
SeenDelivery(ctx context.Context, id string) (bool, error)
RecordDelivery(ctx context.Context, id, event string, now int64) error
// SetMuted changes whether a follow sends push notifications. ErrNotFound
// when the account does not follow the repository.
SetMuted(ctx context.Context, ownerID string, repositoryID int64, muted bool) error
// RequestRefresh coalesces refresh intent per pull request by advancing its
// generation. A nonempty retention run makes the next submission fresh.
// Opened records, in the same transaction, that a webhook announced the
// pull request, so its first snapshot notifies followers.
RequestRefresh(ctx context.Context, ref PullRequestRef, retentionRun string, opened bool, now int64) error
// PullRequestHead returns the last captured head SHA ("" before the first
// snapshot) and whether a webhook announced the pull request.
PullRequestHead(ctx context.Context, repositoryID int64, number int) (head string, opened bool, err error)
// KnownOpen lists pull request numbers last observed open.
KnownOpen(ctx context.Context, repositoryID int64) ([]int, error)
// ClaimRefresh leases the oldest pending, available pull request until
// leaseUntil, fencing earlier leases. ErrNotFound means none is pending.
ClaimRefresh(ctx context.Context, token string, now, leaseUntil int64) (Lease, error)
// RecordLease stores what the lease will publish. ErrLeaseLost when fenced.
RecordLease(ctx context.Context, lease Lease, leased []byte, now int64) error
// Leased returns the recorded lease for a current token. ErrLeaseLost when fenced or expired.
Leased(ctx context.Context, repositoryID int64, number int, token string, now int64) (Lease, []byte, error)
// ReleaseRefresh ends a failed lease; the pull request stays pending until retryAt.
ReleaseRefresh(ctx context.Context, lease Lease, retryAt int64, detail string) error
// CompleteRefresh atomically admits every submission with its notification
// intent, marks the leased generation refreshed and records the observed
// state and, when nonempty, the captured head. A generation requested
// during the lease stays pending.
CompleteRefresh(ctx context.Context, lease Lease, state, head string, admissions []Admission, now int64) error
}
Store persists integration state atomically per operation.