Help us shape the future of Go! Take the 2026 Go Developer Survey

gitoperationslib

package module
v0.1.7 Latest Latest
Warning

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

Go to latest
Published: Oct 21, 2026 License: GPL-3.0 Imports: 8 Imported by: 0

README

gitoperationslib banner

___

gitoperationslib

A Go library for managing git forges through one set of interfaces. Forgejo is the primary backend; GitHub and Bitbucket Cloud are supported wherever their APIs overlap. Code written against the interfaces works with any backend; anything a backend cannot do is reported with a typed error instead of being silently skipped.

  • No third-party types in the public API: all types are this library's own.
  • The Forgejo and Bitbucket backends are small hand-written HTTP clients (no SDK); the GitHub backend uses google/go-github (pinned at v93) plus a minimal GraphQL client for the few features REST cannot do.
  • Every call takes a context.Context.

Contents

Features

Repositories, branches and tags, branch and tag protection, releases and assets, pull requests and reviews, issues (with labels, milestones and comments), webhooks (managing them and receiving deliveries), and Forgejo administration (users, organisations, teams).

What each backend supports, feature by feature, is in docs/support_grid.md.

Installation

go get github.com/jeanfrancoisgratton/gitoperationslib

Requirements: Go 1.27.2 or later; Forgejo 11.0.10 or later (checked when connecting).

Package Import path
Interfaces and types github.com/jeanfrancoisgratton/gitoperationslib
Forgejo backend github.com/jeanfrancoisgratton/gitoperationslib/forgejo
GitHub backend github.com/jeanfrancoisgratton/gitoperationslib/github
Bitbucket Cloud backend github.com/jeanfrancoisgratton/gitoperationslib/bitbucket
Receiving webhooks github.com/jeanfrancoisgratton/gitoperationslib/webhook

The examples below use these imports:

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"time"

	gol "github.com/jeanfrancoisgratton/gitoperationslib"
	"github.com/jeanfrancoisgratton/gitoperationslib/bitbucket"
	"github.com/jeanfrancoisgratton/gitoperationslib/forgejo"
	"github.com/jeanfrancoisgratton/gitoperationslib/github"
	"github.com/jeanfrancoisgratton/gitoperationslib/webhook"
)

gol is just a short alias for the root package; the examples assume a ctx context.Context and a client gol.Client.

Creating a client

Forgejo

forgejo.New connects, reads the server version and refuses servers older than the supported minimum.

// Personal access token (Settings → Applications on Forgejo).
client, err := forgejo.New(ctx, "https://git.example.com", forgejo.Token(os.Getenv("FORGEJO_TOKEN")))
if err != nil {
	var tooOld *gol.UnsupportedVersionError
	if errors.As(err, &tooOld) {
		log.Fatalf("server %s is older than %s", tooOld.Version, tooOld.Minimum)
	}
	log.Fatal(err)
}
fmt.Println("connected to Forgejo", client.Version())

// Username and password instead of a token:
client, err = forgejo.New(ctx, "https://git.example.com", forgejo.BasicAuth("alice", "secret"))

// Anonymous (public data only):
client, err = forgejo.New(ctx, "https://git.example.com", nil)

The base URL may be the instance root, include /api/v1, or point to a sub-path install (https://example.com/forgejo).

Options:

client, err := forgejo.New(ctx, "https://git.example.com", forgejo.Token(token),
	forgejo.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}), // proxies, custom CA, timeouts
	forgejo.WithUserAgent("my-tool/1.0"),
)
GitHub

github.New makes no request.

// Personal access token; fine-grained tokens are recommended.
client, err := github.New(os.Getenv("GITHUB_TOKEN"))

// GitHub Enterprise Server: "/api/v3/" is appended when missing.
client, err = github.New(token, github.WithEnterpriseURL("https://github.example.com", ""))

// Same options as Forgejo:
client, err = github.New(token, github.WithHTTPClient(httpClient), github.WithUserAgent("my-tool/1.0"))
Bitbucket Cloud

bitbucket.New makes no request. Repository owners are workspaces; new repositories go to the workspace's default project.

// Atlassian API token with Bitbucket scopes (ATATT...): Bitbucket only accepts it
// through basic authentication, with the Atlassian account e-mail.
client, err := bitbucket.New(bitbucket.APIToken("alice@example.com", os.Getenv("BITBUCKET_TOKEN")))

// Workspace, project or repository access token (ATCTT...), sent as a bearer token.
client, err = bitbucket.New(bitbucket.Token(os.Getenv("BITBUCKET_ACCESS_TOKEN")))

// Basic authentication: username (or Atlassian account e-mail) and app password.
client, err = bitbucket.New(bitbucket.BasicAuth("alice@example.com", appPassword))

// Same options as the others:
client, err = bitbucket.New(bitbucket.Token(token), bitbucket.WithHTTPClient(httpClient), bitbucket.WithUserAgent("my-tool/1.0"))

Bitbucket accounts have no public login name: fields such as Author, MergedBy or Allowlist.Users hold the user's nickname. Where a user is an input (reviewers, allowlists), a nickname, an account ID or a {UUID} is accepted; nicknames are looked up among the workspace's members.

Choosing at run time

*forgejo.Client, *github.Client and *bitbucket.Client all implement gol.Client, so the rest of your code does not need to know which forge it talks to:

func open(ctx context.Context, forge string) (gol.Client, error) {
	switch forge {
	case "forgejo":
		return forgejo.New(ctx, os.Getenv("FORGEJO_URL"), forgejo.Token(os.Getenv("FORGEJO_TOKEN")))
	case "github":
		return github.New(os.Getenv("GITHUB_TOKEN"))
	case "bitbucket":
		return bitbucket.New(bitbucket.APIToken(os.Getenv("BITBUCKET_EMAIL"), os.Getenv("BITBUCKET_TOKEN")))
	}
	return nil, fmt.Errorf("unknown forge %q", forge)
}

Core concepts

The client and its areas

gol.Client gives access to each area:

client.Repos()             // repositories
client.Branches()          // branches
client.Tags()              // tags
client.BranchProtections() // branch protection rules
client.TagProtections()    // tag protection rules
client.Releases()          // releases and assets
client.PullRequests()      // pull requests and reviews
client.Issues()            // issues and comments
client.Labels()            // repository labels
client.Milestones()        // milestones
client.Webhooks()          // repository and organisation webhooks
Capabilities and optional interfaces

Features only some backends have are exposed through optional interfaces. Check with Capabilities() or a type assertion:

fmt.Println(client.Backend(), client.Capabilities()) // e.g. "forgejo repos|admin|releases|…"

if client.Capabilities().Has(gol.CapAdmin) {
	admin := client.(gol.Admin) // users, organisations, teams (Forgejo only)
	_ = admin
}

if ext, ok := client.Releases().(gol.ExternalAssets); ok { // Forgejo only
	_ = ext
}
Errors
Error Meaning
gol.ErrNotSupported The backend cannot do this (match with errors.Is; details in *gol.NotSupportedError).
*gol.APIError The server returned an error: StatusCode, Message, Details, DocumentationURL.
*gol.UnsupportedVersionError The Forgejo server is too old.
gol.ErrOwnerTeam Refused to delete, rename or change an organisation's owner team.
repo, err := client.Repos().Get(ctx, "acme", "widget")
switch {
case gol.IsNotFound(err):
	fmt.Println("no such repository")
case gol.IsForbidden(err), gol.IsUnauthorized(err):
	fmt.Println("check the token's permissions")
case errors.Is(err, gol.ErrNotSupported):
	fmt.Println("not available on", client.Backend())
case err != nil:
	var apiErr *gol.APIError
	if errors.As(err, &apiErr) {
		fmt.Println(apiErr.StatusCode, apiErr.Message, apiErr.Details)
	}
default:
	fmt.Println(repo.FullName)
}

A few calls return a result and an ErrNotSupported error when the server silently ignored part of the request (for example a visibility change Forgejo does not honour). The returned value then shows the server's actual state.

Pagination

List methods take gol.ListOptions{Page, PerPage} (zero values mean "first page" and "server default") and return a gol.Page[T] with Items and NextPage (0 on the last page). gol.All iterates over every page:

// One page:
page, err := client.Repos().List(ctx, "acme", gol.ListOptions{PerPage: 50})
for _, r := range page.Items {
	fmt.Println(r.FullName)
}

// Everything:
fetch := func(ctx context.Context, o gol.ListOptions) (gol.Page[gol.Repo], error) {
	return client.Repos().List(ctx, "acme", o)
}
for repo, err := range gol.All(ctx, gol.ListOptions{PerPage: 50}, fetch) {
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(repo.FullName)
}
Optional fields

In Edit…Options structs, nil means "leave unchanged". gol.Ptr builds pointers inline:

_, err := client.Repos().Edit(ctx, "acme", "widget", gol.EditRepoOptions{
	Description: gol.Ptr("New description"),
	Archived:    gol.Ptr(true),
})

Repositories

repos := client.Repos()

// Create in the authenticated user's namespace (Owner empty), in an
// organisation (Owner: "acme"), or — Forgejo admins — for another user.
repo, err := repos.Create(ctx, gol.CreateRepoOptions{
	Owner:       "acme",
	Name:        "widget",
	Description: "Widget service",
	Visibility:  gol.VisibilityPrivate,
	AutoInit:    true,              // initial commit…
	Gitignores:  []string{"Go"},    // …with a .gitignore (GitHub: at most one)
	License:     "MIT",
	// DefaultBranch: "main",       // Forgejo only (GitHub: ErrNotSupported)
})

