feat(forgejo): implement Forgejo client wrapper #134

Merged
fuzzy merged 1 commit from feat/forgejo-client into main 2026-08-02 16:14:26 +00:00
Owner

What

Implement the ForgejoService interface from task #32 on Client in internal/forgejo:

  • Auth (#33): NewClient(baseURL, token) authenticates via gitea.SetToken; SetContext(ctx) propagates cancellation to every call
  • CreateRepository (#34): detects org vs user owner via GetOrg/GetUserInfo, creates repo with auto-init, maps site.Visibility to the SDK Private flag (public→false, private→false, limited→private), returns clone URL
  • CreateWebhook (#35): installs a gitea webhook posting push events to the thwap-pagesd endpoint, returns hook ID
  • ListTags (#36): paginates all repository tags, returns tag names
  • var _ ForgejoService = (*Client)(nil) compile-time assertion

Tests: httptest-based fake Forgejo server exercising the real gitea SDK — org/user repo creation, error path, webhook payload validation, paginated tag listing, and token auth header verification.

Why

Phase 3 task #37 — the first concrete integration, used by site registration (#69) and the webhook receiver (#75).

Testing

  • go build ./...
  • go vet ./...
  • go test -race ./internal/forgejo/ — 6 tests pass
  • gofmt -l ./cmd ./internal clean
  • golangci-lint run — 0 issues
  • pre-commit hooks all pass

Breaking Changes

None.

Notes

  • site.VisibilityLimited maps to private — the gitea SDK CreateRepoOption only exposes a Private bool (limited is org-scoped), as discussed.
  • Owner detection uses GetOrg (org → CreateOrgRepo), falling back to user-owned CreateRepo.
  • Issues #33–#37 were closed via the Forgejo API as part of this task per the workflow.

Closes #33
Closes #34
Closes #35
Closes #36
Closes #37

## What Implement the `ForgejoService` interface from task #32 on `Client` in `internal/forgejo`: - **Auth (#33)**: `NewClient(baseURL, token)` authenticates via `gitea.SetToken`; `SetContext(ctx)` propagates cancellation to every call - **CreateRepository (#34)**: detects org vs user owner via `GetOrg`/`GetUserInfo`, creates repo with auto-init, maps `site.Visibility` to the SDK `Private` flag (public→false, private→false, limited→private), returns clone URL - **CreateWebhook (#35)**: installs a `gitea` webhook posting `push` events to the thwap-pagesd endpoint, returns hook ID - **ListTags (#36)**: paginates all repository tags, returns tag names - `var _ ForgejoService = (*Client)(nil)` compile-time assertion **Tests**: httptest-based fake Forgejo server exercising the real gitea SDK — org/user repo creation, error path, webhook payload validation, paginated tag listing, and token auth header verification. ## Why Phase 3 task #37 — the first concrete integration, used by site registration (#69) and the webhook receiver (#75). ## Testing - [x] `go build ./...` - [x] `go vet ./...` - [x] `go test -race ./internal/forgejo/` — 6 tests pass - [x] `gofmt -l ./cmd ./internal` clean - [x] `golangci-lint run` — 0 issues - [x] pre-commit hooks all pass ## Breaking Changes None. ## Notes - `site.VisibilityLimited` maps to private — the gitea SDK `CreateRepoOption` only exposes a `Private` bool (limited is org-scoped), as discussed. - Owner detection uses `GetOrg` (org → `CreateOrgRepo`), falling back to user-owned `CreateRepo`. - Issues #33–#37 were closed via the Forgejo API as part of this task per the workflow. Closes #33 Closes #34 Closes #35 Closes #36 Closes #37
feat(forgejo): implement Forgejo client wrapper
All checks were successful
Test and Release / lint (pull_request) Successful in 3m53s
Test and Release / test (pull_request) Successful in 19m6s
b54708ebbc
Implement the ForgejoService interface on Client:

- Authenticate via token in NewClient and propagate context to all calls
- CreateRepository detects org vs user owner (GetOrg/GetUserInfo) and
  maps site.Visibility to the SDK Private flag (limited -> private)
- CreateWebhook installs a gitea webhook posting push events to the
  thwap-pagesd webhook endpoint, returning the hook ID
- ListTags paginates all repository tags

Add compile-time interface assertion and httptest-based tests that
exercise the real gitea SDK against a fake Forgejo server. Mark task
done in ROADMAP.md.

closes #33
closes #34
closes #35
closes #36
closes #37
the.auditor left a comment

Review Summary

Verified locally on the PR head (b54708e): go build, go vet, gofmt, golangci-lint run all clean; all 6 forgejo tests pass with -race. The httptest fake-server approach exercises the real gitea SDK against realistic payloads — solid integration-style coverage (org/user repo creation, error path, webhook payload, pagination via Link header, token auth header).

Implementation is correct: visibility mapping behaves as intended (public → public repo; private and limited → private repo), errors are wrapped with context, and the ForgejoService interface assertion is in place.

No blocking issues.

Non-blocking observations:

  1. forgejo.go:109 — the visibilityPrivate comment reads "Public and limited map to false and true respectively" and omits private (which also maps to true). Also the PR body states "private→false", which contradicts the code. The code is right; the wording is misleading. Suggest: "Public maps to false; private and limited map to true."
  2. isOrg treats any GetOrg error as "not a user" — a transient error (rate limit, network, 403) silently falls back to CreateRepo, which creates the repo under the authenticated user rather than the requested owner. Consider distinguishing not-found (404) from other errors once error-handling matures.
  3. c.gitea.SetContext(ctx) per call mutates shared client state — makes Client unsafe for concurrent use with different contexts. Fine while use is single-threaded; document it before the webhook handler runs concurrent requests.
  4. CreateWebhook doesn't set a hook secret despite webhook.secret existing in config — will need wiring when the receiver (task #75) validates signatures.
  5. No test covers VisibilityPrivate mapping (only limited and public are exercised).

Approving.

## Review Summary Verified locally on the PR head (`b54708e`): `go build`, `go vet`, `gofmt`, `golangci-lint run` all clean; all 6 forgejo tests pass with `-race`. The httptest fake-server approach exercises the real gitea SDK against realistic payloads — solid integration-style coverage (org/user repo creation, error path, webhook payload, pagination via `Link` header, token auth header). Implementation is correct: visibility mapping behaves as intended (public → public repo; private and limited → private repo), errors are wrapped with context, and the `ForgejoService` interface assertion is in place. **No blocking issues.** Non-blocking observations: 1. `forgejo.go:109` — the `visibilityPrivate` comment reads "Public and limited map to false and true respectively" and omits `private` (which also maps to true). Also the PR body states "private→false", which contradicts the code. The *code* is right; the wording is misleading. Suggest: "Public maps to false; private and limited map to true." 2. `isOrg` treats *any* `GetOrg` error as "not a user" — a transient error (rate limit, network, 403) silently falls back to `CreateRepo`, which creates the repo under the *authenticated* user rather than the requested `owner`. Consider distinguishing not-found (404) from other errors once error-handling matures. 3. `c.gitea.SetContext(ctx)` per call mutates shared client state — makes `Client` unsafe for concurrent use with different contexts. Fine while use is single-threaded; document it before the webhook handler runs concurrent requests. 4. `CreateWebhook` doesn't set a hook secret despite `webhook.secret` existing in config — will need wiring when the receiver (task #75) validates signatures. 5. No test covers `VisibilityPrivate` mapping (only `limited` and `public` are exercised). Approving.
fuzzy scheduled this pull request to auto merge when all checks succeed 2026-08-02 15:56:45 +00:00
fuzzy merged commit b54708ebbc into main 2026-08-02 16:14:26 +00:00
fuzzy deleted branch feat/forgejo-client 2026-08-02 16:14:26 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
thwap/thwap-pagesd!134
No description provided.