Skip to content
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,28 @@ case errors.Is(err, forges.ErrServerError): // 5xx
}
```

The `factory` package imports every provider and returns a `Client` that already knows github.com, gitlab.com, codeberg.org, bitbucket.org and tangled.org. It detects and registers any other domain over https the first time a repository URL on it is used, which sends requests to that domain:

```go
import "github.com/git-pkgs/forge/factory"

client := factory.NewClient(forges.WithToken("git.example.com", token))

f, owner, repo, err := client.ForgeForURL(ctx, "https://git.example.com/octocat/hello-world")
if err != nil {
return err
}
issues, err := f.Issues().List(ctx, owner, repo, forges.ListIssueOpts{State: "open"})
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
```

Detection never uses plain HTTP, so an http-only instance has to be registered with `RegisterDomain` before its URLs are used:

```go
if err := client.RegisterDomain(ctx, "http://git.internal.example", token, factory.Builders()); err != nil {
return err
}
```

Incoming webhook deliveries from GitHub, GitLab and Gitea/Forgejo can be verified and parsed into a normalized `WebhookEvent`. Event types use the same names as `Webhook.Events`. Push, pull request, issue, comment and release deliveries carry typed fields. The raw JSON is always available in `Payload`.

An empty secret skips verification, so read the secret once at startup and refuse to run without it:
Expand Down
6 changes: 1 addition & 5 deletions commits.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,7 @@ func ResolvedCommitSHA(owner, repo, ref, sha string) (string, error) {
// ResolveCommit resolves ref to a full commit SHA for the repository at
// repoURL, routing to the forge registered for that URL's domain.
func (c *Client) ResolveCommit(ctx context.Context, repoURL, ref string) (string, error) {
domain, owner, repo, err := ParseRepoURL(repoURL)
if err != nil {
return "", err
}
f, err := c.forgeFor(domain)
f, owner, repo, err := c.ForgeForURL(ctx, repoURL)
if err != nil {
return "", err
}
Expand Down
58 changes: 58 additions & 0 deletions factory/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Package factory builds forges.Client values wired up with every provider, so
// callers can pass any repository URL without choosing a backend themselves.
package factory

import (
"net/http"

"github.com/git-pkgs/forge"
"github.com/git-pkgs/forge/bitbucket"
"github.com/git-pkgs/forge/gerrit"
"github.com/git-pkgs/forge/gitea"
"github.com/git-pkgs/forge/github"
"github.com/git-pkgs/forge/gitlab"
"github.com/git-pkgs/forge/radicle"
"github.com/git-pkgs/forge/tangled"
)

// Builders returns the constructors for every provider that can be detected on
// a self-hosted domain.
func Builders() forges.ForgeBuilders {
return forges.ForgeBuilders{
GitHub: github.NewWithBase,
GitLab: gitlab.New,
Gitea: gitea.New,
Gerrit: gerrit.New,
Tangled: tangled.New,
Radicle: radicle.New,
}
}

// DefaultForges returns a Forge for each well-known public domain, keyed by
// domain. The token function supplies the token for each domain and may return
// an empty string for anonymous access.
func DefaultForges(token func(domain string) string, hc *http.Client) map[string]forges.Forge {
return map[string]forges.Forge{
"github.com": github.New(token("github.com"), hc),
"gitlab.com": gitlab.New("https://gitlab.com", token("gitlab.com"), hc),
"codeberg.org": gitea.New("https://codeberg.org", token("codeberg.org"), hc),
"bitbucket.org": bitbucket.New(token("bitbucket.org"), hc),
"tangled.org": tangled.New("https://tangled.org", token("tangled.org"), hc),
}
}

// NewClient returns a Client with the default forges registered and detection
// enabled for every other domain. Options apply first, so WithToken and
// WithHTTPClient reach the default forges, and a domain registered with
// WithForge keeps that registration.
func NewClient(opts ...forges.Option) *forges.Client {
c := forges.NewClient(append([]forges.Option{forges.WithDetection(Builders())}, opts...)...)
tokens := c.Tokens()
defaults := DefaultForges(func(domain string) string { return tokens[domain] }, c.HTTPClient())
for domain, f := range defaults {
if _, err := c.ForgeFor(domain); err != nil {
forges.WithForge(domain, f)(c)
}
}
return c
}
Loading
Loading