repo, err = repos.Get(ctx, "acme", "widget")
fmt.Println(repo.FullName, repo.Visibility, repo.DefaultBranch, repo.CloneURL, repo.SSHURL)

// List an organisation's or user's repositories; empty owner lists every
// repository the authenticated user can access.
page, err := repos.List(ctx, "acme", gol.ListOptions{})

// Rename, change settings, change the default branch.
repo, err = repos.Edit(ctx, "acme", "widget", gol.EditRepoOptions{
	Name:          gol.Ptr("widget-service"),
	Website:       gol.Ptr("https://widget.example.com"),
	Visibility:    gol.Ptr(gol.VisibilityPublic),
	DefaultBranch: gol.Ptr("main"),
})

err = repos.Delete(ctx, "acme", "widget-service")

Branches and tags

branches := client.Branches()

// From the default branch (From empty), a branch, a tag or a commit SHA.
br, err := branches.Create(ctx, "acme", "widget", gol.CreateBranchOptions{Name: "feature/login", From: "main"})
br, err = branches.Get(ctx, "acme", "widget", "feature/login")
fmt.Println(br.Name, br.SHA, br.Protected, br.IsDefault)

page, err := branches.List(ctx, "acme", "widget", gol.ListOptions{}) // IsDefault marks the default branch
br, err = branches.Rename(ctx, "acme", "widget", "feature/login", "feature/sign-in")
err = branches.Delete(ctx, "acme", "widget", "feature/sign-in")

The default branch is changed with Repos().Edit(… EditRepoOptions{DefaultBranch: …}).

tags := client.Tags()

// Lightweight tag on the default branch.
tag, err := tags.Create(ctx, "acme", "widget", gol.CreateTagOptions{Name: "v1.0.0"})

// Annotated tag (Message set) on a branch, tag or commit.
tag, err = tags.Create(ctx, "acme", "widget", gol.CreateTagOptions{
	Name: "v1.1.0", Target: "main", Message: "Release 1.1.0",
})

tag, err = tags.Get(ctx, "acme", "widget", "v1.1.0")
fmt.Println(tag.Name, tag.SHA, tag.Message, tag.TarballURL)

page, err := tags.List(ctx, "acme", "widget", gol.ListOptions{})
err = tags.Delete(ctx, "acme", "widget", "v1.0.0")

To publish a release for a tag, use Releases().Create: it also creates the tag if it does not exist yet.

Branch and tag protection

Rules are addressed by pattern (a branch name or glob). Update replaces a rule: get it, change it, update it. Zero values are the protective choice: deletion and force pushes are blocked unless explicitly allowed.

On GitHub, rules are repository rulesets; on free plans they are only available for public repositories.

bp := client.BranchProtections()

rule, err := bp.Create(ctx, "acme", "widget", gol.BranchProtection{
	Pattern:               "main",
	RequiredApprovals:     1,
	DismissStaleApprovals: true,
	RequiredStatusChecks:  []string{"ci/test"},
	RequireUpToDate:       true,  // branch must be up to date before merging
	RequireSignedCommits:  true,
	ApplyToAdmins:         true,  // no exemption for repository admins
	PushAllowlist:         &gol.Allowlist{Teams: []string{"release-managers"}},
})

// Glob rules.
_, err = bp.Create(ctx, "acme", "widget", gol.BranchProtection{Pattern: "release/*", RequiredApprovals: 2})

// Change a rule.
rule, err = bp.Get(ctx, "acme", "widget", "main")
rule.RequiredApprovals = 2
rule, err = bp.Update(ctx, "acme", "widget", "main", *rule)

rules, err := bp.List(ctx, "acme", "widget")
err = bp.Delete(ctx, "acme", "widget", "release/*")

PushAllowlist: nil lets anyone with write access push; an empty &gol.Allowlist{} blocks direct pushes (changes go through pull requests). Backend-specific fields (MergeAllowlist, ProtectedFilePatterns… on Forgejo; RequireLinearHistory, RequireConversationResolution on GitHub) return ErrNotSupported on the other backend.

tp := client.TagProtections()

// Only the release-managers team may create, move or delete v* tags.
// GitHub: an empty allowlist blocks everyone; Forgejo needs at least one entry.
tr, err := tp.Create(ctx, "acme", "widget", gol.TagProtection{
	Pattern:   "v*",
	Allowlist: gol.Allowlist{Teams: []string{"release-managers"}},
})
tr, err = tp.Update(ctx, "acme", "widget", "v*", gol.TagProtection{Pattern: "v*", Allowlist: gol.Allowlist{Users: []string{"alice"}}}) // users: Forgejo only
err = tp.Delete(ctx, "acme", "widget", "v*")

Releases and assets

The usual flow: create a draft, upload the files, then publish.

rels := client.Releases()

// Creates the tag from TargetCommitish if it does not exist yet (when the
// draft is published).
rel, err := rels.Create(ctx, "acme", "widget", gol.CreateReleaseOptions{
	TagName:         "v1.0.1",
	TargetCommitish: "main",
	Name:            "1.0.1",
	Body:            "## Changes\n- Fixed the login bug",
	Draft:           true,
})

// Upload binaries: any io.Reader, with its exact size. The content type is
// guessed from the name when empty.
f, err := os.Open("dist/tool_1.0.1_linux_amd64.tar.gz")
if err != nil {
	log.Fatal(err)
}
defer f.Close()
info, err := f.Stat()
asset, err := rels.UploadAsset(ctx, "acme", "widget", rel.ID, "tool_1.0.1_linux_amd64.tar.gz", f, info.Size(), "")
fmt.Println(asset.ID, asset.Name, asset.Size, asset.DownloadURL)

// Publish.
rel, err = rels.Edit(ctx, "acme", "widget", rel.ID, gol.EditReleaseOptions{Draft: gol.Ptr(false)})

Reading, editing and deleting:

rel, err := rels.GetByTag(ctx, "acme", "widget", "v1.0.1")
latest, err := rels.Latest(ctx, "acme", "widget") // newest published non-prerelease
page, err := rels.List(ctx, "acme", "widget", gol.ListOptions{})

assets, err := rels.ListAssets(ctx, "acme", "widget", rel.ID, gol.ListOptions{})
asset, err := rels.EditAsset(ctx, "acme", "widget", rel.ID, assetID, gol.EditAssetOptions{Name: gol.Ptr("tool-linux.tar.gz")})

// Download (streamed; close it).
rc, err := rels.DownloadAsset(ctx, "acme", "widget", rel.ID, assetID)
if err != nil {
	log.Fatal(err)
}
defer rc.Close()
out, _ := os.Create("tool.tar.gz")
defer out.Close()
_, err = io.Copy(out, rc)

err = rels.DeleteAsset(ctx, "acme", "widget", rel.ID, assetID)
err = rels.Delete(ctx, "acme", "widget", rel.ID) // the tag is kept

Backend-specific options:

// GitHub: generated notes and the "latest" flag.
_, err := rels.Create(ctx, "acme", "widget", gol.CreateReleaseOptions{
	TagName: "v2.0.0", GenerateReleaseNotes: true, MakeLatest: gol.Ptr(true),
})

// Forgejo: hide the source archives, and link files hosted elsewhere.
_, err = rels.Edit(ctx, "acme", "widget", rel.ID, gol.EditReleaseOptions{HideArchiveLinks: gol.Ptr(true)})
if ext, ok := rels.(gol.ExternalAssets); ok {
	_, err = ext.AddExternalAsset(ctx, "acme", "widget", rel.ID, "tool-mirror.tar.gz", "https://mirror.example.com/tool.tar.gz")
}

Pull requests and reviews

prs := client.PullRequests()

pr, err := prs.Create(ctx, "acme", "widget", gol.CreatePROptions{
	Title: "Add sign-in",
	Body:  "Closes #12",
	Head:  "feature/sign-in", // "user:branch" for a fork
	Base:  "main",
	Draft: true, // Forgejo: adds the "WIP: " title prefix
})
fmt.Println(pr.Number, pr.State, pr.Draft, pr.Head.SHA, pr.HTMLURL)

pr, err = prs.Get(ctx, "acme", "widget", pr.Number)
page, err := prs.List(ctx, "acme", "widget", gol.ListPROptions{State: gol.PRStateOpen})

// Mark ready for review (GitHub uses GraphQL), change the title, close, reopen.
pr, err = prs.Edit(ctx, "acme", "widget", pr.Number, gol.EditPROptions{Draft: gol.Ptr(false)})
pr, err = prs.Edit(ctx, "acme", "widget", pr.Number, gol.EditPROptions{Title: gol.Ptr("Add sign-in page")})
pr, err = prs.Edit(ctx, "acme", "widget", pr.Number, gol.EditPROptions{State: gol.Ptr(gol.PRStateClosed)})

On Forgejo, drafts are titles starting with WIP: or [WIP]; Title is returned as stored, prefix included.

Reviews:

err := prs.RequestReviewers(ctx, "acme", "widget", pr.Number, []string{"bob"}, []string{"backend-team"})
err = prs.RemoveReviewers(ctx, "acme", "widget", pr.Number, []string{"bob"}, nil)

// Approving or requesting changes on one's own pull request is refused by both forges.
review, err := prs.CreateReview(ctx, "acme", "widget", pr.Number, gol.CreateReviewOptions{
	Event: gol.ReviewEventApprove, // or ReviewEventRequestChanges, ReviewEventComment
	Body:  "Looks good",
})
reviews, err := prs.ListReviews(ctx, "acme", "widget", pr.Number, gol.ListOptions{})
for _, r := range reviews.Items {
	fmt.Println(r.Reviewer, r.State) // approved, changes_requested, commented, …
}

Merging:

err := prs.Merge(ctx, "acme", "widget", pr.Number, gol.MergeOptions{
	Method:       gol.MergeMethodSquash, // merge, squash, rebase; Forgejo also rebase-merge, fast-forward-only, manually-merged
	Title:        "Add sign-in (#42)",
	HeadSHA:      pr.Head.SHA,           // fail if the branch moved since it was reviewed
	DeleteBranch: true,
})
merged, err := prs.IsMerged(ctx, "acme", "widget", pr.Number)

// Merge automatically once required checks pass, or cancel that.
// GitHub: the repository must allow auto-merge and a required check must be pending.
err = prs.Merge(ctx, "acme", "widget", pr.Number, gol.MergeOptions{Method: gol.MergeMethodMerge, WhenChecksSucceed: true})
err = prs.CancelAutoMerge(ctx, "acme", "widget", pr.Number)

PullRequest.Mergeable is a *bool: GitHub computes it in the background, so it can be nil right after creation.

Issues, labels, milestones and comments

Labels are addressed by name; colours are six hex digits without #.

labels := client.Labels()
_, err := labels.Create(ctx, "acme", "widget", gol.Label{Name: "bug", Color: "d73a4a", Description: "Something is broken"})
_, err = labels.Edit(ctx, "acme", "widget", "bug", gol.EditLabelOptions{Name: gol.Ptr("defect"), Color: gol.Ptr("b60205")})
page, err := labels.List(ctx, "acme", "widget", gol.ListOptions{})
err = labels.Delete(ctx, "acme", "widget", "defect")

Milestones are addressed by Milestone.ID (Forgejo's ID, GitHub's milestone number):

ms, err := client.Milestones().Create(ctx, "acme", "widget", gol.CreateMilestoneOptions{
	Title: "v1.1", Description: "Next minor release", DueOn: time.Now().AddDate(0, 1, 0),
})
open, err := client.Milestones().List(ctx, "acme", "widget", gol.MilestoneStateOpen, gol.ListOptions{})
ms, err = client.Milestones().Edit(ctx, "acme", "widget", ms.ID, gol.EditMilestoneOptions{State: gol.Ptr(gol.MilestoneStateClosed)})

Issues (pull requests are never listed as issues, but label and comment methods accept pull request numbers too):

issues := client.Issues()

issue, err := issues.Create(ctx, "acme", "widget", gol.CreateIssueOptions{
	Title:     "Login fails with SSO",
	Body:      "Steps to reproduce: …",
	Labels:    []string{"bug"},
	Assignees: []string{"alice"},
	Milestone: ms.ID,
})

page, err := issues.List(ctx, "acme", "widget", gol.ListIssuesOptions{
	State:  gol.IssueStateOpen,
	Labels: []string{"bug"},
})

// Non-nil slices replace the current values; Milestone 0 removes it.
issue, err = issues.Edit(ctx, "acme", "widget", issue.Number, gol.EditIssueOptions{
	Labels:    &[]string{"bug", "needs-triage"},
	Assignees: &[]string{},           // unassign everyone
	Milestone: gol.Ptr(int64(0)),
})
err = issues.AddLabels(ctx, "acme", "widget", issue.Number, []string{"security"})
err = issues.RemoveLabel(ctx, "acme", "widget", issue.Number, "needs-triage")

// Comments.
c, err := issues.CreateComment(ctx, "acme", "widget", issue.Number, "Reproduced on main.")
c, err = issues.EditComment(ctx, "acme", "widget", c.ID, "Reproduced on main and v1.0.")
comments, err := issues.ListComments(ctx, "acme", "widget", issue.Number, gol.ListOptions{})
err = issues.DeleteComment(ctx, "acme", "widget", c.ID)

// Close, or delete permanently (repository admins; GitHub uses GraphQL).
issue, err = issues.Edit(ctx, "acme", "widget", issue.Number, gol.EditIssueOptions{State: gol.Ptr(gol.IssueStateClosed)})
err = issues.Delete(ctx, "acme", "widget", issue.Number)

Webhooks

Webhooks live on a repository (gol.RepoHooks) or an organisation (gol.OrgHooks). Event names follow GitHub's and are translated for Forgejo.

hooks := client.Webhooks()
scope := gol.RepoHooks("acme", "widget") // or gol.OrgHooks("acme")

hook, err := hooks.Create(ctx, scope, gol.CreateWebhookOptions{
	URL:    "https://ci.example.com/hooks/forge",
	Secret: os.Getenv("WEBHOOK_SECRET"), // signs deliveries; see "Receiving webhooks"
	Events: []gol.HookEvent{gol.HookEventPush, gol.HookEventPullRequest, gol.HookEventRelease},
	// ContentType: gol.HookContentForm, // default json
	// BranchFilter: "{main,release/*}",  // Forgejo only
})

err = hooks.Ping(ctx, scope, hook.ID) // test delivery

hook, err = hooks.Edit(ctx, scope, hook.ID, gol.EditWebhookOptions{
	Events: []gol.HookEvent{gol.HookEventPush}, // replaces the subscription list
	Active: gol.Ptr(false),
})
page, err := hooks.List(ctx, scope, gol.ListOptions{})
err = hooks.Delete(ctx, scope, hook.ID)

Forgejo silently drops event names it does not know; Create and Edit then return the webhook and an error naming the dropped events. Hook URLs can embed credentials: avoid logging Webhook.URL.

Receiving webhooks

The webhook package verifies signatures (HMAC-SHA256, constant-time), identifies the forge and the event, and decodes common payloads. It does not depend on either backend.

secret := []byte(os.Getenv("WEBHOOK_SECRET"))

http.Handle("/hooks/forge", webhook.Handler(secret, func(ctx context.Context, ev *webhook.Event) error {
	log.Printf("%s %s from %s on %s (delivery %s)", ev.Forge, ev.Name, ev.Sender, ev.Repo, ev.Delivery)

	switch ev.Name {
	case "push":
		push, err := ev.Push()
		if err != nil {
			return err
		}
		if branch := push.Branch(); branch == "main" {
			log.Printf("main moved to %s (%d commits) by %s", push.After, len(push.Commits), push.Pusher)
		}
	case "pull_request":
		pr, err := ev.PullRequest()
		if err != nil {
			return err
		}
		log.Printf("PR #%d %s: %s", pr.Number, pr.Action, pr.PullRequest.Title)
	case "release":
		rel, err := ev.Release()
		if err != nil {
			return err
		}
		if rel.Action == "published" {
			log.Printf("released %s", rel.Release.TagName)
		}
	default:
		// Anything else: ev.Payload holds the raw JSON.
	}
	return nil
}))
log.Fatal(http.ListenAndServe(":8080", nil))

Handler answers 401 for missing or invalid signatures, 400 for undecodable deliveries, 500 when your function fails and 204 otherwise. For your own server, the lower-level functions are available:

func serve(w http.ResponseWriter, r *http.Request) {
	ev, err := webhook.Parse(r, secret) // reads (max 25 MB), verifies and decodes
	if errors.Is(err, webhook.ErrInvalidSignature) || errors.Is(err, webhook.ErrMissingSignature) {
		http.Error(w, "bad signature", http.StatusUnauthorized)
		return
	}
	// …
	_ = ev
}

Or with a body and headers you already have:

if err := webhook.Verify(body, header, secret); err != nil {
	return err
}
ev, err := webhook.Decode(header, body)

Forgejo's test delivery (Webhooks().Ping) is a push event; GitHub's is a ping event.

Bitbucket deliveries are recognised by their X-Event-Key header and verified with X-Hub-Signature (sha256=…). Their keys are mapped to the same names: repo:push is push, pullrequest:created/updated/fulfilled/rejected are pull_request with action opened/edited/closed/closed, and approvals or change requests are pull_request_review; ev.Key keeps the raw key. Push() and PullRequest() decode Bitbucket's payloads; a push updating several refs is decoded from its first change. This support follows Bitbucket's documentation and has not yet been checked against live deliveries.

Administration (Forgejo)

gol.Admin (Forgejo only) manages accounts. User management needs a site-admin token; organisations and teams need the corresponding owner rights.

admin, ok := client.(gol.Admin)
if !ok {
	log.Fatal("this backend has no administration API")
}
Users
users := admin.Users()

u, err := users.Create(ctx, gol.CreateUserOptions{
	Login:              "alice",
	Email:              "alice@example.com",
	Password:           "initial-password",
	FullName:           "Alice Example",
	MustChangePassword: gol.Ptr(true), // nil: Forgejo's default (true)
	Visibility:         gol.OwnerVisibilityLimited,
})

u, err = users.Edit(ctx, "alice", gol.EditUserOptions{
	Location:      gol.Ptr("Montréal"),
	ProhibitLogin: gol.Ptr(true),
})
err = users.Rename(ctx, "alice", "alice.example")
page, err := users.List(ctx, gol.ListOptions{})

// Purge also deletes the user's repositories; without it, deleting a user
// who owns repositories fails.
err = users.Delete(ctx, "alice.example", gol.DeleteUserOptions{Purge: true})

Forgejo 15 ignores a user visibility change back to public; Edit then returns the user together with an ErrNotSupported error.

Organisations
orgs := admin.Orgs()

org, err := orgs.Create(ctx, gol.CreateOrgOptions{
	Name:       "acme",
	FullName:   "Acme Inc.",
	Visibility: gol.OwnerVisibilityPrivate,
	// Owner: "alice", // create it for another user (site admin)
})
org, err = orgs.Edit(ctx, "acme", gol.EditOrgOptions{Description: gol.Ptr("Widgets since 1999")})
mine, err := orgs.List(ctx, "", gol.ListOptions{})   // the authenticated user's organisations
all, err := orgs.ListAll(ctx, gol.ListOptions{})     // every visible organisation

