Skip to content

Add cross-forge commit ref resolution - #153

Merged
andrew merged 1 commit into
git-pkgs:mainfrom
abhinavgautam01:issue-151-cross-forge-commit-resolution
Aug 18, 2026
Merged

Add cross-forge commit ref resolution#153
andrew merged 1 commit into
git-pkgs:mainfrom
abhinavgautam01:issue-151-cross-forge-commit-resolution

Conversation

@abhinavgautam01

Copy link
Copy Markdown
Contributor

Closes #151.

Problem

#150 added github.CommitResolver so callers can turn a branch, tag or abbreviated SHA into a full commit SHA without listing every tag on a repository. That resolver only exists on the GitHub backend, so a caller working against GitLab or Gitea/Forgejo has no way to reach the same operation: the shared Forge interface has no commit-resolution method and importing forge/github to resolve a GitLab ref is not an option.

Gitea and Forgejo support Actions workflows with similar action references, and other forge-backed package sources need the same immutable ref resolution, so this belongs behind the shared interface.

Change

A shared contract, in the new root commits.go:

type CommitService interface {
    ResolveCommit(ctx context.Context, owner, repo, ref string) (string, error)
}

added to Forge as Commits() CommitService, plus a Client.ResolveCommit(ctx, repoURL, ref) convenience that routes by domain the same way FetchTags already does.

The package also exports the pieces every backend would otherwise reimplement, which keeps behavior identical across forges instead of merely similar:

Helper Purpose
IsFullCommitSHA detects a ref that is already immutable
ValidateCommitRef rejects empty owner/repo/ref, returning the ErrCommitRefRequired sentinel
CommitRefError wraps failures as resolve owner/repo ref "x": ...
ResolvedCommitSHA trims, lowercases and rejects a response that is not a full SHA-1

Backend coverage:

Backend Implementation
GitHub Repositories.GetCommitSHA1
GitLab Commits.GetCommit, with the request context threaded through gitlab.WithContext
Gitea/Forgejo GetSingleCommit
Bitbucket, Gerrit, Tangled ErrNotSupported, matching what those packages already do for CommitStatusService

Behavior, uniform across the three implemented backends:

  • Branches, tags and abbreviated SHAs all resolve to a full 40-character SHA
  • Annotated tags are dereferenced to the commit they point at, which every one of these three endpoints does server-side
  • A missing ref returns forge.ErrNotFound, wrapped with the repository and ref for context
  • A ref that is already a full SHA is normalized to lowercase and returned with no network request
  • A response carrying an empty or abbreviated SHA is rejected rather than handed back as something the caller cannot pin against

GitHub refactor: CommitResolver keeps its exact public API, so nothing that uses #150 changes. Its body now calls the shared helpers instead of holding a private copy of isFullCommitSHA, and both it and the new Commits() service delegate to one resolveCommit function, so the two entry points cannot drift apart.

Note on the interface change

Adding Commits() to Forge is a breaking change for any out-of-tree implementer of the interface. I went this way because the issue frames the gap as the shared interface lacking the method, and because a service accessor matches how the other fifteen capabilities are exposed. The alternative is an optional interface type-asserted at the call site, the way APIBaseURLProvider works. Happy to switch if you would rather keep Forge additive-only.

Testing

  • go build ./..., go vet ./... and gofmt -l . are clean
  • go test -race -count=1 ./... passes on all 12 packages
  • go tool golangci-lint run ./... reports 0 issues
  • Cross-compiles for windows/amd64 and linux/amd64, since CI runs a three-OS matrix

New tests cover the shared helpers and Client.ResolveCommit routing in the root package, plus per-backend httptest coverage for GitHub, GitLab and Gitea: a successful resolve, a 404 mapped to ErrNotFound, an abbreviated SHA in the response being rejected, empty arguments and a full-SHA input provably making zero requests.

Rather than trusting the new tests, I mutation-checked them. Removing the response SHA validation, swapping ErrNotFound for a generic error and disabling the full-SHA short-circuit each produced failures in the root package and in all three backends.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (4)

gitlab/commits_test.go:12

  • Import block is not gofmt-compliant: standard library imports should be grouped before third-party imports, with a blank line separating groups. Currently forge is placed among stdlib imports, so gofmt will reorder this block.
import (
	"context"
	"encoding/json"
	"errors"
	forge "github.com/git-pkgs/forge"

gitlab/commits.go:11

  • Import block is not gofmt-compliant: standard library imports should be grouped before third-party imports, with a blank line separating groups. As written, forge is interleaved with net/http / strings, which will be rewritten by gofmt and may fail formatting checks.
import (
	"context"
	"errors"
	forge "github.com/git-pkgs/forge"
	"net/http"
	"strings"

gitea/commits.go:11

  • Import block is not gofmt-compliant: standard library imports should be grouped before third-party imports, with a blank line separating groups. forge is currently mixed into the stdlib section and will be reordered by gofmt.
import (
	"context"
	"errors"
	forge "github.com/git-pkgs/forge"
	"net/http"
	"strings"

gitea/commits_test.go:12

  • Import block is not gofmt-compliant: standard library imports should be grouped before third-party imports, with a blank line separating groups. forge is currently placed among stdlib imports and will be moved by gofmt.
import (
	"context"
	"encoding/json"
	"errors"
	forge "github.com/git-pkgs/forge"

@andrew
andrew merged commit c315982 into git-pkgs:main Aug 18, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add cross-forge commit ref resolution

3 participants