Skip to content

feat(tasks): add Gitea and Forgejo task source - #5493

Open
beatkapo wants to merge 6 commits into
stablyai:mainfrom
beatkapo:feat/gitea-task-source
Open

beatkapo wants to merge 6 commits into
stablyai:mainfrom
beatkapo:feat/gitea-task-source

Conversation

@beatkapo

@beatkapo beatkapo commented Jun 16, 2026 •

Copy link
Copy Markdown

ELI5

People using Gitea or Forgejo can browse their repository's issues and pull requests in Tasks and start a workspace from one, just as they can with other task providers.

What Changed

Before, Orca could attach an existing Gitea pull request to a workspace, but Gitea was missing from Tasks. This adds a repository-scoped Gitea source, server connections in Settings, issue creation and editing, comments, and pull-request files, checks, reviews, and merge actions. Existing profiles keep their chosen task sources; Gitea can be enabled in Settings → Tasks.

The provider plugs into the current per-provider Tasks layout and extends the existing Gitea client and encrypted credential store. Server credentials match the repository's remote host, including non-default HTTPS ports. Label edits preserve numeric IDs, including organization labels outside the fetched repository label page. These address the compatibility gaps identified in #6920.

Review fixes also probe saved credentials at startup, reject non-loopback HTTP and redirects, invalidate caches when credentials or issues change, discard stale requests, keep the saved title when starting work, show comment authors, reject fractional review lines, and retain successfully loaded pull-request resources when another request fails. Setup explains token permissions; UI copy is localized and uses the current design-system primitives.

Why

Gitea issues belong to repositories, so this follows the repository-backed Tasks model rather than adding an account-wide task list. Reusing the existing Gitea request and credential services keeps Tasks and hosted review on the same authentication path. The existing GitHub integration and Gitea hosted-review support do not provide this Tasks workflow.

Linked Issue

Fixes #4703

Visual Proof

Windows, hidden Electron renderer driven through Playwright CDP. These show the issue workflow before and after saving a title and posting a comment, using deterministic mock Gitea responses rather than a live deployment.

Before editing After saving and commenting
Issue before editing Saved title and comment author

Testing

  • I manually tested these changes locally
  • Automated tests added/updated

Verified on Windows. No live Gitea/Forgejo server, macOS, Linux, or SSH end-to-end test is claimed.

  • 117 focused tests pass across 15 Gitea backend, store, settings, metadata, and partial-resource suites.
  • Expanded Tasks/settings/provider/persistence suites: 1,906 tests passed, one skipped, and seven locale-sensitive session-history assertions failed under Spanish number formatting. All 48 tests in those three unchanged upstream files pass with an English Intl.NumberFormat default matching their expectations.
  • Hidden Electron issue-edit/comment test passes (tests/e2e/gitea-tasks.spec.ts).
  • Typecheck, changed-code quality gate, max-lines and reliability gates, RPC catalog, and localization validation pass.
  • Full build passes after installing the separate mobile workspace dependencies and normalizing existing mobile source files to the repository's LF policy; no mobile source changes are included.
  • Full pnpm lint stops on five existing import-cycle warnings in unchanged mobile/src/transport and mobile/src/terminal/document files. The changed-code gate reports no new findings.

Setup, token scopes, and a reviewer walkthrough are in docs/reference/gitea-task-source.md. The Electron test uses the existing isolated application fixture and keeps windows hidden.

AI Disclosure

Implementation and follow-up fixes were assisted by Claude Code and OpenAI Codex (GPT-6).

Review

The original backend, localization, Tasks UI, and Settings commits remain separate. The branch incorporates current upstream main; follow-up fixes cover the outstanding automated review findings and the port/label concerns raised in the discussion.

Agent skill upstream boundary

  • Not applicable: no upstream skill-installer implementation or assets are copied into this PR.

Notes

API access uses the desktop's saved integration credentials; repository discovery uses the existing connection-aware resolver. Plain folders without a usable Git remote do not have repository-scoped tasks. No new runtime RPC or stream opcode is introduced. Existing task-source preferences are preserved.

Checklist

  • This PR is small and focused
  • I explained the user-facing change, mechanism, and choice of approach
  • Visual workflow evidence attached
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and folder-workspace impact considered
  • Full lint, typecheck, tests, and build are all green without environment qualifications

The feature spans backend and UI, so the size box remains unchecked. Validation limitations are listed above rather than hidden behind a blanket green checkbox.

GitHub reports this head as mergeable without conflicts. The three pull-request workflows are waiting for maintainer action (action_required), including PR Checks; they have not yet validated this head.

@ghost

ghost commented Jun 16, 2026 •

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Jun 16, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Understand this PR’s impact

Explore downstream dependencies and potential security impact with Blast Radius.

View blast radius →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This change adds Gitea as a task provider. It introduces shared Gitea types, encrypted multi-server credentials, authenticated REST operations, issue and pull-request support, IPC and preload APIs, Zustand caching, task lists, settings integration, issue and pull-request workspaces, localization, and tests. Provider identities, workspace links, task-source routing, status checks, comments, mutations, and connection flows now support Gitea.

Priority: ➖ Normal

Merge Risk: 🟠 High · up to 7ed9b