members, err := orgs.Members(ctx, "acme", gol.ListOptions{})
isMember, err := orgs.IsMember(ctx, "acme", "bob")
err = orgs.RemoveMember(ctx, "acme", "bob")

err = orgs.Rename(ctx, "acme", "acme-corp")
err = orgs.Delete(ctx, "acme-corp") // fails while it still owns repositories

Members join an organisation through its teams.

Teams
teams := admin.Teams()

// Per-unit access…
devs, err := teams.Create(ctx, "acme", gol.CreateTeamOptions{
	Name: "developers",
	Access: gol.TeamAccess{Units: map[gol.TeamUnit]gol.TeamPermission{
		gol.TeamUnitCode:   gol.TeamPermissionWrite,
		gol.TeamUnitPulls:  gol.TeamPermissionWrite,
		gol.TeamUnitIssues: gol.TeamPermissionRead,
	}},
	CanCreateOrgRepo: true,
})

// …or one level on every unit: Permission read/write, or admin.
ops, err := teams.Create(ctx, "acme", gol.CreateTeamOptions{
	Name: "ops", Access: gol.TeamAccess{Permission: gol.TeamPermissionAdmin}, IncludesAllRepos: true,
})

owners, err := teams.GetByName(ctx, "acme", "Owners")
err = teams.AddMember(ctx, devs.ID, "bob") // also makes bob an organisation member
err = teams.AddRepo(ctx, devs.ID, "acme", "widget")
members, err := teams.Members(ctx, devs.ID, gol.ListOptions{})

devs, err = teams.Edit(ctx, devs.ID, gol.EditTeamOptions{Access: &gol.TeamAccess{Permission: gol.TeamPermissionRead}})
err = teams.Delete(ctx, ops.ID)

// The owner team is protected: deleting, renaming or changing its access
// returns gol.ErrOwnerTeam.
err = teams.Delete(ctx, owners.ID) // errors.Is(err, gol.ErrOwnerTeam)

Backend differences

Where the forges differ, the library either adapts or returns ErrNotSupported. The full per-feature grid for Forgejo, GitHub and Bitbucket is in docs/support_grid.md; docs/TODO.md lists open items and the reasons behind each limitation.

Testing

Unit tests need nothing but Go:

go test -race ./...

Integration tests are behind the integration build tag and skip themselves unless their environment variables are set. They only create and delete repositories, users and organisations named gitopslib-it-….

