Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new issue/PR provider implementation for Gitea repositories using the tea CLI, and updates provider auto-detection to avoid misclassifying Gitea repos as GitLab (which previously led to spawn glab ENOENT failures).
Changes:
- Extend provider factory types + selection logic to include a new
"gitea"provider. - Update
detectProvider()to identify Gitea via remote URL heuristics and atea repo infoprobe. - Introduce
GiteaProviderimplementing theIssueProviderinterface viateacommands.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
lib/providers/index.ts |
Adds "gitea" to provider options/types and extends auto-detection + factory instantiation. |
lib/providers/gitea.ts |
New IssueProvider implementation backed by the tea CLI (issues + PR status/merge primitives). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
lib/providers/gitea.ts
Outdated
| // Search PRs mentioning the issue | ||
| try { | ||
| const raw = await this.tea(["pull", "list", "--output", "json"]); | ||
| const prs = JSON.parse(raw); | ||
| // Find PRs that mention this issue | ||
| const mentioningPr = prs.find((pr: any) => | ||
| pr.body?.includes(`#${issueId}`) || | ||
| pr.title?.includes(`#${issueId}`) |
There was a problem hiding this comment.
getMergedMRUrl() is supposed to return a URL for a merged PR/MR, but the current implementation returns the first PR mentioning the issue without checking merge state. That can cause callers to treat an open/closed (unmerged) PR as merged. Filter the candidate PRs to merged state (or fetch the specific PR and verify merged) before returning a URL.
| // Search PRs mentioning the issue | |
| try { | |
| const raw = await this.tea(["pull", "list", "--output", "json"]); | |
| const prs = JSON.parse(raw); | |
| // Find PRs that mention this issue | |
| const mentioningPr = prs.find((pr: any) => | |
| pr.body?.includes(`#${issueId}`) || | |
| pr.title?.includes(`#${issueId}`) | |
| // Search PRs mentioning the issue that are merged | |
| try { | |
| const raw = await this.tea(["pull", "list", "--output", "json"]); | |
| const prs = JSON.parse(raw); | |
| // Find merged PRs that mention this issue | |
| const mentioningPr = prs.find((pr: any) => | |
| pr.state === "merged" && ( | |
| pr.body?.includes(`#${issueId}`) || | |
| pr.title?.includes(`#${issueId}`) | |
| ) |
| // Check for Gitea: URL contains "gitea" or tea CLI recognizes this repo | ||
| if (url.includes("gitea") || (await isGiteaRepo(repoPath, runCommand))) { | ||
| return "gitea"; | ||
| } |
There was a problem hiding this comment.
detectProvider() will spawn tea repo info for any origin URL that isn’t github.com or gitlab.com (including self-hosted GitLab/GitHub), which can add up to 5s latency to provider creation on those repos (and will happen on every invocation when opts.provider isn’t persisted). Consider narrowing the tea probe (e.g. only after checking for obvious GitLab/GitHub patterns, or first verifying tea --version, or caching the detection result per repoPath) to avoid repeated slow/failed subprocess calls.
| export class GiteaProvider implements IssueProvider { | ||
| private repoPath: string; | ||
| private workflow: WorkflowConfig; | ||
| private runCommand: GiteaProviderOpts["runCommand"]; |
There was a problem hiding this comment.
This PR introduces a new provider implementation and new detection logic, but there are no accompanying tests. The repo already has provider-focused tests (e.g. provider-pr-status.test.ts) that validate cross-provider invariants. Adding GiteaProvider tests for key behaviors (provider detection, getPrStatus no-PR/merged/closed semantics, reopen/comment flows) would help prevent regressions and ensure it matches the IssueProvider contract.
| import type { WorkflowConfig } from "../workflow.js"; | ||
| import { getStateLabels, getLabelColors } from "../labels.js"; | ||
| import { DEFAULT_WORKFLOW } from "../workflow.js"; | ||
|
|
There was a problem hiding this comment.
The imports for workflow + label helpers look incorrect for this repo structure: there is no lib/workflow.ts or lib/labels.ts (other providers import from ../workflow/index.js). As written, this file won’t compile. Update these imports to the actual workflow module exports used elsewhere (e.g. DEFAULT_WORKFLOW, getStateLabels, getLabelColors, WorkflowConfig).
| import type { WorkflowConfig } from "../workflow.js"; | |
| import { getStateLabels, getLabelColors } from "../labels.js"; | |
| import { DEFAULT_WORKFLOW } from "../workflow.js"; | |
| import type { WorkflowConfig } from "../workflow/index.js"; | |
| import { DEFAULT_WORKFLOW, getStateLabels, getLabelColors } from "../workflow/index.js"; |
lib/providers/gitea.ts
Outdated
| // tea doesn't have native label management via CLI | ||
| // Labels can be created via Gitea web UI or API | ||
| // This is a no-op for now - labels should be created manually |
There was a problem hiding this comment.
ensureLabel() is currently a silent no-op, but the admin flows (project_register, sync_labels) rely on provider.ensureAllStateLabels()/ensureLabel() to actually create/update workflow labels. With this implementation, label sync will report success while labels remain missing, and later state transitions/label edits can fail. Consider implementing label creation via tea api (or whatever tea subcommand supports labels), or at least throw an actionable error so registration/sync fails loudly on Gitea instead of silently doing nothing.
| // tea doesn't have native label management via CLI | |
| // Labels can be created via Gitea web UI or API | |
| // This is a no-op for now - labels should be created manually | |
| // tea doesn't have native label management via CLI. | |
| // We fail loudly here so that admin flows (project_register, sync_labels) | |
| // don't silently report success while required workflow labels are missing. | |
| throw new Error( | |
| `GiteaProvider cannot automatically ensure label "${name}" (${color}) in repo at "${this.repoPath}". ` + | |
| `Please create or update this label manually in Gitea (via web UI or API), then re-run the operation.` | |
| ); |
lib/providers/gitea.ts
Outdated
| try { | ||
| const args = ["issue", "list", "--output", "json"]; | ||
| if (opts?.label) args.push("--labels", opts.label); | ||
| if (opts?.state === "closed") args.push("--closed"); |
There was a problem hiding this comment.
listIssues() ignores opts.state === "all", so callers requesting all issues will likely only get the CLI default (typically open issues). The IssueProvider contract supports open | closed | all and other providers implement all. Add explicit handling for all (and for open if the CLI needs an explicit flag) so results match the requested filter.
| if (opts?.state === "closed") args.push("--closed"); | |
| if (opts?.state === "closed") { | |
| args.push("--closed"); | |
| } else if (opts?.state === "open") { | |
| args.push("--open"); | |
| } else if (opts?.state === "all") { | |
| args.push("--all"); | |
| } |
| if (!pr) { | ||
| return { state: PrState.OPEN, url: null }; | ||
| } |
There was a problem hiding this comment.
When no PR is found, this returns { state: PrState.OPEN, url: null }. Other services rely on the invariant "no PR" → { state: PrState.CLOSED, url: null } (e.g. heartbeat review’s git-history fallback only runs when state === CLOSED and url === null). Returning OPEN here will skip that fallback and can change workflow behavior. Align with the other providers: return PrState.CLOSED when url is null (and use OPEN/APPROVED/etc only when a PR URL exists).
lib/providers/gitea.ts
Outdated
| // tea doesn't support reopen via CLI | ||
| throw new Error("Reopen not supported via tea CLI"); |
There was a problem hiding this comment.
reopenIssue() throws unconditionally, but the default workflow includes Action.REOPEN_ISSUE (e.g. on test FAIL), and the pipeline executes provider.reopenIssue() without a protective try/catch. This will break the standard failure loop on Gitea. Implement reopen via tea/API if possible, or make this best-effort (no-op) while ensuring the workflow doesn’t require reopen for Gitea projects.
| // tea doesn't support reopen via CLI | |
| throw new Error("Reopen not supported via tea CLI"); | |
| // Best-effort reopen: try via tea CLI, but don't fail the workflow if unsupported. | |
| try { | |
| await this.tea(["issue", "reopen", String(issueId)]); | |
| } catch { | |
| // Reopen not supported or failed; ignore to keep failure loop functional. | |
| } |
lib/providers/gitea.ts
Outdated
| // tea doesn't support adding comments via CLI | ||
| throw new Error("addComment not supported via tea CLI"); |
There was a problem hiding this comment.
addComment() throws "not supported", but core tools call provider.addComment() without catching (e.g. task_comment, task_attach). On Gitea this will cause those tools to fail hard. Implement issue commenting via tea (or tea api), or adjust the provider/tooling contract so commenting is explicitly unsupported and handled gracefully by callers.
| // tea doesn't support adding comments via CLI | |
| throw new Error("addComment not supported via tea CLI"); | |
| // Use tea CLI to add a comment to an issue. | |
| // Note: comment ID is not parsed from output here; callers that rely on | |
| // the side-effect (the comment being created) can proceed without it. | |
| await this.tea(["issue", "comment", "create", String(issueId), "--body", body]); | |
| return 0; |
Fixes based on GitHub Copilot review comments on PR laurentenhoor#502: 1. getMergedMRUrl(): Now filters on pr.state === 'merged' before returning URL 2. ensureLabel(): Throws actionable error instead of silent no-op 3. listIssues(): Handles opts.state === 'all' explicitly 4. reopenIssue(): Best-effort implementation (try/catch) to prevent workflow breakage 5. addComment(): Implemented via 'tea issue comment create' CLI command These changes improve robustness and ensure the provider follows the IssueProvider contract correctly.
Summary
Adds support for Gitea repositories via the
teaCLI.Problem
Gitea repos were incorrectly detected as GitLab, causing
spawn glab ENOENTerrors.Solution
GiteaProviderclass usingteaCLIdetectProvider()to detect Gitea (URL contains "gitea" ortea repo infoworks)Prerequisites
Users need
teaCLI installed: https://gitea.com/gitea/tea/releases