Gitea tokens and cached private data can be exposed under supported configurations, while common PR and credential failures are hidden or leave incomplete state. Resolve these issues before merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.57% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 148 functions across 62 files. (10 skippe… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description includes all required template sections, clearly explains the user-facing change and rationale, links issue #4703, provides visual proof, documents automated testing and known validati…
Title check ✅ Passed The title clearly identifies the primary change: adding Gitea and Forgejo as task sources. It is concise and directly related to the changeset.
Full details: Docstring Coverage

Explanation

Docstring coverage is 17.57% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 148 functions across 62 files. (10 skipped: 1 unsupported, 9 over the file limit.)

  • Fix all pre-merge checks with AI

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 18

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (8)
src/renderer/src/components/gitea-issue-meta-controls.tsx-118-118 (1)

118-118: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Button disable logic doesn't match the save guard.

The save function (line 46) returns early when any field is pending, but each button only disables itself when its own field is saving. For example, the Labels button checks pending === 'labels' (line 131), so it remains enabled while title is saving. If clicked, the save call returns early with no feedback—the button appears clickable but does nothing.

Proposed fix: disable all buttons when any save is pending
-        disabled={pending === 'title' || !titleDraft.trim() || titleDraft.trim() === title}
+        disabled={pending !== null || !titleDraft.trim() || titleDraft.trim() === title}
-        <PopoverTrigger asChild>
-          <Button variant="outline" size="sm" disabled={pending === 'labels'} className="gap-1.5">
+        <PopoverTrigger asChild>
+          <Button variant="outline" size="sm" disabled={pending !== null} className="gap-1.5">
-        <PopoverTrigger asChild>
           <Button
             variant="outline"
             size="sm"
-            disabled={pending === 'assignees'}
+            disabled={pending !== null}
             className="gap-1.5"
           >

Also applies to: 131-131, 182-182

src/renderer/src/i18n/locales/es.json-5877-5878 (1)

5877-5878: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translate newly added Gitea locale strings to Spanish.

Several new es.json entries are still in English (for example around Line 5878, Line 7877, and throughout Line 10930-Line 11071). This will surface mixed-language UI in Spanish mode for Gitea flows.

Also applies to: 7874-7889, 10927-11071

src/renderer/src/i18n/locales/zh.json-5863-5863 (1)

5863-5863: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translate newly added Gitea strings in zh.json (currently mixed English/Chinese).

Several new Gitea entries are still English in the Simplified Chinese locale, so zh users will see mixed-language UI in core task flows.

Suggested fix (sample; apply similarly across the full new Gitea block)
- "a054078ab7": "Show Gitea in the Tasks source picker and sidebar shortcuts."
+ "a054078ab7": "在任务来源选择器和侧边栏快捷方式中显示 Gitea。"

- "9ce5f12a0d": "Connect Gitea"
+ "9ce5f12a0d": "连接 Gitea"

- "f47924e554": "Each connected Gitea server stores one token, encrypted locally."
+ "f47924e554": "每个已连接的 Gitea 服务器都会保存一个令牌,并在本地加密。"

- "heading": "Issues & pull requests"
+ "heading": "议题与拉取请求"

- "ea3f6f14a1": "No comments yet."
+ "ea3f6f14a1": "还没有评论。"

- "d2823a1de0": "Connect Gitea server"
+ "d2823a1de0": "连接 Gitea 服务器"

Also applies to: 7877-7888, 10905-11070

src/renderer/src/components/TaskPage.tsx-6293-6299 (1)

6293-6299: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clear the opposite Gitea drawer before opening a new one.

selectedGiteaItem and selectedGiteaPr are rendered independently below, so switching from an open PR to an issue—or vice versa—can leave both workspaces mounted. Reset the inactive selection when opening the active type.

Proposed fix
       const selection = { repo, item, scope: makeGiteaScope(repo) }
       if (item.type === 'pull') {
+        setSelectedGiteaItem(null)
         setSelectedGiteaPr(selection)
         return
       }
+      setSelectedGiteaPr(null)
       setSelectedGiteaItem(selection)
src/renderer/src/components/gitea-issue-comments.tsx-9-11 (1)

9-11: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize the invalid-date fallback text.

The fallback string 'recently' is hardcoded while the rest of the component is localized. Route this value through translate(...) for locale consistency.

src/renderer/src/components/gitea-pr-file-diff.tsx-25-32 (1)

25-32: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize file status labels.

STATUS_LABELS is user-facing and currently hardcoded in English. Move labels behind translate(...) to match the rest of the Gitea UI localization.

src/main/ipc/gitea-pr.ts-64-66 (1)

64-66: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Tighten PR input validation before forwarding to domain calls.

Lines 64-65 coerce invalid SHA inputs to '', and Line 120 accepts non-finite or non-positive line numbers. This pushes malformed payloads downstream instead of failing fast at the IPC boundary.

💡 Proposed fix
   ipcMain.handle(
     'gitea:prFileContents',
@@
       const repo = assertRegisteredRepo(args, store)
       if (typeof args.path !== 'string' || !args.path) {
         return { original: '', modified: '', originalIsBinary: false, modifiedIsBinary: false }
       }
+      if (
+        typeof args.baseSha !== 'string' ||
+        !args.baseSha.trim() ||
+        typeof args.headSha !== 'string' ||
+        !args.headSha.trim()
+      ) {
+        return { original: '', modified: '', originalIsBinary: false, modifiedIsBinary: false }
+      }
       return getGiteaPullRequestFileContents(
         repo.path,
         {
           path: args.path,
           oldPath: typeof args.oldPath === 'string' ? args.oldPath : undefined,
           status: args.status,
-          baseSha: typeof args.baseSha === 'string' ? args.baseSha : '',
-          headSha: typeof args.headSha === 'string' ? args.headSha : ''
+          baseSha: args.baseSha.trim(),
+          headSha: args.headSha.trim()
         },
         repoConnectionId(repo)
       )
@@
-      if (typeof args.line !== 'number') {
+      if (!Number.isInteger(args.line) || args.line < 1) {
         return { ok: false, error: 'Line is required.' }
       }

Also applies to: 120-122

src/preload/api-types.ts-1723-1723 (1)

1723-1723: ⚠️ Potential issue | 🟡 Minor

Update selectServer to use GiteaServerSelection type for type consistency and correctness.

The preload API currently declares selectServer with plain string, but all actual implementations (IPC handler in src/main/ipc/gitea.ts, main handler in src/main/gitea/connect.ts, and renderer slice in src/renderer/src/store/slices/gitea.ts) use the GiteaServerSelection type. Additionally, GiteaServerSelection is defined as string | 'all', and the implementation actively uses the 'all' value to handle multi-server selection, which the current preload signature cannot express.

🧹 Nitpick comments (4)
src/renderer/src/components/gitea-connect-dialog.tsx (1)

172-172: ⚡ Quick win

Replace arbitrary font-size utility with a style-guide token class.

text-[11px] introduces an ad-hoc size. Please switch to the closest documented typography token/class from your style guide to keep UI typography consistent.

As per coding guidelines, "Never invent new color values, font sizes, or shadow tiers when a documented one in STYLEGUIDE.md already covers the role" and "All UI work ... must follow docs/STYLEGUIDE.md and use tokens defined in src/renderer/src/assets/main.css".

Source: Coding guidelines

src/renderer/src/components/settings/gitea-integration-card.tsx (1)

173-173: ⚡ Quick win

Use style-guide tokens instead of arbitrary utility values for typography/layering.

text-[11px], z-[110], and z-[120] look like ad-hoc values. Please map these to documented tokenized classes/tiers defined by the style guide.

As per coding guidelines, "Never invent new color values, font sizes, or shadow tiers when a documented one in STYLEGUIDE.md already covers the role" and "All UI work ... must follow docs/STYLEGUIDE.md and use tokens defined in src/renderer/src/assets/main.css".

Also applies to: 198-199

Source: Coding guidelines

src/renderer/src/components/gitea-issue-meta-controls.tsx (1)

217-217: 💤 Low value

Simplify: remove unnecessary cn() wrapper.

The cn() utility is only needed when merging multiple classes or handling conditional class logic. Here it wraps a single static string.

Proposed simplification
-                {selectedLogins.has(user.login) ? (
-                  <Check className={cn('size-3.5 shrink-0')} />
-                ) : null}
+                {selectedLogins.has(user.login) ? <Check className="size-3.5 shrink-0" /> : null}
src/renderer/src/components/gitea-pr-tab-body.tsx (1)

80-90: ⚡ Quick win

Pre-group review comments before rendering file diffs.

Filtering reviewComments inside each files.map(...) scales poorly on large PRs. Build a path-indexed map once per render (or memoize) and pass grouped arrays to each file row.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1e7b4b03-874a-4d30-a599-1ecff7eb9684

📥 Commits

Reviewing files that changed from the base of the PR and between c8f583c and ed066fd.

📒 Files selected for processing (62)
  • src/main/gitea/client.ts
  • src/main/gitea/connect.test.ts
  • src/main/gitea/connect.ts
  • src/main/gitea/issues.test.ts
  • src/main/gitea/issues.ts
  • src/main/gitea/mappers.test.ts
  • src/main/gitea/mappers.ts
  • src/main/gitea/pull-requests.test.ts
  • src/main/gitea/pull-requests.ts
  • src/main/gitea/request.ts
  • src/main/gitea/server-store.test.ts
  • src/main/gitea/server-store.ts
  • src/main/ipc/gitea-pr.ts
  • src/main/ipc/gitea-repo-access.ts
  • src/main/ipc/gitea.ts
  • src/main/ipc/register-core-handlers.ts
  • src/preload/api-types.ts
  • src/preload/index.ts
  • src/renderer/src/components/GiteaIssueWorkspace.tsx
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/renderer/src/components/TaskPage.tsx
  • src/renderer/src/components/automations/automation-source-display.ts
  • src/renderer/src/components/automations/automation-target-availability.ts
  • src/renderer/src/components/gitea-connect-dialog.tsx
  • src/renderer/src/components/gitea-issue-comments.tsx
  • src/renderer/src/components/gitea-issue-meta-controls.tsx
  • src/renderer/src/components/gitea-new-issue-dialog.tsx
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/renderer/src/components/gitea-pr-file-diff.tsx
  • src/renderer/src/components/gitea-pr-line-comments.tsx
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/components/gitea-pr-tab-body.tsx
  • src/renderer/src/components/icons/GiteaIcon.tsx
  • src/renderer/src/components/settings/IntegrationsPane.tsx
  • src/renderer/src/components/settings/TasksPane.tsx
  • src/renderer/src/components/settings/gitea-integration-card.test.tsx
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/renderer/src/components/settings/task-tracker-integration-cards.tsx
  • src/renderer/src/components/settings/use-integration-provider-status-refresh.ts
  • src/renderer/src/components/task-page-list-chrome-visibility.ts
  • src/renderer/src/components/task-page-localized-options.tsx
  • src/renderer/src/components/task-source-context-summary-format.ts
  • src/renderer/src/components/task-source-context-summary.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/store/index.ts
  • src/renderer/src/store/slices/diffComments.test.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/renderer/src/store/slices/gitea.ts
  • src/renderer/src/store/slices/store-test-helpers.ts
  • src/renderer/src/store/slices/ui.ts
  • src/renderer/src/store/types.ts
  • src/shared/gitea-types.ts
  • src/shared/integration-credential-errors.ts
  • src/shared/task-providers.test.ts
  • src/shared/task-providers.ts
  • src/shared/task-source-context.ts
  • src/shared/types.ts

Comment thread src/main/gitea/connect.ts Resolved
Comment thread src/main/gitea/issues.ts Resolved
Comment thread src/main/gitea/issues.ts Resolved
Comment thread src/main/gitea/request.ts Resolved
Comment thread src/renderer/src/i18n/locales/ja.json Outdated
Comment on lines +5899 to +5900
"d8a6f1c3fe": "Gitea",
"a054078ab7": "Show Gitea in the Tasks source picker and sidebar shortcuts."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Japanese locale regression: new Gitea strings are still English.

Several newly added ja entries are untranslated (for example, Line 5900, Line 7878, Line 10930, Line 11010). In a Japanese locale file, this causes mixed-language UI in core Gitea flows (settings, task list, issue/PR workspaces). Please localize these new Gitea strings to Japanese before release.

Also applies to: 7877-7888, 10930-11071

Comment thread src/renderer/src/i18n/locales/ko.json Outdated
Comment on lines +7874 to +7890
"gitea": {
"integration": {
"card": {
"069f07af1c": "{{value0}} server{{value1}} connected",
"31b7f980fb": "Checking Gitea access before showing setup actions.",
"5a8cd33300": "Browse, create, and start work from Gitea issues.",
"bdcb1e773f": "Add server",
"9ce5f12a0d": "Connect Gitea",
"3852b1fd6f": "Verified",
"09bde44485": "Testing...",
"2bf28d5929": "Test",
"d0b31b4845": "Disconnect {{value0}}",
"f47924e554": "Each connected Gitea server stores one token, encrypted locally.",
"c043731756": "Connect a self-hosted Gitea server with its URL and a personal access token. Credentials are stored locally and encrypted when local runtime storage supports it.",
"9a555db81e": "Re-check"
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Korean locale regression: newly added Gitea copy is still English

Line 5863, Line 7874, and most of Line 10927-Line 11071 introduce user-facing strings in English inside ko.json. Please provide Korean translations for the new Gitea surface before release to avoid mixed-language UI in the Korean locale.

Also applies to: 10927-11071, 5863-5863

Comment thread src/renderer/src/store/slices/gitea.ts Resolved
Comment thread src/renderer/src/store/slices/gitea.ts Resolved

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/src/components/GiteaIssueWorkspace.tsx (1)

79-84: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard every async continuation with the current selection request.

The initial load ignores stale responses, but closing the sheet does not advance requestRef, and refreshDetail, state toggles, and comment submission update local state after awaits without checking that the same item is still selected. A late response from item A can overwrite detail/closed/comments while item B is open.

🐛 Suggested guard pattern
   useEffect(() => {
     if (!selection || !item || !scope) {
+      requestRef.current += 1
+      setDetail(null)
+      setComments([])
+      setCommentDraft('')
+      setRepoLabels([])
+      setRepoAssignees([])
+      setClosed(false)
+      setLoading(false)
       return
     }
   const refreshDetail = useCallback(async (): Promise<void> => {
     if (!scope || !item) {
       return
     }
+    const requestId = requestRef.current
     const issue = (await window.api.gitea.issue({
       repoPath: scope.repoPath,
       repoId: scope.repoId ?? null,
       sourceContext: scope.sourceContext ?? null,
       number: item.number
     })) as GiteaIssue | null
-    if (issue) {
+    if (issue && requestId === requestRef.current) {
       setDetail(issue)
       setClosed(issue.state !== 'open')
     }
   }, [scope, item])
       if (!result.ok) {
         throw new Error(result.error)
       }
-      setClosed(nextState === 'closed')
+      if (requestId === requestRef.current) {
+        setClosed(nextState === 'closed')
+      }
       if (!result.ok) {
         throw new Error(result.error)
       }
-      setComments((prev) => [
-        ...prev,
-        { id: Date.now(), body, createdAt: new Date().toISOString() }
-      ])
-      setCommentDraft('')
+      if (requestId === requestRef.current) {
+        setComments((prev) => [
+          ...prev,
+          { id: Date.now(), body, createdAt: new Date().toISOString() }
+        ])
+        setCommentDraft('')
+      }

Also applies to: 149-163, 165-190, 194-218

🧹 Nitpick comments (1)
src/main/ipc/register-core-handlers.test.ts (1)

396-396: ⚡ Quick win

Add duplicate-registration guard coverage for Gitea as well.

Line 396 validates initial registration, but the "only registers once" test should also assert registerGiteaHandlersMock is not called on the second invocation to fully lock the module-level guard behavior.

Suggested test assertion
   expect(registerBrowserHandlersMock).not.toHaveBeenCalled()
+  expect(registerGiteaHandlersMock).not.toHaveBeenCalled()
   // Why: ipcMain.handle throws on duplicate channel registration, so the

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e9bd1bb2-b2d8-45b2-8139-b63656216d15

📥 Commits

Reviewing files that changed from the base of the PR and between db70f2e and df06152.

📒 Files selected for processing (23)
  • src/main/gitea/client.test.ts
  • src/main/gitea/connect.ts
  • src/main/gitea/issues.ts
  • src/main/gitea/request.ts
  • src/main/ipc/register-core-handlers.test.ts
  • src/main/persistence.test.ts
  • src/renderer/src/assets/main.css
  • src/renderer/src/components/GiteaIssueWorkspace.tsx
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/renderer/src/components/TaskPage.tsx
  • src/renderer/src/components/gitea-new-issue-dialog.tsx
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/renderer/src/components/gitea-pr-file-diff.tsx
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/store/slices/gitea.test.ts
  • src/renderer/src/store/slices/gitea.ts
✅ Files skipped from review due to trivial changes (2)
  • src/main/persistence.test.ts
  • src/renderer/src/assets/main.css
🚧 Files skipped from review as they are similar to previous changes (17)
  • src/renderer/src/components/gitea-new-issue-dialog.tsx
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/en.json
  • src/main/gitea/request.ts
  • src/main/gitea/connect.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/i18n/locales/ja.json
  • src/main/gitea/issues.ts
  • src/renderer/src/store/slices/gitea.ts
  • src/renderer/src/components/TaskPage.tsx

@beatkapo

Copy link
Copy Markdown
Author

Rebased onto the latest main (force-push) and tidied to two clean commits:

  1. feat(gitea): Gitea as a task source (issues + PR review)
  2. feat(gitea): right-sidebar Checks panel for Gitea PRs — checks, conversation comments (view + add), merge, close/reopen and title editing, reusing the Gitea PR IPCs.

Note: main already merged the Gitea source-control / hosted-review base (client.ts, pull-request-mappers, repository-ref, hosted-review-gitea). After the rebase those are identical to upstream and drop out of the diff, so this PR is now just the task-source layer + Checks panel on top of that base (client.ts keeps a small task-source delta).

Verified locally: typecheck (node + web), lint (incl. 5-locale i18n parity), and the Gitea / checks-panel / hosted-review test suites all pass. Now MERGEABLE (71 files).

@AmethystLiang

Copy link
Copy Markdown
Contributor

thanks for the PR! we're doing some refactoring work to add more task sources. and will land this after the refactoring

@nwparker nwparker added the enhancement New feature or request label Jul 27, 2026
@CannyDuck

Copy link
Copy Markdown

Do you have an estimate for when we can expect an update once the refactoring is complete?

fgo and others added 4 commits September 21, 2026 10:24
Ports the Gitea task-source backend onto the per-provider task-source
architecture: adds 'gitea' to TaskProvider, its provider identity, the
IPC handlers, the preload bridge, and the renderer store slice.

The Tasks-page UI is not wired yet; this commit only makes the provider
exist end to end so the UI can be added on top.

Gitea is integrated as a repo-scoped source alongside GitHub and GitLab:
the saved server holds credentials per host, while issues and pull
requests are scoped by repo path, matching how the slice resolves them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds the Gitea provider to the Tasks page under the per-provider layout:
a gitea/Content that lists the selected repos' issues and pull requests,
opens either one inline, and starts a workspace from the selected item.

Gitea is added to the source picker so it can be chosen like any other
provider, and the linked-work-item provider unions accept it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds the Gitea card to Integrations and a two-step guided setup in Task
Sources, matching how Jira is set up: connect a server, then choose
whether it shows in Tasks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@beatkapo
beatkapo force-pushed the feat/gitea-task-source branch from 84a2410 to 7ed9b13 Compare September 21, 2026 09:08
@beatkapo

Copy link
Copy Markdown
Author

@AmethystLiang the task-source refactor you were waiting on has landed, so I've rebuilt this branch on top of it. @CannyDuck — this is the update you asked about.

This is a re-implementation, not a rebase. TaskPage.tsx is gone, so the old revision could not be rebased: the provider now lives in task-page/gitea/ and plugs into the per-provider dispatch, the same shape as jira/ and linear/. The branch is on today's main and went from CONFLICTING to MERGEABLE. The previous head was 84a2410 if you want to diff against it.

Verified: pnpm tc, oxlint, the max-lines ratchet, reliability gates, the RPC params catalog, all four localization verifiers, and the targeted suites (task-page 148 files / 1223 tests, settings, shared, main/gitea, persistence, and the global-fetch audit). The four commits are separable — backend, i18n, Tasks UI, Settings — so the backend can be reviewed or landed without the UI.

The CodeRabbit comments above are from the June revision and are stale; almost none of that code survives.

Four things I think are yours to decide, not mine:

  1. Existing profiles don't gain Gitea. New installs get it because it's in TASK_PROVIDERS, but I deliberately did not add a visibleTaskProvidersDefaultedForGitea migration next to the Jira one in prepare-loaded-profile-settings.ts. That writes to every existing user's settings, which felt like a product call rather than a contributor's. Say the word and I'll add it.

  2. Repo-scoped, not account-scoped. Gitea issues are addressed by repo, so it reuses the repo-backed summary and availability paths alongside GitHub/GitLab, with the saved server acting as per-host credential storage. The alternative — account-scoped like Jira, with a server picker in SourceBar — would mean widening the taskSource === 'linear' || taskSource === 'jira' guards instead. If you'd rather it behave like Jira, that's a contained change but it should be settled before review, not after.

  3. List chrome stays visible when a Gitea item is open. Detail selection lives in task-page/gitea/Content.tsx rather than the composer model, so shouldHideTaskPageListChrome returns false for 'gitea'. This kept the change out of the 39-stage pipeline and off use-task-page-composer-actions.ts (264/300 lines). If you want it to match the other providers, it needs a model stage and that hook would need splitting first.

  4. Size. It's ~6.5k lines. I've split it into four commits so it can land in stages, but if you'd rather have separate PRs — backend first, UI second — I'll split it that way.

Two smaller notes: there's no visual proof attached because I don't have a public Gitea instance to demo, and the non-English catalogs carry the English strings for the new keys rather than translations (that was also true of the June revision). Happy to fix either.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 12


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: db2f286c-0ee6-4491-aca0-f5d1d0e85666

📥 Commits

Reviewing files that changed from the base of the PR and between bbc8a8c and 7ed9b13.

📒 Files selected for processing (60)
  • src/main/gitea/connect.ts
  • src/main/gitea/request.ts
  • src/main/gitea/server-store.ts
  • src/main/global-fetch-call-site-audit.test.ts
  • src/main/ipc/gitea-pr.ts
  • src/main/ipc/gitea-repo-access.ts
  • src/main/ipc/gitea.ts
  • src/main/ipc/register-core-handlers/register-core-handlers.test.ts
  • src/main/ipc/register-core-handlers/register-core-handlers.ts
  • src/main/persistence-settings-ui-defaults.test.ts
  • src/main/persistence-source-control-ai-migration.test.ts
  • src/preload/api-types.ts
  • src/preload/api/gitea-api.ts
  • src/preload/api/gitea-bridge.ts
  • src/preload/index.ts
  • src/renderer/src/components/GiteaIssueWorkspace.tsx
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/renderer/src/components/automations/automation-source-display.ts
  • src/renderer/src/components/automations/automation-target-availability.ts
  • src/renderer/src/components/gitea-issue-comments.tsx
  • src/renderer/src/components/gitea-issue-meta-controls.tsx
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/renderer/src/components/gitea-pr-file-diff.tsx
  • src/renderer/src/components/gitea-pr-line-comments.tsx
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/components/gitea-pr-tab-body.tsx
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.test.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.ts
  • src/renderer/src/components/settings/IntegrationsPane.tsx
  • src/renderer/src/components/settings/TaskSourceSimpleSetup.tsx
  • src/renderer/src/components/settings/TasksPane.test.tsx
  • src/renderer/src/components/settings/TasksPane.tsx
  • src/renderer/src/components/settings/gitea-integration-card.test.tsx
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/renderer/src/components/settings/task-provider-integration-section-ids.ts
  • src/renderer/src/components/settings/task-source-setup-state.test.ts
  • src/renderer/src/components/settings/tasks-search.ts
  • src/renderer/src/components/settings/use-task-source-provider-readiness.ts
  • src/renderer/src/components/task-page-list-chrome-visibility.ts
  • src/renderer/src/components/task-page-localized-options.tsx
  • src/renderer/src/components/task-page/Content.tsx
  • src/renderer/src/components/task-page/gitea/Content.tsx
  • src/renderer/src/components/task-provider-identity-label.ts
  • src/renderer/src/components/task-source-context-summary.ts
  • src/renderer/src/hooks/composer-state/derived-model.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/store/index.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/renderer/src/store/slices/gitea.ts
  • src/renderer/src/store/slices/store-test-helpers.ts
  • src/renderer/src/store/slices/ui/ui-slice-contract-core.ts
  • src/renderer/src/store/types.ts
  • src/shared/gitea-types.ts
  • src/shared/integration-credential-errors.ts
  • src/shared/task-provider-identity.ts
  • src/shared/task-providers.test.ts
  • src/shared/task-providers.ts
  • src/shared/workspace-name.ts
  • src/shared/worktree/types.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/shared/task-providers.test.ts
  • src/renderer/src/components/automations/automation-source-display.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread src/main/gitea/connect.ts
Comment on lines +82 to +91
const servers = file.servers.filter((server) => hasStoredToken(server.id))
const activeServer =
servers.find((server) => server.id === file.activeServerId) ?? servers[0] ?? null
// Why: keep the reported selection valid — 'all' is always allowed, otherwise
// it must point at a still-connected server (fall back to the active one).
const selectedServerId =
file.selectedServerId === 'all' || servers.some((server) => server.id === file.selectedServerId)
? file.selectedServerId
: (activeServer?.id ?? null)
const credentialError = getCredentialError(servers.map((server) => server.id))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Probe stored tokens before reporting credential status.

After a process restart, credentialErrors is empty. getStatus checks only whether each token file has content. It therefore reports an undecryptable credential as connected without setting credentialError.

Call getServerTokens('all') before reading getCredentialError. This call records per-server decryption failures while preserving the saved server metadata.

Comment on lines +37 to +40
export function normalizeGiteaApiBaseUrl(value: string): string {
const trimmed = value.trim().replace(/\/+$/, '')
return /\/api\/v1$/i.test(trimmed) ? trimmed : `${trimmed}/api/v1`
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🛡️ Analyzed with Security Review | 🟠 Major | ⚡ Quick win

Sensitive Data Exposure

Reachability: External
Exploitability: Moderate
CWE: CWE-319 — Cleartext Transmission of Sensitive Information

Reject plaintext Gitea credential destinations.

The connection and request paths accept http:// API bases and then attach an access token. For a non-loopback HTTP server, a network observer can intercept the token.

  • src/main/gitea/server-store.ts#L37-L40: Reject non-HTTPS API bases unless the host is loopback.
  • src/main/gitea/connect.ts#L106-L108: Validate the transport scheme before fetchGiteaUser sends the token.
  • src/main/gitea/request.ts#L102-L104: Enforce the same rule for authenticated JSON reads.
  • src/main/gitea/request.ts#L151-L153: Enforce the same rule for authenticated text reads.
  • src/main/gitea/request.ts#L196-L204: Enforce the same rule for authenticated writes.

Use one shared validator so stored, environment, and repository-derived API bases follow the same policy. Based on learnings, credential-bearing requests must reject HTTP except for loopback hosts.

📍 Affects 3 files
  • src/main/gitea/server-store.ts#L37-L40 (this comment)
  • src/main/gitea/connect.ts#L106-L108
  • src/main/gitea/request.ts#L102-L104
  • src/main/gitea/request.ts#L151-L153
  • src/main/gitea/request.ts#L196-L204

Comment on lines +31 to +38
const STATUS_LABELS: Record<GiteaPRFile['status'], string> = {
added: 'Added',
modified: 'Modified',
deleted: 'Deleted',
renamed: 'Renamed',
copied: 'Copied',
changed: 'Changed'
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

rg -n 'translate\(|STATUS_LABELS|statusLabel=|\{state\}|\{check\.state\}|\{method\}' src/renderer/src/components/gitea-pr-file-diff.tsx src/renderer/src/components/GiteaPullRequestWorkspace.tsx src/renderer/src/components/gitea-pr-checks.tsx src/renderer/src/components/gitea-pr-merge-button.tsx src/renderer/src/components/settings/gitea-integration-card.tsx
find . -maxdepth 3 -iname '*styleguide*' -o -iname '*contributing*' -o -iname '*guideline*'
rg -n 'localiz|i18n|user-facing strings|translate\(' CONTRIBUTING* README* docs .github 2>/dev/null

Repository: stablyai/orca

Length of output: 5980


🤖 get_repo_knowledge executed:

get_repo_knowledge stablyai/orca /tmp/coderabbit-repo-knowledge/stablyai-orca-89dc44e4/learnings

Length of output: 30621


🏁 Script executed:

#!/bin/bash
set -o pipefail
printf '%s\n' '--- guidance files ---'
sed -n '1,220p' docs/STYLEGUIDE.md
printf '%s\n' '--- contributing localization references ---'
rg -n -C 3 -i 'localiz|i18n|translate|user.?visible|copy|string' .github/CONTRIBUTING.md
printf '%s\n' '--- focused source ranges ---'
for spec in \
  'src/renderer/src/components/gitea-pr-file-diff.tsx:1:150' \
  'src/renderer/src/components/GiteaPullRequestWorkspace.tsx:300:345' \
  'src/renderer/src/components/gitea-pr-checks.tsx:1:75' \
  'src/renderer/src/components/gitea-pr-merge-button.tsx:1:65' \
  'src/renderer/src/components/settings/gitea-integration-card.tsx:80:125'; do
  IFS=: read -r file start end <<< "$spec"
  printf '\n--- %s:%s-%s ---\n' "$file" "$start" "$end"
  sed -n "${start},${end}p" "$file" | nl -ba -v "$start"
done
printf '%s\n' '--- translation declarations and direct prop consumers ---'
rg -n -C 3 'function translate|const translate|export .*translate|statusLabel|interface .*Status|type .*Status|connected' src/renderer/src | head -240

Repository: stablyai/orca

Length of output: 41877


🏁 Script executed:

sed -n '1,220p' docs/STYLEGUIDE.md; printf '\n--- CONTRIBUTING ---\n'; rg -n -C 4 -i 'localiz|i18n|translate|user.?visible|copy|string' .github/CONTRIBUTING.md; printf '\n--- sources ---\n'; sed -n '1,145p' src/renderer/src/components/gitea-pr-file-diff.tsx; sed -n '315,338p' src/renderer/src/components/GiteaPullRequestWorkspace.tsx; sed -n '1,65p' src/renderer/src/components/gitea-pr-checks.tsx; sed -n '1,55p' src/renderer/src/components/gitea-pr-merge-button.tsx; sed -n '85,118p' src/renderer/src/components/settings/gitea-integration-card.tsx

Repository: stablyai/orca

Length of output: 29431


🏁 Script executed:

rg -n -C 5 'function IntegrationCardShell|const IntegrationCardShell|statusLabel' src/renderer/src/components src/renderer/src | head -160

Repository: stablyai/orca

Length of output: 14875


Localize the Gitea status labels. These user-visible labels bypass translate() and can remain English in non-English or pseudo-localized locales. Apply localization to the file status labels, pull-request state, check state, and merge methods.

const sorted = [...comments].sort((a, b) => a.line - b.line)

const handleSubmit = async (): Promise<void> => {
const lineNumber = Number.parseInt(line, 10)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject decimal line numbers instead of truncating them.

Number.parseInt('12.5', 10) produces 12. The comment can therefore be submitted against a different line than the user entered.

Proposed fix
-    const lineNumber = Number.parseInt(line, 10)
+    const lineNumber = Number(line)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const lineNumber = Number.parseInt(line, 10)
const lineNumber = Number(line)

Comment on lines +214 to +217
setComments((prev) => [
...prev,
{ id: Date.now(), body, createdAt: new Date().toISOString() }
])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

New comments lose their author in both workspaces. Both optimistic updates create a partial GiteaComment without user, so the shared comment renderer displays “Unknown.”

  • src/renderer/src/components/GiteaIssueWorkspace.tsx#L214-L217: refresh issue comments or append the comment returned by the API.
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx#L150-L153: refresh PR conversation comments or append the comment returned by the API.
📍 Affects 2 files
  • src/renderer/src/components/GiteaIssueWorkspace.tsx#L214-L217 (this comment)
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx#L150-L153

})
}
})
.catch(() => {})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

sed -n '70,140p' src/renderer/src/components/GiteaPullRequestWorkspace.tsx
sed -n '240,360p' src/renderer/src/components/GiteaPullRequestWorkspace.tsx

Repository: stablyai/orca

Length of output: 7454


🏁 Script executed:

sed -n '1,180p' src/renderer/src/components/GiteaPullRequestWorkspace.tsx
sed -n '180,280p' src/renderer/src/components/GiteaPullRequestWorkspace.tsx
sed -n '360,680p' src/renderer/src/components/GiteaPullRequestWorkspace.tsx

Repository: stablyai/orca

Length of output: 12229


Preserve successful initial PR resources when one request fails.

If any initial request rejects, Promise.all skips the success callback. The other successful responses are not applied. The empty catch reports no initial-load error, and finally clears the loading state. The PR shell and comment editor remain visible, but the tab body receives empty resource state.

Handle each resource independently and report failures. Promise.allSettled can provide this behavior, but separate catches are also sufficient if each successful request updates its own state.

Comment thread src/renderer/src/i18n/locales/en.json Outdated
"incompleteBannerBodyWithLinear": "Hide providers you do not use, or expand a card and finish its steps. For Linear: API access, the agent skill, and Show in Tasks."
"incompleteBannerBodyWithLinear": "Hide providers you do not use, or expand a card and finish its steps. For Linear: API access, the agent skill, and Show in Tasks.",
"giteaLabel": "Gitea",
"giteaDescription": "Connect a Gitea or Forgejo server and show its issues in Tasks.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Mention pull requests in the setup descriptions.

These strings describe Gitea only as an issue source, but this provider also exposes pull requests, checks, review comments, and merge actions. Update all three descriptions so the setup UI accurately presents the available workflow.

Also applies to: 12036-12036, 17135-17135

Comment thread src/renderer/src/i18n/locales/en.json Outdated
"83b75fa9b7": "https://gitea.example.com",
"062ae1ea05": "Access token",
"79c04f358b": "Gitea personal access token",
"3ecb3e1153": "Create a token under your Gitea user settings → Applications, with read/write access to issues.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(pull-requests|gitea|package\.json|README|CONTRIBUTING|.*config.*|.*test.*)' | head -200
printf '%s\n' '--- pull-requests symbols and implementation ---'
if [ -f src/main/gitea/pull-requests.ts ]; then
  ast-grep outline src/main/gitea/pull-requests.ts
  cat -n src/main/gitea/pull-requests.ts
else
  printf '%s\n' 'src/main/gitea/pull-requests.ts not present'
fi
printf '%s\n' '--- Gitea references in source/config/tests ---'
rg -n -S -g '!src/renderer/src/i18n/locales/en.json' 'Gitea|gitea|pull.?request|permissions|scope|version' src package.json README.md CONTRIBUTING.md 2>/dev/null | head -300
printf '%s\n' '--- locale context ---'
sed -n '17125,17150p' src/renderer/src/i18n/locales/en.json

Repository: stablyai/orca

Length of output: 41625


🤖 get_repo_knowledge executed:

get_repo_knowledge stablyai/orca /tmp/coderabbit-repo-knowledge/stablyai-orca-89dc44e4/learnings

Length of output: 31740


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- pull request file ---'
cat -n src/main/gitea/pull-requests.ts 2>&1 || true
printf '%s\n' '--- related Gitea files ---'
git ls-files | rg 'gitea|Gitea' | head -100
printf '%s\n' '--- Gitea references ---'
rg -n -S 'Gitea|gitea|pull.?request|permission|scope|version' src package.json README.md 2>/dev/null | head -250
printf '%s\n' '--- locale entry ---'
sed -n '17135,17145p' src/renderer/src/i18n/locales/en.json

Repository: stablyai/orca

Length of output: 41424


🏁 Script executed:

pwd; git ls-files src/main/gitea src/renderer/src/i18n/locales/en.json; sed -n '1,240p' src/main/gitea/pull-requests.ts

Repository: stablyai/orca

Length of output: 8054


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- request/auth implementation ---'
cat -n src/main/gitea/request.ts
printf '%s\n' '--- connection implementation ---'
cat -n src/main/gitea/connect.ts
printf '%s\n' '--- connection and request tests relevant to permissions/versions ---'
rg -n -C 4 -i 'permission|scope|token|version|401|403|pull|merge|write' src/main/gitea/*.test.ts src/main/gitea/*.ts
printf '%s\n' '--- supported Gitea references in repository ---'
rg -n -i 'gitea.{0,40}(version|support)|support.{0,40}gitea|gitea.{0,40}(scope|permission)|permission.{0,40}gitea' . --glob '!src/renderer/src/i18n/locales/*.json' --glob '!src/shared/__fixtures__/**' | head -200

Repository: stablyai/orca

Length of output: 42781


🌐 Web query:

official Gitea documentation personal access token permissions pull request merge API issue permission scope

💡 Result:

<source_evidence>

<title>Gitea API for AI Agents: Methods, Permissions & Safe Access | Bollard AI</title> https://bollardai.com/resources/gitea The Gitea API is how an app or AI agent works with a Gitea instance: reading and creating repositories, filing and editing issues, opening and merging pull requests, and reading or writing files in a repository. Access is granted through an access token sent in an Authorization header, and each token carries scopes that decide which areas it can read or write, so an agent is limited to the repositories and accounts that token reaches. Gitea can also push events to a webhook URL when something happens in a repository. ... Every write and most private reads need an access token. The token is sent as &`#39`;Authorization: token &`#39`;, a header format Gitea keeps for historical reasons, or as a &`#39`;token&`#39`; or &`#39`;access_token&`#39`; query parameter; OAuth2 tokens may instead use &`#39`;Authorization: bearer &`#39`;. Basic authentication and signed SSH requests are also supported. Tokens are created through the API or in the user settings UI. ... An access token carries scopes that gate which areas it can reach, each available as a read or write variant: read:repository and write:repository, read:issue and write:issue, read:user and write:user, read:organization and write:organization, plus read:package, read:notification, read:misc, read:activitypub, and an admin scope for instance administration. A scope of all grants read and write across everything. Granular scopes were added in Gitea 1.19 and extended in later releases. ... The Gitea project maintains an ... com/gitea/gitea-m ... . It lets an ... itea through ... Model Context Protocol over standard input ... , and covers repositories, ... A personal access token is the standard way to authenticate. It carries a chosen set of read and write scopes, so it can be limited to only the areas an integration needs, and it is sent in the Authorization header on each request. ... The Gitea API is split into areas an agent can act on, such as repositories, issues, pull requests, file contents, releases, and webhooks. Each area maps to its own token scope, and some grant access to far more than others. ... ### Pull requests ... List, read, create, and edit pull requests, and merge a pull request into its base branch. ... | Issues List, read, create, and edit issues in a repository, and list and create their comments. 5 | | | | | | ... | GET | `/repos/{owner}/{repo}/issues` | List a repository&`#39`;s issues. | read | `read:issue` | Current | ... | Pull requests can also be returned here, filtered through the type parameter. Acts on issue Permission (capability)`read:issue` Version Available since the API’s base version Webhook event None Rate limit Standard limits apply Source Official documentation ↗ | | | | | | ... | POST | `/repos/{owner}/{repo}/issues` | Create an issue. | write | `write:issue` | Current | ... | Creating an issue needs the write:issue scope. Acts on issue Permission (capability)`write:issue` Version Available since the API’s base version Webhook event`issues` Rate limit Standard limits apply Source Official documentation ↗ | | | | | | ... | Pull requests List, read, create, and edit pull requests, and merge a pull request into its base branch. 5 | | | | | | ... | GET | `/repos/{owner}/{repo}/pulls` | List a repository&`#39`;s pull requests. | read | `read:repository` | Current | ... | Pull requests live in the repository area, so they use the repository scope. Acts on pull request Permission (capability)`read:repository` Version Available since the API’s base version Webhook event None Rate limit Standard limits apply Source Official documentation ↗ | | | | | | ... | POST | `/repos/{owner}/{repo}/pulls` | Create a pull request. | write | `write:repository` | Current | ... | Opens a pull request from a head branch into a base branch. Acts on pull request Permission (capability)`write:repository` Version Available since the API’s base version Webhook event`pull_request` Rate limit Standard limits apply Source Official documentation ↗ | | | | | | ... | POST | `/rep…[truncated] <title>OAuth2 Provider | Gitea Documentation</title> https://docs.gitea.com/1.26/development/oauth2-provider/ As of version v1.23, Gitea supports granular scopes, allowing third parties to request more limited access. These scopes, previously available only for Personal Access Tokens, enable users to restrict access to specific URL routes. ... Scopes are grouped by high-level API routes and further refined as follows: ... - `read`: `GET` routes - `write`: `POST`, `PUT`, `PATCH`, and `DELETE` routes (in addition to `GET`) ... When any of the granular scopes known from Personal Access Tokens is introduced, Gitea will not allow full access (as it does by default). Instead, it will build granular access following read or write permissions to resources such as Repositories, Issues, ActivityPub, Admin functions, Organizations, Users, Packages, or miscellaneous features. ... > NOTE: If third party adds any scope other than the OIDC ones: `openid`, `email`, `profile`, and `groups` or the ones found already in Personal Access Token the scope will fallback to full access as it was the case before v1.23 ... The approval page displayed to the user shows the list of scopes requested by the third party. Once approved, this decision is remembered. If the third party changes its requested scopes in future requests, the entire flow will fail, requiring re-authorization. ... Gitea supports scoped access tokens, which allow users the ability to restrict tokens to operate only on selected url routes. Scopes are grouped by high-level API routes, and further refined to the following: ... - `read`: `GET` routes - `write`: `POST`, `PUT`, `PATCH`, and `DELETE` routes (in addition to `GET`) ... Gitea token scopes are as follows: ... | Name | Description | | --- | --- | | (no scope) | Not supported. A scope is required even for public repositories. | | activitypub | `activitypub` API routes: ActivityPub related operations. | | read:activitypub | Grants read access for ActivityPub operations. | | write:activitypub | Grants read/write/delete access for ActivityPub operations. | ... | admin | `/admin ... hidden for non-admin accounts). | | read: ... | Grants read access for ... operations, such ... or registered user emails. | ... | write:admin | ... read/write/delete access for ... operations, such as running cron jobs or updating user accounts. | ... | issue | `issues/*`, `labels/*`, `milestones/*` API routes: Issue-related operations. | | read:issue | Grants read access for issues operations, such as getting issue comments, issue attachments, and milestones. | | write:issue | Grants read/write/delete access for issues operations, such as posting or editing an issue comment or attachment, and updating milestones. | ... | repository | `/repos/*` API routes except `/repos/issues/*`: Repository file, pull-request, and release operations. | | read:repository | Grants read access to repository operations, such as getting repository files, releases, collaborators. | | write:repository | Grants read/write/delete access to repository operations, such as getting updating repository files, creating pull requests, updating collaborators. | <title>OAuth2 Provider | Gitea Documentation</title> https://docs.gitea.com/development/oauth2-provider/ As of version v1.23, Gitea supports granular scopes, allowing third parties to request more limited access. These scopes, previously available only for Personal Access Tokens, enable users to restrict access to specific URL routes. ... Scopes are grouped by high-level API routes and further refined as follows: ... - `read`: `GET` routes - `write`: `POST`, `PUT`, `PATCH`, and `DELETE` routes (in addition to `GET`) ... When any of the granular scopes known from Personal Access Tokens is introduced, Gitea will not allow full access (as it does by default). Instead, it will build granular access following read or write permissions to resources such as Repositories, Issues, ActivityPub, Admin functions, Organizations, Users, Packages, or miscellaneous features. ... > NOTE: If third party adds any scope other than the OIDC ones: `openid`, `email`, `profile`, and `groups` or the ones found already in Personal Access Token the scope will fallback to full access as it was the case before v1.23 ... The approval page displayed to the user shows the list of scopes requested by the third party. Once approved, this decision is remembered. If the third party changes its requested scopes in future requests, the entire flow will fail, requiring re-authorization. ... Gitea supports scoped access tokens, which allow users the ability to restrict tokens to operate only on selected url routes. Scopes are grouped by high-level API routes, and further refined to the following: ... - `read`: `GET` routes - `write`: `POST`, `PUT`, `PATCH`, and `DELETE` routes (in addition to `GET`) ... Gitea token scopes are as follows: ... | Name | Description | | --- | --- | | (no scope) | Not supported. A scope is required even for public repositories. | | activitypub | `activitypub` API routes: ActivityPub related operations. | | read:activitypub | Grants read access for ActivityPub operations. | | write:activitypub | Grants read/write/delete access for ActivityPub operations. | ... | admin | `/admin ... hidden for non-admin accounts). | | read: ... | Grants read access for ... operations, such ... or registered user emails. | ... | write:admin | ... read/write/delete access for ... operations, such as running cron jobs or updating user accounts. | ... | issue | `issues/*`, `labels/*`, `milestones/*` API routes: Issue-related operations. | | read:issue | Grants read access for issues operations, such as getting issue comments, issue attachments, and milestones. | | write:issue | Grants read/write/delete access for issues operations, such as posting or editing an issue comment or attachment, and updating milestones. | ... | repository | `/repos/*` API routes except `/repos/issues/*`: Repository file, pull-request, and release operations. | | read:repository | Grants read access to repository operations, such as getting repository files, releases, collaborators. | | write:repository | Grants read/write/delete access to repository operations, such as getting updating repository files, creating pull requests, updating collaborators. | <title>Merge a pull request | Gitea Documentation</title> https://docs.gitea.com/api/1.23/operations/repo-merge-pull-request/ Merge a pull request | Gitea Documentation # Merge a pull request POST /repos/{owner}/{repo}/pulls/{index}/merge const url = &`#39`; https://gitea.com/api/v1/repos/example/example/pulls/1/merge&`#39`;; const options = { method: &`#39`; POST&`#39`;, headers: {Authorization: &`#39`; Basic &`#39`;, &`#39`; Content-Type&`#39`;: &`#39`; application/json&`#39`;}, body: &`#39`;{"Do":"merge","MergeCommitID":"example","MergeMessageField":"example","MergeTitleField":"example","delete_branch_after_merge":true,"force_merge":true,"head_commit_id":"example","merge_when_checks_succeed":true}&`#39`; }; try { const response = await fetch(url, options); const data = await response. json(); console. log(data); } catch (error) { console. error(error); } curl --request POST \ --url https://gitea.com/api/v1/repos/example/example/pulls/1/merge \ --header &`#39`; Authorization: Basic &`#39`; \ --header &`#39`; Content-Type: application/json&`#39`; \ --data &`#39`;{ "Do": "merge", "MergeCommitID": "example", "MergeMessageField": "example", "MergeTitleField": "example", "delete_branch_after_merge": true, "force_merge": true, "head_commit_id": "example", "merge_when_checks_succeed": true }&`#39`; - gitea.com/api/v1/repos/{owner}/{repo}/pulls/{index}/merge ## Authorizations - BasicAuth - Token - AccessToken - AuthorizationHeaderToken - SudoParam - SudoHeader - TOTPHeader ## Parameters ### Path Parameters owner required string Owner of the repo repo required Name of the repo index required integer format: int64 Index of the pull request to merge ## Request Body MergePullRequestForm form for merging Pull Request object Do required string Allowed values: merge rebase rebase-merge squash fast-forward-only manually-merged MergeCommitID string MergeMessageField string MergeTitleField string delete_branch_after_merge boolean force_merge boolean head_commit_id string merge_when_checks_succeed boolean MergePullRequestForm form for merging Pull Request object Do required string Allowed values: merge rebase rebase-merge squash fast-forward-only manually-merged MergeCommitID string MergeMessageField string MergeTitleField string delete_branch_after_merge boolean force_merge boolean head_commit_id string merge_when_checks_succeed boolean ## Responses ### 200 APIEmpty is an empty response ### 404 APINotFound is a not found empty response ### 405 APIEmpty is an empty response APIError is error format response ### 423 APIRepoArchivedError is an error that is raised when an archived repo should be modified <title>Pull Requests API</title> https://go-gitea-gitea.mintlify.app/api/pull-requests # Pull Requests API ... > Create, list, update, merge, and manage pull requests in your Gitea repositories ... The Pull Requests API enables you to manage pull requests in your repositories. You can create PRs, list them with various filters, update their properties, merge them, and check merge status. ... Endpoint: ` ... /api/v1/repos/{owner}/{repo}/ ... ## Create Pull Request ... Endpoint: `POST /api/v1/repos/{owner}/{repo}/pulls` ... Whether to allow maintainers to edit the pull request ... ## Merge Pull Request ... ### Merge a Pull Request ... Merge a pull request into the base branch. ... Endpoint: `POST /api/v1/repos/{owner}/{repo}/pulls/{index}/merge` ... #### Request Body ... Merge method. Options: `merge`, `rebase`, `rebase-merge`, `squash`, `manually-merged` ... Whether to delete the head ... Force merge even if checks haven&`#39`;t passed ... Schedule auto-merge when checks succeed ... HEAD commit SHA for verification ... ```bash curl -X POST "https://gitea.example ... com/api/v1/repos/myorg/myrepo/pulls/13/merge" \ -H "Authorization: token YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d &`#39`;{ "Do": "squash", "MergeTitleField": "Add user analytics dashboard", "delete_branch_after_merge": true }&`#39`; ... ## Additional Operations ... ### Check Merge Status ... Check if a pull request has been merged. ... Endpoint: `GET /api/v1/repos/{owner}/{repo}/pulls/{index}/merge` ... ### Update Pull Request Branch ... Merge the base branch into the head branch to update ... pull request. ... Endpoint: `POST /api/v1/repos/{owner}/{repo}/pulls/{index}/update` ... ### Cancel Auto-Merge ... ### Get Pull ... by Base and Head ... ## Error Responses ... User does not have permission to perform the operation ... Repository or pull request not found ... Operation not allowed (e.g., cannot merge a work-in-progress PR) ... Merge conflict or duplicate pull request ... Validation error (e.g., invalid base/head branches)

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
cat -n src/main/gitea/request.ts
printf '%s\n' '--- connect ---'
cat -n src/main/gitea/connect.ts
printf '%s\n' '--- tests for auth and connection validation ---'
rg -n -C 5 -i 'token|authorization|permission|scope|401|403|connect|verify|user|repo' src/main/gitea/client.test.ts src/main/gitea/connect.test.ts src/main/gitea/request.ts src/main/gitea/connect.ts src/main/gitea/server-store.ts

Repository: stablyai/orca

Length of output: 42523


🌐 Web query:

site:docs.gitea.com Gitea personal access token permissions pull requests merge issue scope repository scope

💡 Result:

<source_evidence>

<title>OAuth2 Provider | Gitea Documentation</title> https://docs.gitea.com/development/oauth2-provider/ As of version v1.23, Gitea supports granular scopes, allowing third parties to request more limited access. These scopes, previously available only for Personal Access Tokens, enable users to restrict access to specific URL routes. ... When any of the granular scopes known from Personal Access Tokens is introduced, Gitea will not allow full access (as it does by default). Instead, it will build granular access following read or write permissions to resources such as Repositories, Issues, ActivityPub, Admin functions, Organizations, Users, Packages, or miscellaneous features. ... > NOTE: If third party adds any scope other than the OIDC ones: `openid`, `email`, `profile`, and `groups` or the ones found already in Personal Access Token the scope will fallback to full access as it was the case before v1.23 ... Gitea supports scoped access tokens, which allow users the ability to restrict tokens to operate only on selected url routes. Scopes are grouped by high-level API routes, and further refined to the following: ... Gitea token scopes are as follows: ... | Name | Description | | --- | --- | | (no scope) | Not supported. A scope is required even for public repositories. | | activitypub | `activitypub` API routes: ActivityPub related operations. | | read:activitypub | Grants read access for ActivityPub operations. | | write:activitypub | Grants read/write/delete access for ActivityPub operations. | ... | issue | `issues/*`, `labels/*`, `milestones/*` API routes: Issue-related operations. | | read:issue | Grants read access for issues operations, such as getting issue comments, issue attachments, and milestones. | | write:issue | Grants read/write/delete access for issues operations, such as posting or editing an issue comment or attachment, and updating milestones. | ... | repository | `/repos/*` API routes except `/repos/issues/*`: Repository file, pull-request, and release operations. | | read:repository | Grants read access to repository operations, such as getting repository files, releases, collaborators. | | write:repository | Grants read/write/delete access to repository operations, such as getting updating repository files, creating pull requests, updating collaborators. | <title>OAuth2 Provider | Gitea Documentation</title> https://docs.gitea.com/1.26/development/oauth2-provider/ As of version v1.23, Gitea supports granular scopes, allowing third parties to request more limited access. These scopes, previously available only for Personal Access Tokens, enable users to restrict access to specific URL routes. ... When any of the granular scopes known from Personal Access Tokens is introduced, Gitea will not allow full access (as it does by default). Instead, it will build granular access following read or write permissions to resources such as Repositories, Issues, ActivityPub, Admin functions, Organizations, Users, Packages, or miscellaneous features. ... > NOTE: If third party adds any scope other than the OIDC ones: `openid`, `email`, `profile`, and `groups` or the ones found already in Personal Access Token the scope will fallback to full access as it was the case before v1.23 ... Gitea supports scoped access tokens, which allow users the ability to restrict tokens to operate only on selected url routes. Scopes are grouped by high-level API routes, and further refined to the following: ... Gitea token scopes are as follows: ... | Name | Description | | --- | --- | | (no scope) | Not supported. A scope is required even for public repositories. | | activitypub | `activitypub` API routes: ActivityPub related operations. | | read:activitypub | Grants read access for ActivityPub operations. | | write:activitypub | Grants read/write/delete access for ActivityPub operations. | ... | issue | `issues/*`, `labels/*`, `milestones/*` API routes: Issue-related operations. | | read:issue | Grants read access for issues operations, such as getting issue comments, issue attachments, and milestones. | | write:issue | Grants read/write/delete access for issues operations, such as posting or editing an issue comment or attachment, and updating milestones. | ... | repository | `/repos/*` API routes except `/repos/issues/*`: Repository file, pull-request, and release operations. | | read:repository | Grants read access to repository operations, such as getting repository files, releases, collaborators. | | write:repository | Grants read/write/delete access to repository operations, such as getting updating repository files, creating pull requests, updating collaborators. | <title>OAuth2 Provider | Gitea Documentation</title> https://docs.gitea.com/next/development/oauth2-provider/ As of version v1.23, Gitea supports granular scopes, allowing third parties to request more limited access. These scopes, previously available only for Personal Access Tokens, enable users to restrict access to specific URL routes. ... When any of the granular scopes known from Personal Access Tokens is introduced, Gitea will not allow full access (as it does by default). Instead, it will build granular access following read or write permissions to resources such as Repositories, Issues, ActivityPub, Admin functions, Organizations, Users, Packages, or miscellaneous features. ... > NOTE: If third party adds any scope other than the OIDC ones: `openid`, `email`, `profile`, and `groups` or the ones found already in Personal Access Token the scope will fallback to full access as it was the case before v1.23 ... Gitea supports scoped access tokens, which allow users the ability to restrict tokens to operate only on selected url routes. Scopes are grouped by high-level API routes, and further refined to the following: ... Gitea token scopes are as follows: ... | Name | Description | | --- | --- | | (no scope) | Not supported. A scope is required even for public repositories. | | activitypub | `activitypub` API routes: ActivityPub related operations. | | read:activitypub | Grants read access for ActivityPub operations. | | write:activitypub | Grants read/write/delete access for ActivityPub operations. | ... | issue | `issues/*`, `labels/*`, `milestones/*` API routes: Issue-related operations. | | read:issue | Grants read access for issues operations, such as getting issue comments, issue attachments, and milestones. | | write:issue | Grants read/write/delete access for issues operations, such as posting or editing an issue comment or attachment, and updating milestones. | ... | repository | `/repos/*` API routes except `/repos/issues/*`: Repository file, pull-request, and release operations. | | read:repository | Grants read access to repository operations, such as getting repository files, releases, collaborators. | | write:repository | Grants read/write/delete access to repository operations, such as getting updating repository files, creating pull requests, updating collaborators. | <title>Merge a pull request | Gitea Documentation</title> https://docs.gitea.com/api/operations/repo-merge-pull-request/ Merge a pull request | Gitea Documentation # Merge a pull request POST /repos/{owner}/{repo}/pulls/{index}/merge const url = &`#39`; https://gitea.com/api/v1/repos/example/example/pulls/1/merge&`#39`;; const options = { method: &`#39`; POST&`#39`;, headers: {Authorization: &`#39`; Basic &`#39`;, &`#39`; Content-Type&`#39`;: &`#39`; application/json&`#39`;}, body: &`#39`;{"delete_branch_after_merge":true,"do":"merge","force_merge":true,"head_commit_id":"example","merge_commit_id":"example","merge_message_field":"example","merge_title_field":"example","merge_when_checks_succeed":true}&`#39`; }; try { const response = await fetch(url, options); const data = await response. json(); console. log(data); } catch (error) { console. error(error); } curl --request POST \ --url https://gitea.com/api/v1/repos/example/example/pulls/1/merge \ --header &`#39`; Authorization: Basic &`#39`; \ --header &`#39`; Content-Type: application/json&`#39`; \ --data &`#39`;{ "delete_branch_after_merge": true, "do": "merge", "force_merge": true, "head_commit_id": "example", "merge_commit_id": "example", "merge_message_field": "example", "merge_title_field": "example", "merge_when_checks_succeed": true }&`#39`; - gitea.com/api/v1/repos/{owner}/{repo}/pulls/{index}/merge ## Authorizations - BasicAuth - Token - AccessToken - AuthorizationHeaderToken - SudoParam - SudoHeader - TOTPHeader ## Parameters ### Path Parameters owner required string Owner of the repo repo required Name of the repo required integer format: int64 Index of the pull request to merge ## Request Body MergePullRequestForm form for merging Pull Request object delete_branch_after_merge boolean do required string Allowed values: merge rebase rebase-merge squash fast-forward-only manually-merged force_merge boolean head_commit_id string merge_commit_id string merge_message_field string merge_title_field string merge_when_checks_succeed boolean ## Responses ### 200 APIEmpty is an empty response ### 403 APIForbiddenError is a forbidden error response APINotFound is a not found empty response ### 405 APIEmpty is an empty response APIError is error format response ### 423 APIRepoArchivedError is an error that is raised when an archived repo should be modified <title>Merge a pull request | Gitea Documentation</title> https://docs.gitea.com/api/1.23/operations/repo-merge-pull-request/ Merge a pull request | Gitea Documentation # Merge a pull request POST /repos/{owner}/{repo}/pulls/{index}/merge const url = &`#39`; https://gitea.com/api/v1/repos/example/example/pulls/1/merge&`#39`;; const options = { method: &`#39`; POST&`#39`;, headers: {Authorization: &`#39`; Basic &`#39`;, &`#39`; Content-Type&`#39`;: &`#39`; application/json&`#39`;}, body: &`#39`;{"Do":"merge","MergeCommitID":"example","MergeMessageField":"example","MergeTitleField":"example","delete_branch_after_merge":true,"force_merge":true,"head_commit_id":"example","merge_when_checks_succeed":true}&`#39`; }; try { const response = await fetch(url, options); const data = await response. json(); console. log(data); } catch (error) { console. error(error); } curl --request POST \ --url https://gitea.com/api/v1/repos/example/example/pulls/1/merge \ --header &`#39`; Authorization: Basic &`#39`; \ --header &`#39`; Content-Type: application/json&`#39`; \ --data &`#39`;{ "Do": "merge", "MergeCommitID": "example", "MergeMessageField": "example", "MergeTitleField": "example", "delete_branch_after_merge": true, "force_merge": true, "head_commit_id": "example", "merge_when_checks_succeed": true }&`#39`; - gitea.com/api/v1/repos/{owner}/{repo}/pulls/{index}/merge ## Authorizations - BasicAuth - Token - AccessToken - AuthorizationHeaderToken - SudoParam - SudoHeader - TOTPHeader ## Parameters ### Path Parameters owner required string Owner of the repo repo required Name of the repo index required integer format: int64 Index of the pull request to merge ## Request Body MergePullRequestForm form for merging Pull Request object Do required string Allowed values: merge rebase rebase-merge squash fast-forward-only manually-merged MergeCommitID string MergeMessageField string MergeTitleField string delete_branch_after_merge boolean force_merge boolean head_commit_id string merge_when_checks_succeed boolean MergePullRequestForm form for merging Pull Request object Do required string Allowed values: merge rebase rebase-merge squash fast-forward-only manually-merged MergeCommitID string MergeMessageField string MergeTitleField string delete_branch_after_merge boolean force_merge boolean head_commit_id string merge_when_checks_succeed boolean ## Responses ### 200 APIEmpty is an empty response ### 404 APINotFound is a not found empty response ### 405 APIEmpty is an empty response APIError is error format response ### 423 APIRepoArchivedError is an error that is raised when an archived repo should be modified

Citations:


Require repository permissions for Gitea pull-request actions.

The PR workspace calls /repos/{owner}/{repo}/pulls/* for PR reads, review comments, and merges. Gitea assigns these routes to read:repository and write:repository, not the issue scopes. The current connection check only validates /user, so an issue-only token can be accepted and then fail in the PR workspace. Update the guidance to request read/write access to repositories as well as issues, or validate those permissions during connection.

Source: MCP tools

Comment on lines +121 to +126
giteaConnect: async (args) => {
const result = await window.api.gitea.connect(args)
if (result.ok) {
await get().refreshGiteaStatus()
}
return result.ok ? { ok: true } : { ok: false, error: result.error }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🛡️ Analyzed with Security Review | 🟠 Major | ⚡ Quick win

Sensitive Data Exposure

Reachability: External
Exploitability: Moderate
CWE: CWE-200 — Exposure of Sensitive Information to an Unauthorized Actor

Clear provider caches when credentials change.

The server ID is host-keyed, but these actions preserve giteaWorkItems and giteaIssueDetail. If the user reconnects to the same host with another account, cache hits can expose data loaded with the previous account for up to 60 seconds. Clear both caches after a successful connection and after disconnection. Alternatively, include an account or credential generation in each cache key.

Also applies to: 129-132

Comment on lines +215 to +221
if (result.ok) {
const detailKey = `${scopeKey(scope, get().giteaStatus?.selectedServerId)}#${issueNumber}`
set((state) => ({
giteaIssueDetail: Object.fromEntries(
Object.entries(state.giteaIssueDetail).filter(([key]) => key !== detailKey)
)
}))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Invalidate cached work-item lists after an issue update.

This branch removes only the detail entry. A title, label, assignee, or state update leaves every cached list unchanged. A closed issue can remain in the open list until the 60-second TTL expires. Remove list entries with the same scope prefix when the update succeeds.

@dpfurners

Copy link
Copy Markdown

+1 from a self-hosted Forgejo user.

Worth spelling out for anyone searching, since neither this PR's title nor #4703 says the word: this covers Forgejo too, because Forgejo speaks the Gitea API. #13149 ("[Feature] Forgejo integration") was closed as completed, but what actually landed there is the hosted-review/PR half under src/main/gitea/ — TaskProvider in src/shared/task-providers.ts is still github | gitlab | linear | jira, so there is no way to browse Forgejo issues or start a workspace from one.

That task-source half is the part I need: using issues as the entry point for automating work, not just attaching a PR to a workspace that already exists.

Since the task-source refactor @AmethystLiang was waiting on has landed and @beatkapo has rebuilt the branch on top of it, is there anything blocking a review pass now? Happy to test against a live Forgejo instance if that helps.

@k1n6b0b

k1n6b0b commented Sep 27, 2026

Copy link
Copy Markdown

+1, and happy to help test this on macOS, since the PR notes it hasn't been exercised there yet.

I run a self-hosted Gitea 1.22.6 with private repos and use Orca 1.4.212 on macOS. The current integration isn't usable for me: it reads the token only from ORCA_GITEA_TOKEN in the process environment, which a Dock-launched app on macOS doesn't inherit. So the Settings → Integrations token store in this PR matters to me as much as the task source.

Glad to run the branch against my instance and report back on: connecting a server in Settings, the Tasks list and detail for issues and PRs, starting a workspace from an issue, and the PR checks and merge flow.

Two questions:

  1. Does this rebuild include the two fixes from Gitea task source (#5493) rebased onto main + two validated fixes #6920 (the token not being applied for a Gitea server on a non-default port, and label edits dropping labels missing from the first page of repo labels)?
  2. Which token scopes should a tester grant? The current integration's /user check needs read:user (per [Feature]: Surface the actual Gitea API error when integration auth fails #13786), so I'd like to match whatever this PR expects.

@beatkapo beatkapo changed the title feat(gitea): add Gitea as a task source (issues + PR review) feat(tasks): add Gitea and Forgejo task source Sep 28, 2026
@M-Kerr

M-Kerr commented Oct 4, 2026

Copy link
Copy Markdown

Adding a voice from another self-hosted Forgejo user. Issues are the entry point for our agent workflow, and since Forgejo speaks the Gitea API this PR is the only route to using Orca's Tasks page with it; the PR-review half of the Gitea integration already works well for us. @brennanb2025 @AmethystLiang, now that the task-source refactor has landed and the branch has been rebuilt on it, could you say whether a review is on the horizon, or whether new sources are expected to come through the plugin route instead (#22174)? Either answer would help those of us planning around it. Thanks for the work on Orca.

@EdgerHao

Copy link
Copy Markdown
Contributor

Looking forward to the further promotion of this function, I hope to see Gitea in the task as soon as possible.

@alario-tang

Copy link
Copy Markdown

+1 — and a note from the adjacent Tencent-CN ecosystem: Tencent CNB (cnb.cool) would be a natural follow-on candidate for the same task-source shape this PR establishes (issues + PRs + pipeline status over OpenAPI).

We already run Orca worktrees against CNB remotes (git:cnb.cool/... projects) — the git side just works. If this lands as the template for third-party/self-hosted sources, a CNB adapter could follow the same layout (task-page/cnb/ beside gitea/), and we'd be glad to help test or contribute it.

Also volunteering as another macOS tester for this PR if that's still a gap.

@pnocera

pnocera commented Oct 19, 2026

Copy link
Copy Markdown

+1 from a private Forgejo user running Orca on linux

@MichaelMartinez

Copy link
Copy Markdown

Please consider merging this as another voice from the crowd running forgejo and vikunja.

@rene-biosolveit

Copy link
Copy Markdown

+1 from a self-hosted gitea user, running Orca on Windows

@EdgerHao

EdgerHao commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

+1 from a self-hosted Gitea user running Orca on macOS (v1.4.245).

Setup: Gitea 1.27.0 on a home NAS, reached over Tailscale, configured via ORCA_GITEA_TOKEN + ORCA_GITEA_API_BASE_URL. The current integration works well here — the Integrations badge shows connected, and attaching a Gitea PR to a workspace via its /pulls/N URL is smooth.

The gap this PR fills is the one I hit daily: repo issues/PRs aren't browsable in Tasks, so the Gitea web UI has to stay open in a browser tab next to Orca. Being able to start a workspace straight from a Gitea issue/PR — like the other providers — would complete the workflow.

Happy to test a build against my instance if that helps.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Gitea task sources