Suite Environment
Forgejo contract checks GITOPSLIB_FORGEJO_URL, GITOPSLIB_FORGEJO_TOKEN (scopes: repository, organization, user, issue — read and write), GITOPSLIB_FORGEJO_ORG (a throwaway organisation the token's user owns)
GitHub contract checks GITOPSLIB_GITHUB_TOKEN, GITOPSLIB_GITHUB_ORG (an organisation, or the account's own login)
Bitbucket contract checks GITOPSLIB_BITBUCKET_TOKEN (Atlassian API token with Bitbucket scopes, or an access token), GITOPSLIB_BITBUCKET_EMAIL (the Atlassian account e-mail; required with an API token, unset with an access token), GITOPSLIB_BITBUCKET_WORKSPACE (optional when the token sees a single workspace)
Forgejo admin checks the disposable instance below

Admin checks need a site-admin token, so they only run against a disposable local Forgejo (Docker), and refuse any non-loopback URL:

scripts/forgejo-testenv.sh start   # starts Forgejo 15 on 127.0.0.1:3999, writes .env.testenv
set -a; source .env; source .env.testenv; set +a
go test -tags integration ./...
scripts/forgejo-testenv.sh stop    # removes the container and .env.testenv

GitHub's protection checks briefly create a public repository (rulesets need one on free plans). Running the GitHub suites many times in a row can trigger GitHub's secondary rate limit on repository creation (a temporary block of up to an hour).

License

Copyright (C) 2026 Jean-François Gratton

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file for details.

SPDX-License-Identifier: GPL-3.0-or-later

Documentation

Overview

Package gitoperationslib wraps git forge operations behind a common set of interfaces. Forgejo is the primary backend; GitHub is supported where the two APIs overlap.

Index

Constants

This section is empty.

Variables

AllTeamUnits lists every TeamUnit.

View Source
var ErrNotSupported = errors.New("operation not supported")

ErrNotSupported is matched (via errors.Is) by every error returned when a backend cannot perform an operation, instead of silently degrading.

View Source
var ErrOwnerTeam = errors.New("refusing to delete, rename or change the permissions of an owner team")

ErrOwnerTeam is returned when an operation would delete an organisation's owner team, rename it, or change its permissions. Forgejo allows deleting it, which leaves the organisation without owners.

Functions

func All

func All[T any](ctx context.Context, opts ListOptions, fetch func(context.Context, ListOptions) (Page[T], error)) iter.Seq2[T, error]

All iterates over every item of a paginated list, starting at opts.Page. Iteration stops at the first error, which is yielded with a zero item.

for repo, err := range gitoperationslib.All(ctx, opts, func(ctx context.Context, o gitoperationslib.ListOptions) (gitoperationslib.Page[gitoperationslib.Repo], error) {
	return client.Repos().List(ctx, "owner", o)
}) { ... }

func IsForbidden

func IsForbidden(err error) bool

func IsNotFound

func IsNotFound(err error) bool

func IsUnauthorized

func IsUnauthorized(err error) bool

func NotSupported

func NotSupported(backend Backend, operation, reason string) error

NotSupported returns a *NotSupportedError.

func Ptr

func Ptr[T any](v T) *T

Ptr returns a pointer to v, for filling optional fields.

func StatusCode

func StatusCode(err error) int

StatusCode returns the HTTP status of the first *APIError in err's chain, or 0 if there is none.

Types

type APIError

type APIError struct {
	Backend    Backend
	StatusCode int
	Message    string
	// DocumentationURL is GitHub's documentation_url or Forgejo's url field.
	DocumentationURL string
	// Details holds GitHub's errors[] entries, flattened to strings.
	Details []string
}

APIError is the normalised form of an error response from either backend.

func (*APIError) Error

func (e *APIError) Error() string

type Admin

type Admin interface {
	Users() Users
	Orgs() Orgs
	Teams() Teams
}

Admin manages accounts. Only the Forgejo backend implements it; detect it with a type assertion (client.(gitoperationslib.Admin)) or CapAdmin.

type Allowlist

type Allowlist struct {
	Users []string // logins
	Teams []string // team names (GitHub: team slugs; organisations only)
}

Allowlist names users and teams exempt from a restriction. On GitHub, rules are rulesets whose exemptions ("bypass actors") can be teams but not individual users, so Users returns ErrNotSupported there.

type Asset

type Asset struct {
	ID            int64
	Name          string
	Size          int64
	ContentType   string // reported by GitHub only
	DownloadCount int64
	DownloadURL   string // browser download URL
	// External marks a Forgejo asset that links to a file hosted elsewhere
	// (DownloadURL) instead of an uploaded file.
	External  bool
	CreatedAt time.Time
}

Asset is a file attached to a release.

type Backend

type Backend string

Backend identifies which forge implementation a Client talks to.

const (
	BackendForgejo Backend = "forgejo"
	BackendGitHub  Backend = "github"
	// BackendBitbucket is Bitbucket Cloud (REST API 2.0).
	BackendBitbucket Backend = "bitbucket"
)

type Branch

type Branch struct {
	Name      string
	SHA       string // head commit
	Protected bool
	IsDefault bool
}

Branch is a repository branch.

type BranchProtection

type BranchProtection struct {
	ID int64 // GitHub ruleset ID; 0 on Forgejo
	// Pattern is a branch name or glob ("main", "release/*"). Rules are
	// addressed by it.
	Pattern string

	RequiredApprovals     int
	DismissStaleApprovals bool // a new push dismisses earlier approvals
	// RequiredStatusChecks lists the status check contexts that must pass.
	RequiredStatusChecks []string
	// RequireUpToDate requires the branch to be up to date with the base
	// before merging (GitHub strict checks; Forgejo blocks outdated
	// branches).
	RequireUpToDate      bool
	RequireSignedCommits bool
	// ApplyToAdmins enforces the rule for repository administrators too.
	ApplyToAdmins bool
	// PushAllowlist restricts direct pushes to the listed users and teams;
	// nil lets anyone with write access push. An empty Allowlist blocks
	// direct pushes entirely (changes then go through pull requests).
	PushAllowlist *Allowlist

	AllowDeletion  bool // Forgejo: not supported (protected branches cannot be deleted)
	AllowForcePush bool // Forgejo: not supported

	// GitHub only.
	RequireLinearHistory          bool
	RequireConversationResolution bool

	// Forgejo only.
	MergeAllowlist                *Allowlist // who may merge pull requests; nil: anyone with write access
	ApprovalsAllowlist            *Allowlist // whose approvals count; nil: anyone with write access
	BlockOnRejectedReviews        bool
	BlockOnOfficialReviewRequests bool
	ProtectedFilePatterns         string // semicolon-separated globs
	UnprotectedFilePatterns       string
}

BranchProtection is a protection rule for the branches matching Pattern. On GitHub it is a branch ruleset; on Forgejo a branch protection rule.

Zero values are the protected choice: deletion and force pushes are blocked unless explicitly allowed.

type BranchProtections

type BranchProtections interface {
	List(ctx context.Context, owner, repo string) ([]BranchProtection, error)
	Get(ctx context.Context, owner, repo, pattern string) (*BranchProtection, error)
	Create(ctx context.Context, owner, repo string, rule BranchProtection) (*BranchProtection, error)
	Update(ctx context.Context, owner, repo, pattern string, rule BranchProtection) (*BranchProtection, error)
	Delete(ctx context.Context, owner, repo, pattern string) error
}

BranchProtections manages branch protection rules, addressed by pattern. Update replaces a rule entirely: Get it, change it, then Update.

type Branches

type Branches interface {
	// List returns the branches; IsDefault marks the default one.
	List(ctx context.Context, owner, repo string, opts ListOptions) (Page[Branch], error)
	Get(ctx context.Context, owner, repo, name string) (*Branch, error)
	Create(ctx context.Context, owner, repo string, opts CreateBranchOptions) (*Branch, error)
	// Rename renames a branch. Open pull requests and protection rules
	// follow it.
	Rename(ctx context.Context, owner, repo, name, newName string) (*Branch, error)
	Delete(ctx context.Context, owner, repo, name string) error
}

Branches manages repository branches. The default branch is changed with Repos().Edit.

type Capability

type Capability uint64

Capability is a bit set of features a Client supports.

const (
	CapRepos Capability = 1 << iota
	CapMirrors
	CapMigrate
	CapAdmin
	CapWebhooks
	CapSystemWebhooks
	CapReleases
	CapPullRequests
	CapExternalAssets
	CapBranches
	CapTags
	CapBranchProtection
	CapTagProtection
	CapIssues
)

func (Capability) Has

func (c Capability) Has(want Capability) bool

Has reports whether c includes every capability in want.

func (Capability) String

func (c Capability) String() string

String lists the named capabilities in c, separated by "|".

type Client

type Client interface {
	Backend() Backend
	Capabilities() Capability
	Repos() Repos
	Releases() Releases
	PullRequests() PullRequests
	Webhooks() Webhooks
	Branches() Branches
	Tags() Tags
	BranchProtections() BranchProtections
	TagProtections() TagProtections
	Issues() Issues
	Labels() Labels
	Milestones() Milestones
}

Client is the entry point to a forge backend.

Areas a backend may not support are exposed through optional interfaces; check Capabilities, or use a type assertion, before relying on them.

type Comment

type Comment struct {
	ID        int64
	Author    string
	Body      string
	HTMLURL   string
	CreatedAt time.Time
	UpdatedAt time.Time
}

Comment is a comment on an issue or pull request.

type CreateBranchOptions

type CreateBranchOptions struct {
	Name string // required
	// From is the branch, tag or commit SHA to start from; empty means the
	// default branch.
	From string
}

CreateBranchOptions describes a branch to create.

type CreateIssueOptions

type CreateIssueOptions struct {
	Title     string // required
	Body      string
	Labels    []string // names; they must exist
	Assignees []string
	Milestone int64 // Milestone.ID; 0 for none
}

CreateIssueOptions describes an issue to open.

type CreateMilestoneOptions

type CreateMilestoneOptions struct {
	Title       string // required
	Description string
	DueOn       time.Time // zero: none
}

CreateMilestoneOptions describes a milestone to create.

type CreateOrgOptions

type CreateOrgOptions struct {
	// Owner is the user who will own the organisation. Empty means the
	// authenticated user; another user requires a site-admin token.
	Owner                     string
	Name                      string // required
	FullName                  string
	Description               string
	Website                   string
	Location                  string
	Email                     string
	Visibility                OwnerVisibility // zero value: backend default
	RepoAdminChangeTeamAccess bool
}

CreateOrgOptions describes an organisation to create.

type CreatePROptions

type CreatePROptions struct {
	Title string // required
	Body  string
	Head  string // branch with the changes; "user:branch" for a fork
	Base  string // branch to merge into
	// Draft opens the pull request as a draft. On Forgejo this adds the
	// "WIP: " prefix to Title unless it already has a draft prefix.
	Draft               bool
	AllowMaintainerEdit *bool // let base maintainers push to the head branch
}

CreatePROptions describes a pull request to open.

type CreateReleaseOptions

type CreateReleaseOptions struct {
	TagName         string // required
	TargetCommitish string
	Name            string
	Body            string
	Draft           bool
	Prerelease      bool

	// GenerateReleaseNotes asks the server to write the notes from merged
	// pull requests (GitHub only).
	GenerateReleaseNotes bool
	// MakeLatest controls whether the release becomes the "latest" one;
	// nil means the server decides (GitHub only).
	MakeLatest *bool
	// HideArchiveLinks hides the generated source archives (Forgejo only).
	HideArchiveLinks *bool
}

CreateReleaseOptions describes a release to create.

If TagName does not exist yet, it is created from TargetCommitish (the default branch when empty). For a draft, both backends create the tag only when the draft is published.

type CreateRepoOptions

type CreateRepoOptions struct {
	// Owner is the user or organisation that will own the repository; the
	// backend determines which. Empty means the authenticated user. Naming a
	// user other than the authenticated one requires an admin token (Forgejo)
	// and is not supported on GitHub.
	Owner         string
	Name          string
	Description   string
	Visibility    Visibility // zero value: backend default
	DefaultBranch string
	Template      bool
	// AutoInit creates an initial commit, using Gitignores and License if set.
	AutoInit bool
	// Gitignores lists gitignore template names. GitHub accepts at most one
	// and returns ErrNotSupported otherwise.
	Gitignores []string
	License    string // license template name
}

CreateRepoOptions describes a repository to create.

type CreateReviewOptions

type CreateReviewOptions struct {
	Event    ReviewEvent // required
	Body     string      // required for request_changes and comment
	CommitID string      // empty: the current head
}

CreateReviewOptions submits a review. Authors cannot approve or request changes on their own pull requests.

type CreateTagOptions

type CreateTagOptions struct {
	Name string // required
	// Target is the branch, tag or commit SHA to tag; empty means the
	// default branch.
	Target string
	// Message makes an annotated tag; empty makes a lightweight tag.
	Message string
}

CreateTagOptions describes a tag to create. To publish a release for a tag, use Releases().Create, which also creates the tag when needed.

type CreateTeamOptions

type CreateTeamOptions struct {
	Name             string // required
	Description      string
	Access           TeamAccess // required
	IncludesAllRepos bool
	CanCreateOrgRepo bool
}

CreateTeamOptions describes a team to create.

type CreateUserOptions

type CreateUserOptions struct {
	Login    string // required
	Email    string // required
	Password string
	FullName string
	// MustChangePassword forces a password change at first sign-in.
	// Nil means the Forgejo default, which is true.
	MustChangePassword *bool
	Visibility         OwnerVisibility // zero value: backend default
	Restricted         bool
	// SendNotify emails the user about the new account.
	SendNotify bool
}

CreateUserOptions describes a user account to create.

type CreateWebhookOptions

type CreateWebhookOptions struct {
	URL         string          // required
	ContentType HookContentType // zero value: json
	// Secret signs deliveries (see the webhook package). Strongly
	// recommended.
	Secret string
	Events []HookEvent // required
	Active *bool       // nil: active
	// BranchFilter limits push and branch events to matching branches, as a
	// glob such as "{main,release/*}" (Forgejo only).
	BranchFilter string
	// AuthorizationHeader is sent with every delivery (Forgejo only).
	AuthorizationHeader string
}

CreateWebhookOptions describes a webhook to create.

type DeleteUserOptions

type DeleteUserOptions struct {
	// Purge also deletes the user's repositories, organisation memberships
	// and other content. Without it, deleting a user who still owns
	// repositories fails.
	Purge bool
}

DeleteUserOptions controls user deletion.

type EditAssetOptions

type EditAssetOptions struct {
	Name *string
}

EditAssetOptions changes an asset. Nil fields are left unchanged.

type EditIssueOptions

type EditIssueOptions struct {
	Title     *string
	Body      *string
	State     *IssueState // close or reopen
	Labels    *[]string
	Assignees *[]string
	Milestone *int64 // 0 removes the milestone
}

EditIssueOptions changes an issue. Nil fields are left unchanged; non-nil slices replace the current values (an empty slice clears them).

type EditLabelOptions

type EditLabelOptions struct {
	Name        *string // renames the label
	Color       *string
	Description *string
}

EditLabelOptions changes a label. Nil fields are left unchanged.

type EditMilestoneOptions

type EditMilestoneOptions struct {
	Title       *string
	Description *string
	State       *MilestoneState // close or reopen
	DueOn       *time.Time
}

EditMilestoneOptions changes a milestone. Nil fields are left unchanged.

type EditOrgOptions

type EditOrgOptions struct {
	FullName                  *string
	Description               *string
	Website                   *string
	Location                  *string
	Email                     *string
	Visibility                *OwnerVisibility
	RepoAdminChangeTeamAccess *bool
}

EditOrgOptions changes an organisation. Nil fields are left unchanged.

type EditPROptions

type EditPROptions struct {
	Title *string
	Body  *string
	Base  *string
	// State closes (PRStateClosed) or reopens (PRStateOpen) the pull request.
	State *PRState
	// Draft converts to or from a draft. Forgejo adds or removes the title
	// prefix; GitHub uses its GraphQL API.
	Draft               *bool
	AllowMaintainerEdit *bool
}

EditPROptions changes a pull request. Nil fields are left unchanged.

type EditReleaseOptions

type EditReleaseOptions struct {
	TagName         *string
	TargetCommitish *string
	Name            *string
	Body            *string
	Draft           *bool
	Prerelease      *bool

	MakeLatest       *bool // GitHub only
	HideArchiveLinks *bool // Forgejo only
}

EditReleaseOptions changes a release. Nil fields are left unchanged. Setting Draft to false publishes a draft.

type EditRepoOptions

type EditRepoOptions struct {
	Name          *string
	Description   *string
	Website       *string
	Visibility    *Visibility
	DefaultBranch *string
	Archived      *bool
	Template      *bool
}

EditRepoOptions changes repository settings. Nil fields are left unchanged; use Ptr to set a value.

type EditTeamOptions

type EditTeamOptions struct {
	Name             *string
	Description      *string
	Access           *TeamAccess
	IncludesAllRepos *bool
	CanCreateOrgRepo *bool
}

EditTeamOptions changes a team. Nil fields are left unchanged.

type EditUserOptions

type EditUserOptions struct {
	Email                   *string
	FullName                *string
	Description             *string
	Website                 *string
	Location                *string
	Password                *string
	MustChangePassword      *bool
	Visibility              *OwnerVisibility
	Admin                   *bool
	Active                  *bool
	ProhibitLogin           *bool
	Restricted              *bool
	AllowCreateOrganization *bool
	MaxRepoCreation         *int // -1 means the instance default
}

EditUserOptions changes a user account. Nil fields are left unchanged.

type EditWebhookOptions

type EditWebhookOptions struct {
	URL                 *string
	ContentType         *HookContentType
	Secret              *string
	Events              []HookEvent
	Active              *bool
	BranchFilter        *string // Forgejo only
	AuthorizationHeader *string // Forgejo only
}

EditWebhookOptions changes a webhook. Nil fields are left unchanged; a non-nil Events replaces the subscription list.

type ExternalAssets

type ExternalAssets interface {
	AddExternalAsset(ctx context.Context, owner, repo string, releaseID int64, name, url string) (*Asset, error)
}

ExternalAssets adds release assets that link to files hosted elsewhere. Only the Forgejo backend implements it (CapExternalAssets), on the value returned by Releases():

ext, ok := client.Releases().(gitoperationslib.ExternalAssets)

type HookContentType

type HookContentType string

HookContentType is the encoding of webhook payloads.

const (
	HookContentJSON HookContentType = "json"
	HookContentForm HookContentType = "form" // payload=<json>, URL-encoded
)

type HookEvent

type HookEvent string

HookEvent is an event a webhook subscribes to. The constants use GitHub's names and are translated for Forgejo; other values are passed to the server unchanged, for backend-specific events.

const (
	HookEventPush              HookEvent = "push"
	HookEventCreate            HookEvent = "create" // branch or tag created
	HookEventDelete            HookEvent = "delete" // branch or tag deleted
	HookEventFork              HookEvent = "fork"
	HookEventRelease           HookEvent = "release"
	HookEventIssues            HookEvent = "issues"
	HookEventIssueComment      HookEvent = "issue_comment"
	HookEventPullRequest       HookEvent = "pull_request"
	HookEventPullRequestReview HookEvent = "pull_request_review"
	HookEventRepository        HookEvent = "repository"
	HookEventWiki              HookEvent = "wiki" // GitHub: gollum
)

type HookScope

type HookScope struct {
	Owner string
	Repo  string
}

HookScope selects where webhooks live: a repository, or an organisation when Repo is empty.

func OrgHooks

func OrgHooks(org string) HookScope

OrgHooks is the scope of an organisation's webhooks.

func RepoHooks

func RepoHooks(owner, repo string) HookScope

RepoHooks is the scope of a repository's webhooks.

type Issue

type Issue struct {
	Number    int
	Title     string
	Body      string
	State     IssueState
	Author    string
	Labels    []string // names
	Assignees []string // logins
	Milestone *Milestone
	Comments  int
	Locked    bool
	HTMLURL   string
	CreatedAt time.Time
	UpdatedAt time.Time
	ClosedAt  time.Time
}

Issue is an issue. Issues and pull requests share one number sequence; label, assignee and milestone methods also work on pull requests.

type IssueState

type IssueState string

IssueState is the state of an issue. IssueStateAll is only meaningful as a List filter.

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

type Issues

type Issues interface {
	List(ctx context.Context, owner, repo string, opts ListIssuesOptions) (Page[Issue], error)
	Get(ctx context.Context, owner, repo string, number int) (*Issue, error)
	Create(ctx context.Context, owner, repo string, opts CreateIssueOptions) (*Issue, error)
	Edit(ctx context.Context, owner, repo string, number int, opts EditIssueOptions) (*Issue, error)
	// Delete removes an issue permanently (repository admins only; GitHub
	// through GraphQL).
	Delete(ctx context.Context, owner, repo string, number int) error

	AddLabels(ctx context.Context, owner, repo string, number int, labels []string) error
	RemoveLabel(ctx context.Context, owner, repo string, number int, label string) error

	ListComments(ctx context.Context, owner, repo string, number int, opts ListOptions) (Page[Comment], error)
	CreateComment(ctx context.Context, owner, repo string, number int, body string) (*Comment, error)
	EditComment(ctx context.Context, owner, repo string, id int64, body string) (*Comment, error)
	DeleteComment(ctx context.Context, owner, repo string, id int64) error
}

Issues manages issues and their comments. Label and comment methods also accept pull request numbers.

type Label

type Label struct {
	ID          int64
	Name        string
	Color       string // six hex digits, lower case, without "#"
	Description string
}

Label is a repository label.

type Labels

type Labels interface {
	List(ctx context.Context, owner, repo string, opts ListOptions) (Page[Label], error)
	Get(ctx context.Context, owner, repo, name string) (*Label, error)
	Create(ctx context.Context, owner, repo string, label Label) (*Label, error)
	Edit(ctx context.Context, owner, repo, name string, opts EditLabelOptions) (*Label, error)
	Delete(ctx context.Context, owner, repo, name string) error
}

Labels manages repository labels, addressed by name.

type ListIssuesOptions

type ListIssuesOptions struct {
	ListOptions
	State  IssueState // zero value: open
	Labels []string   // issues carrying all these labels
}

ListIssuesOptions selects issues to list. Pull requests are never included.

type ListOptions

type ListOptions struct {
	Page    int // 1-based
	PerPage int
}

ListOptions selects one page of a list call. Zero values mean "first page" and "backend default page size".

type ListPROptions

type ListPROptions struct {
	ListOptions
	State PRState // zero value: open
}

ListPROptions selects pull requests to list.

type MergeMethod

type MergeMethod string

MergeMethod is the strategy used to merge a pull request. Values match Forgejo's "Do" field; the GitHub backend supports only MergeMethodMerge, MergeMethodSquash and MergeMethodRebase.

MergeMethodRebase replays the commits without a merge commit; MergeMethodRebaseMerge rebases and then adds a merge commit; MergeMethodManuallyMerged only marks a pull request whose commits are already in the base branch as merged.

const (
	MergeMethodMerge           MergeMethod = "merge"
	MergeMethodRebase          MergeMethod = "rebase"
	MergeMethodRebaseMerge     MergeMethod = "rebase-merge"
	MergeMethodSquash          MergeMethod = "squash"
	MergeMethodFastForwardOnly MergeMethod = "fast-forward-only"
	MergeMethodManuallyMerged  MergeMethod = "manually-merged"
)

func (MergeMethod) Valid

func (m MergeMethod) Valid() bool

Valid reports whether m is one of the named merge methods.

type MergeOptions

type MergeOptions struct {
	// Method defaults to MergeMethodMerge. GitHub supports only merge,
	// squash and rebase.
	Method  MergeMethod
	Title   string // merge or squash commit title; empty: server default
	Message string // commit message body; empty: server default
	// HeadSHA makes the merge fail unless the head branch is still at this
	// commit.
	HeadSHA string
	// DeleteBranch deletes the head branch after a successful merge.
	DeleteBranch bool
	// WhenChecksSucceed schedules the merge for when status checks pass;
	// cancel it with CancelAutoMerge. GitHub (through GraphQL) accepts it
	// only in repositories that allow auto-merge, while the pull request
	// still waits for a required check, and not together with DeleteBranch.
	WhenChecksSucceed bool
}

MergeOptions controls a merge.

type Milestone

type Milestone struct {
	// ID addresses the milestone: Forgejo's ID, GitHub's milestone number.
	ID           int64
	Title        string
	Description  string
	State        MilestoneState
	DueOn        time.Time // zero if none
	OpenIssues   int
	ClosedIssues int
	ClosedAt     time.Time
}

Milestone is a repository milestone.

type MilestoneState

type MilestoneState string

MilestoneState is the state of a milestone; MilestoneStateAll is only meaningful as a List filter.

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

type Milestones

type Milestones interface {
	List(ctx context.Context, owner, repo string, state MilestoneState, opts ListOptions) (Page[Milestone], error)
	Get(ctx context.Context, owner, repo string, id int64) (*Milestone, error)
	Create(ctx context.Context, owner, repo string, opts CreateMilestoneOptions) (*Milestone, error)
	Edit(ctx context.Context, owner, repo string, id int64, opts EditMilestoneOptions) (*Milestone, error)
	Delete(ctx context.Context, owner, repo string, id int64) error
}

Milestones manages repository milestones, addressed by Milestone.ID.

type NotSupportedError

type NotSupportedError struct {
	Backend   Backend
	Operation string
	Reason    string // optional
}

NotSupportedError describes which operation a backend refused.

func (*NotSupportedError) Error

func (e *NotSupportedError) Error() string

func (*NotSupportedError) Is

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

type Org

type Org struct {
	ID          int64
	Name        string
	FullName    string
	Description string
	Website     string
	Location    string
	Email       string
	Visibility  OwnerVisibility
	// RepoAdminChangeTeamAccess lets repository admins change team access.
	RepoAdminChangeTeamAccess bool
}

Org is an organisation.

type Orgs

type Orgs interface {
	Get(ctx context.Context, name string) (*Org, error)
	// List returns the organisations user belongs to. Empty user means the
	// authenticated user.
	List(ctx context.Context, user string, opts ListOptions) (Page[Org], error)
	// ListAll returns every organisation visible to the authenticated user
	// (all of them for a site admin).
	ListAll(ctx context.Context, opts ListOptions) (Page[Org], error)
	Create(ctx context.Context, opts CreateOrgOptions) (*Org, error)
	Edit(ctx context.Context, name string, opts EditOrgOptions) (*Org, error)
	// Rename changes an organisation's name. The old name keeps redirecting
	// to the new one.
	Rename(ctx context.Context, name, newName string) error
	// Delete removes an organisation. It fails while the organisation still
	// owns repositories.
	Delete(ctx context.Context, name string) error

	Members(ctx context.Context, org string, opts ListOptions) (Page[User], error)
	// IsMember reports whether user belongs to org. A user that does not
	// exist is reported as not a member.
	IsMember(ctx context.Context, org, user string) (bool, error)
	// RemoveMember removes user from org and all its teams. Removing the last
	// owner fails.
	RemoveMember(ctx context.Context, org, user string) error
}

Orgs manages organisations and their membership. Members join an organisation through its teams; there is no direct "add member".

type OwnerVisibility

type OwnerVisibility string

OwnerVisibility is the visibility of a user or organisation profile. The zero value means "use the backend default" in option structs.

const (
	OwnerVisibilityPublic OwnerVisibility = "public"
	// OwnerVisibilityLimited is visible to signed-in users only.
	OwnerVisibilityLimited OwnerVisibility = "limited"
	OwnerVisibilityPrivate OwnerVisibility = "private"
)

func (OwnerVisibility) Valid

func (v OwnerVisibility) Valid() bool

Valid reports whether v is one of the named owner visibilities.

type PRBranch

type PRBranch struct {
	Ref  string // branch name
	SHA  string
	Repo string // "owner/name"; differs from the base repository for forks
}

PRBranch is one side of a pull request.

type PRState

type PRState string

PRState is the state of a pull request. PRStateAll is only meaningful as a List filter.

const (
	PRStateOpen   PRState = "open"
	PRStateClosed PRState = "closed" // includes merged pull requests
	PRStateAll    PRState = "all"
)

type Page

type Page[T any] struct {
	Items []T
	// NextPage is the page number to request next, or 0 if this is the last page.
	NextPage int
}

Page is one page of results.

type PullRequest

type PullRequest struct {
	Number int
	// Title is returned as the server stores it. On Forgejo a draft is a
	// title starting with a work-in-progress prefix ("WIP:" or "[WIP]" by
	// default), which therefore appears in Title.
	Title  string
	Body   string
	State  PRState
	Draft  bool
	Author string
	Head   PRBranch
	Base   PRBranch
	// Mergeable is nil while the server has not computed it yet (GitHub
	// computes it in the background).
	Mergeable      *bool
	Merged         bool
	MergedBy       string
	MergedAt       time.Time
	MergeCommitSHA string
	HTMLURL        string
	CreatedAt      time.Time
	UpdatedAt      time.Time
	ClosedAt       time.Time
}

PullRequest is a pull request.

type PullRequests

type PullRequests interface {
	List(ctx context.Context, owner, repo string, opts ListPROptions) (Page[PullRequest], error)
	Get(ctx context.Context, owner, repo string, number int) (*PullRequest, error)
	// Create opens a pull request. If the server does not honour Draft
	// (Forgejo with custom work-in-progress prefixes), the pull request is
	// returned together with an error matching ErrNotSupported.
	Create(ctx context.Context, owner, repo string, opts CreatePROptions) (*PullRequest, error)
	// Edit changes a pull request; see EditPROptions. A Draft change the
	// server does not honour is reported as in Create.
	Edit(ctx context.Context, owner, repo string, number int, opts EditPROptions) (*PullRequest, error)
	// Merge merges a pull request. Drafts cannot be merged. With
	// MergeOptions.WhenChecksSucceed the merge is scheduled instead.
	Merge(ctx context.Context, owner, repo string, number int, opts MergeOptions) error
	// CancelAutoMerge cancels a merge scheduled with WhenChecksSucceed.
	CancelAutoMerge(ctx context.Context, owner, repo string, number int) error
	IsMerged(ctx context.Context, owner, repo string, number int) (bool, error)

	RequestReviewers(ctx context.Context, owner, repo string, number int, users, teams []string) error
	RemoveReviewers(ctx context.Context, owner, repo string, number int, users, teams []string) error
	ListReviews(ctx context.Context, owner, repo string, number int, opts ListOptions) (Page[Review], error)
	CreateReview(ctx context.Context, owner, repo string, number int, opts CreateReviewOptions) (*Review, error)
}

PullRequests manages pull requests and their reviews.

type Release

type Release struct {
	ID              int64
	TagName         string
	TargetCommitish string // branch or commit the tag is created from
	Name            string // title
	Body            string // notes, Markdown
	Draft           bool
	Prerelease      bool
	Author          string // login
	HTMLURL         string
	TarballURL      string // source archives generated by the forge
	ZipballURL      string
	CreatedAt       time.Time
	PublishedAt     time.Time // zero for drafts
	Assets          []Asset
}

Release is a named, tagged point in a repository's history, with notes and attached files (assets).

type Releases

type Releases interface {
	List(ctx context.Context, owner, repo string, opts ListOptions) (Page[Release], error)
	Get(ctx context.Context, owner, repo string, id int64) (*Release, error)
	GetByTag(ctx context.Context, owner, repo, tag string) (*Release, error)
	// Latest returns the most recent published release that is not a
	// prerelease.
	Latest(ctx context.Context, owner, repo string) (*Release, error)
	Create(ctx context.Context, owner, repo string, opts CreateReleaseOptions) (*Release, error)
	Edit(ctx context.Context, owner, repo string, id int64, opts EditReleaseOptions) (*Release, error)
	// Delete removes a release. Its tag is kept.
	Delete(ctx context.Context, owner, repo string, id int64) error

	ListAssets(ctx context.Context, owner, repo string, releaseID int64, opts ListOptions) (Page[Asset], error)
	GetAsset(ctx context.Context, owner, repo string, releaseID, assetID int64) (*Asset, error)
	// UploadAsset streams size bytes from r as a new asset called name.
	// size must be exact. An empty contentType is guessed from the name's
	// extension, falling back to application/octet-stream.
	UploadAsset(ctx context.Context, owner, repo string, releaseID int64, name string, r io.Reader, size int64, contentType string) (*Asset, error)
	EditAsset(ctx context.Context, owner, repo string, releaseID, assetID int64, opts EditAssetOptions) (*Asset, error)
	DeleteAsset(ctx context.Context, owner, repo string, releaseID, assetID int64) error
	// DownloadAsset streams an uploaded asset's content; the caller must
	// close it. External assets are not downloaded: fetch their DownloadURL.
	DownloadAsset(ctx context.Context, owner, repo string, releaseID, assetID int64) (io.ReadCloser, error)
}

Releases manages releases and their assets.

Asset methods take the release ID as well as the asset ID: Forgejo needs both, GitHub ignores the release ID.

type Repo

type Repo struct {
	ID            int64
	Owner         string
	Name          string
	FullName      string // "owner/name"
	Description   string
	Website       string // Forgejo "website", GitHub "homepage"
	Visibility    Visibility
	DefaultBranch string
	Fork          bool
	Mirror        bool
	Archived      bool
	Template      bool
	Empty         bool // not reported by GitHub (always false there)
	HTMLURL       string
	CloneURL      string
	SSHURL        string
	CreatedAt     time.Time
	UpdatedAt     time.Time
}

Repo is a repository, modelled on the fields Forgejo and GitHub share.

type Repos

type Repos interface {
	Get(ctx context.Context, owner, name string) (*Repo, error)
	// List returns repositories owned by owner (a user or organisation).
	// Empty owner lists every repository the authenticated user can access,
	// including those of organisations they belong to.
	List(ctx context.Context, owner string, opts ListOptions) (Page[Repo], error)
	Create(ctx context.Context, opts CreateRepoOptions) (*Repo, error)
	Edit(ctx context.Context, owner, name string, opts EditRepoOptions) (*Repo, error)
	Delete(ctx context.Context, owner, name string) error
}

Repos manages repositories.

type Review

type Review struct {
	ID          int64
	Reviewer    string
	State       ReviewState
	Body        string
	CommitID    string
	HTMLURL     string
	SubmittedAt time.Time
}

Review is a pull request review.

type ReviewEvent

type ReviewEvent string

ReviewEvent is the verdict submitted with a review.

const (
	ReviewEventApprove        ReviewEvent = "approve"
	ReviewEventRequestChanges ReviewEvent = "request_changes"
	ReviewEventComment        ReviewEvent = "comment"
)

type ReviewState

type ReviewState string

ReviewState is the outcome of a review.

const (
	ReviewApproved         ReviewState = "approved"
	ReviewChangesRequested ReviewState = "changes_requested"
	ReviewCommented        ReviewState = "commented"
	ReviewPending          ReviewState = "pending"   // started, not submitted
	ReviewDismissed        ReviewState = "dismissed" // GitHub
	ReviewRequested        ReviewState = "requested" // Forgejo: review requested, not given
)

type Tag

type Tag struct {
	Name string
	SHA  string // commit the tag points to (annotated tags are peeled)
	// Message is the annotation of an annotated tag; empty for lightweight
	// tags. GitHub reports it from Get only, not from List.
	Message    string
	TarballURL string
	ZipballURL string
}

Tag is a repository tag.

type TagProtection

type TagProtection struct {
	ID        int64
	Pattern   string
	Allowlist Allowlist
}

TagProtection restricts creating, updating and deleting the tags matching Pattern ("v*") to the users and teams in Allowlist. On GitHub it is a tag ruleset, and an empty Allowlist blocks everyone, administrators included. Forgejo requires at least one allowed user or team.

type TagProtections

type TagProtections interface {
	List(ctx context.Context, owner, repo string) ([]TagProtection, error)
	Get(ctx context.Context, owner, repo, pattern string) (*TagProtection, error)
	Create(ctx context.Context, owner, repo string, rule TagProtection) (*TagProtection, error)
	Update(ctx context.Context, owner, repo, pattern string, rule TagProtection) (*TagProtection, error)
	Delete(ctx context.Context, owner, repo, pattern string) error
}

TagProtections manages tag protection rules, addressed by pattern.

type Tags

type Tags interface {
	List(ctx context.Context, owner, repo string, opts ListOptions) (Page[Tag], error)
	Get(ctx context.Context, owner, repo, name string) (*Tag, error)
	Create(ctx context.Context, owner, repo string, opts CreateTagOptions) (*Tag, error)
	Delete(ctx context.Context, owner, repo, name string) error
}

Tags manages repository tags. Releases are managed with Releases().

type Team

type Team struct {
	ID          int64
	Org         string
	Name        string
	Description string
	// Permission is the overall level: admin or owner when the team has
	// that level on every unit, otherwise derived by the server from Units.
	Permission TeamPermission
	Units      map[TeamUnit]TeamPermission
	// IncludesAllRepos gives the team access to every repository of the
	// organisation, current and future.
	IncludesAllRepos bool
	CanCreateOrgRepo bool
}

Team is a team within an organisation.

type TeamAccess

type TeamAccess struct {
	Permission TeamPermission
	Units      map[TeamUnit]TeamPermission
}

TeamAccess sets a team's permissions. Exactly one form applies:

  • Permission admin: admin on every unit; Units must be empty.
  • Units set: per-unit permissions (none, read or write); Permission must be empty.
  • Permission read or write with no Units: that level on every unit.

type TeamPermission

type TeamPermission string

TeamPermission is a team's access level, overall or for one unit.

const (
	TeamPermissionNone  TeamPermission = "none"
	TeamPermissionRead  TeamPermission = "read"
	TeamPermissionWrite TeamPermission = "write"
	TeamPermissionAdmin TeamPermission = "admin"
	// TeamPermissionOwner is reported for an organisation's owner team; it
	// cannot be assigned.
	TeamPermissionOwner TeamPermission = "owner"
)

type TeamUnit

type TeamUnit string

TeamUnit is a repository feature a team can be granted access to.

const (
	TeamUnitCode           TeamUnit = "repo.code"
	TeamUnitIssues         TeamUnit = "repo.issues"
	TeamUnitExternalIssues TeamUnit = "repo.ext_issues"
	TeamUnitWiki           TeamUnit = "repo.wiki"
	TeamUnitExternalWiki   TeamUnit = "repo.ext_wiki"
	TeamUnitPulls          TeamUnit = "repo.pulls"
	TeamUnitReleases       TeamUnit = "repo.releases"
	TeamUnitProjects       TeamUnit = "repo.projects"
	TeamUnitPackages       TeamUnit = "repo.packages"
	TeamUnitActions        TeamUnit = "repo.actions"
)

type Teams

type Teams interface {
	List(ctx context.Context, org string, opts ListOptions) (Page[Team], error)
	Get(ctx context.Context, id int64) (*Team, error)
	// GetByName returns the team of org with exactly that name
	// (case-insensitive), or a 404 *APIError.
	GetByName(ctx context.Context, org, name string) (*Team, error)
	Create(ctx context.Context, org string, opts CreateTeamOptions) (*Team, error)
	// Edit changes a team. Renaming the owner team or changing its access
	// returns ErrOwnerTeam.
	Edit(ctx context.Context, id int64, opts EditTeamOptions) (*Team, error)
	// Delete removes a team. Deleting the owner team returns ErrOwnerTeam.
	Delete(ctx context.Context, id int64) error

	Members(ctx context.Context, id int64, opts ListOptions) (Page[User], error)
	IsMember(ctx context.Context, id int64, user string) (bool, error)
	// AddMember adds user to the team, which also makes them a member of
	// the organisation.
	AddMember(ctx context.Context, id int64, user string) error
	RemoveMember(ctx context.Context, id int64, user string) error

	Repos(ctx context.Context, id int64, opts ListOptions) (Page[Repo], error)
	AddRepo(ctx context.Context, id int64, owner, repo string) error
	RemoveRepo(ctx context.Context, id int64, owner, repo string) error
}

Teams manages an organisation's teams, their members and repositories. Teams are addressed by ID; use GetByName to look one up.

type UnsupportedVersionError

type UnsupportedVersionError struct {
	Backend Backend
	Version string
	Minimum string
}

UnsupportedVersionError is returned when a server is older than the minimum version the library supports.

func (*UnsupportedVersionError) Error

func (e *UnsupportedVersionError) Error() string

type User

type User struct {
	ID            int64
	Login         string
	FullName      string
	Email         string
	Description   string
	Website       string
	Location      string
	Visibility    OwnerVisibility
	IsAdmin       bool
	Active        bool
	Restricted    bool
	ProhibitLogin bool
	HTMLURL       string
	CreatedAt     time.Time
	LastLogin     time.Time // zero if the user never signed in
}

User is a user account.

type Users

type Users interface {
	Get(ctx context.Context, login string) (*User, error)
	List(ctx context.Context, opts ListOptions) (Page[User], error)
	Create(ctx context.Context, opts CreateUserOptions) (*User, error)
	// Edit applies opts. If the server ignores a visibility change (Forgejo
	// 15.0.x cannot switch a user back to public), Edit returns the updated
	// user together with an error matching ErrNotSupported.
	Edit(ctx context.Context, login string, opts EditUserOptions) (*User, error)
	// Rename changes a user's login. The old login keeps redirecting to the
	// new one, so Get(oldLogin) returns the renamed user.
	Rename(ctx context.Context, login, newLogin string) error
	Delete(ctx context.Context, login string, opts DeleteUserOptions) error
}

Users manages user accounts. All methods except Get require a site-admin token.

type Visibility

type Visibility string

Visibility is the visibility of a repository. The zero value means "use the backend default" in option structs.

const (
	VisibilityPublic  Visibility = "public"
	VisibilityPrivate Visibility = "private"
	// VisibilityInternal is visible to all members of a GitHub enterprise.
	// Forgejo repositories only have a private flag, so the Forgejo backend
	// is expected to return ErrNotSupported for it. [verify in increment 3]
	VisibilityInternal Visibility = "internal"
)

func (Visibility) Valid

func (v Visibility) Valid() bool

Valid reports whether v is one of the named visibilities.

type Webhook

type Webhook struct {
	ID          int64
	URL         string
	ContentType HookContentType
	// Events lists the subscribed events. Forgejo may add finer-grained
	// events implied by the requested ones (e.g. issue_assign with issues);
	// those keep Forgejo's names.
	Events       []HookEvent
	Active       bool
	BranchFilter string // Forgejo only
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

Webhook is a configured webhook. The secret is never returned.

type Webhooks

type Webhooks interface {
	List(ctx context.Context, scope HookScope, opts ListOptions) (Page[Webhook], error)
	Get(ctx context.Context, scope HookScope, id int64) (*Webhook, error)
	// Create adds a webhook. Forgejo silently drops event names it does not
	// know; Create then returns the webhook together with an error.
	Create(ctx context.Context, scope HookScope, opts CreateWebhookOptions) (*Webhook, error)
	// Edit changes a webhook; dropped events are reported as in Create.
	Edit(ctx context.Context, scope HookScope, id int64, opts EditWebhookOptions) (*Webhook, error)
	Delete(ctx context.Context, scope HookScope, id int64) error
	// Ping asks the server to send a test delivery: a "ping" event on
	// GitHub, a "push" of the latest commit on Forgejo (repositories only).
	Ping(ctx context.Context, scope HookScope, id int64) error
}

Webhooks manages repository and organisation webhooks. Receiving and verifying deliveries is done by the webhook sub-package.

Directories

Path Synopsis
Package bitbucket is the Bitbucket Cloud backend (REST API 2.0).
Package bitbucket is the Bitbucket Cloud backend (REST API 2.0).
Package forgejo is the Forgejo backend.
Package forgejo is the Forgejo backend.
Package github is the GitHub backend, built on google/go-github.
Package github is the GitHub backend, built on google/go-github.
internal
bitbucketapi
Package bitbucketapi is the HTTP transport for the Bitbucket Cloud backend (REST API 2.0).
Package bitbucketapi is the HTTP transport for the Bitbucket Cloud backend (REST API 2.0).
contracttest
Package contracttest holds behaviour checks shared by every backend's integration tests, so all backends are held to the same contract.
Package contracttest holds behaviour checks shared by every backend's integration tests, so all backends are held to the same contract.
forgejoapi
Package forgejoapi is the HTTP transport for the Forgejo backend.
Package forgejoapi is the HTTP transport for the Forgejo backend.
Package webhook receives webhook deliveries from Forgejo and GitHub: it verifies their signature, identifies the forge and event, and decodes the common payloads.
Package webhook receives webhook deliveries from Forgejo and GitHub: it verifies their signature, identifies the forge and event, and decodes the common payloads.

Jump to

Keyboard shortcuts

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