Repository navigation
Conversation
|
Ready to review this PR? Stage has broken it down into 12 individual chapters for you: Chapters generated by Stage for commit 84a2410 on Jun 19, 2026 7:39am UTC. |
|
Understand this PR’s impact Explore downstream dependencies and potential security impact with Blast Radius. Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis 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 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)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation 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.)
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. Comment |
There was a problem hiding this comment.
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 winButton 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 winTranslate newly added Gitea locale strings to Spanish.
Several new
es.jsonentries 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 winTranslate 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 winClear the opposite Gitea drawer before opening a new one.
selectedGiteaItemandselectedGiteaPrare 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 winLocalize the invalid-date fallback text.
The fallback string
'recently'is hardcoded while the rest of the component is localized. Route this value throughtranslate(...)for locale consistency.src/renderer/src/components/gitea-pr-file-diff.tsx-25-32 (1)
25-32:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLocalize file status labels.
STATUS_LABELSis user-facing and currently hardcoded in English. Move labels behindtranslate(...)to match the rest of the Gitea UI localization.src/main/ipc/gitea-pr.ts-64-66 (1)
64-66:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTighten 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 | 🟡 MinorUpdate
selectServerto useGiteaServerSelectiontype for type consistency and correctness.The preload API currently declares
selectServerwith plainstring, but all actual implementations (IPC handler insrc/main/ipc/gitea.ts, main handler insrc/main/gitea/connect.ts, and renderer slice insrc/renderer/src/store/slices/gitea.ts) use theGiteaServerSelectiontype. Additionally,GiteaServerSelectionis defined asstring | '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 winReplace 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 winUse style-guide tokens instead of arbitrary utility values for typography/layering.
text-[11px],z-[110], andz-[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 valueSimplify: 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 winPre-group review comments before rendering file diffs.
Filtering
reviewCommentsinside eachfiles.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
📒 Files selected for processing (62)
src/main/gitea/client.tssrc/main/gitea/connect.test.tssrc/main/gitea/connect.tssrc/main/gitea/issues.test.tssrc/main/gitea/issues.tssrc/main/gitea/mappers.test.tssrc/main/gitea/mappers.tssrc/main/gitea/pull-requests.test.tssrc/main/gitea/pull-requests.tssrc/main/gitea/request.tssrc/main/gitea/server-store.test.tssrc/main/gitea/server-store.tssrc/main/ipc/gitea-pr.tssrc/main/ipc/gitea-repo-access.tssrc/main/ipc/gitea.tssrc/main/ipc/register-core-handlers.tssrc/preload/api-types.tssrc/preload/index.tssrc/renderer/src/components/GiteaIssueWorkspace.tsxsrc/renderer/src/components/GiteaPullRequestWorkspace.tsxsrc/renderer/src/components/GiteaTaskList.tsxsrc/renderer/src/components/TaskPage.tsxsrc/renderer/src/components/automations/automation-source-display.tssrc/renderer/src/components/automations/automation-target-availability.tssrc/renderer/src/components/gitea-connect-dialog.tsxsrc/renderer/src/components/gitea-issue-comments.tsxsrc/renderer/src/components/gitea-issue-meta-controls.tsxsrc/renderer/src/components/gitea-new-issue-dialog.tsxsrc/renderer/src/components/gitea-pr-checks.tsxsrc/renderer/src/components/gitea-pr-file-diff.tsxsrc/renderer/src/components/gitea-pr-line-comments.tsxsrc/renderer/src/components/gitea-pr-merge-button.tsxsrc/renderer/src/components/gitea-pr-tab-body.tsxsrc/renderer/src/components/icons/GiteaIcon.tsxsrc/renderer/src/components/settings/IntegrationsPane.tsxsrc/renderer/src/components/settings/TasksPane.tsxsrc/renderer/src/components/settings/gitea-integration-card.test.tsxsrc/renderer/src/components/settings/gitea-integration-card.tsxsrc/renderer/src/components/settings/task-tracker-integration-cards.tsxsrc/renderer/src/components/settings/use-integration-provider-status-refresh.tssrc/renderer/src/components/task-page-list-chrome-visibility.tssrc/renderer/src/components/task-page-localized-options.tsxsrc/renderer/src/components/task-source-context-summary-format.tssrc/renderer/src/components/task-source-context-summary.tssrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh.jsonsrc/renderer/src/store/index.tssrc/renderer/src/store/slices/diffComments.test.tssrc/renderer/src/store/slices/gitea.test.tssrc/renderer/src/store/slices/gitea.tssrc/renderer/src/store/slices/store-test-helpers.tssrc/renderer/src/store/slices/ui.tssrc/renderer/src/store/types.tssrc/shared/gitea-types.tssrc/shared/integration-credential-errors.tssrc/shared/task-providers.test.tssrc/shared/task-providers.tssrc/shared/task-source-context.tssrc/shared/types.ts
| "d8a6f1c3fe": "Gitea", | ||
| "a054078ab7": "Show Gitea in the Tasks source picker and sidebar shortcuts." |
There was a problem hiding this comment.
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
| "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" | ||
| } | ||
| } |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 winGuard every async continuation with the current selection request.
The initial load ignores stale responses, but closing the sheet does not advance
requestRef, andrefreshDetail, 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 winAdd duplicate-registration guard coverage for Gitea as well.
Line 396 validates initial registration, but the "only registers once" test should also assert
registerGiteaHandlersMockis 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
📒 Files selected for processing (23)
src/main/gitea/client.test.tssrc/main/gitea/connect.tssrc/main/gitea/issues.tssrc/main/gitea/request.tssrc/main/ipc/register-core-handlers.test.tssrc/main/persistence.test.tssrc/renderer/src/assets/main.csssrc/renderer/src/components/GiteaIssueWorkspace.tsxsrc/renderer/src/components/GiteaPullRequestWorkspace.tsxsrc/renderer/src/components/GiteaTaskList.tsxsrc/renderer/src/components/TaskPage.tsxsrc/renderer/src/components/gitea-new-issue-dialog.tsxsrc/renderer/src/components/gitea-pr-checks.tsxsrc/renderer/src/components/gitea-pr-file-diff.tsxsrc/renderer/src/components/gitea-pr-merge-button.tsxsrc/renderer/src/components/settings/gitea-integration-card.tsxsrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh.jsonsrc/renderer/src/store/slices/gitea.test.tssrc/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
bbc8a8c to
84a2410
Compare
|
Rebased onto the latest
Note: Verified locally: |
|
thanks for the PR! we're doing some refactoring work to add more task sources. and will land this after the refactoring |
|
Do you have an estimate for when we can expect an update once the refactoring is complete? |
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>
84a2410 to
7ed9b13
Compare
|
@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. Verified: 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:
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. |
There was a problem hiding this comment.
Actionable comments posted: 12
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: db2f286c-0ee6-4491-aca0-f5d1d0e85666
📒 Files selected for processing (60)
src/main/gitea/connect.tssrc/main/gitea/request.tssrc/main/gitea/server-store.tssrc/main/global-fetch-call-site-audit.test.tssrc/main/ipc/gitea-pr.tssrc/main/ipc/gitea-repo-access.tssrc/main/ipc/gitea.tssrc/main/ipc/register-core-handlers/register-core-handlers.test.tssrc/main/ipc/register-core-handlers/register-core-handlers.tssrc/main/persistence-settings-ui-defaults.test.tssrc/main/persistence-source-control-ai-migration.test.tssrc/preload/api-types.tssrc/preload/api/gitea-api.tssrc/preload/api/gitea-bridge.tssrc/preload/index.tssrc/renderer/src/components/GiteaIssueWorkspace.tsxsrc/renderer/src/components/GiteaPullRequestWorkspace.tsxsrc/renderer/src/components/GiteaTaskList.tsxsrc/renderer/src/components/automations/automation-source-display.tssrc/renderer/src/components/automations/automation-target-availability.tssrc/renderer/src/components/gitea-issue-comments.tsxsrc/renderer/src/components/gitea-issue-meta-controls.tsxsrc/renderer/src/components/gitea-pr-checks.tsxsrc/renderer/src/components/gitea-pr-file-diff.tsxsrc/renderer/src/components/gitea-pr-line-comments.tsxsrc/renderer/src/components/gitea-pr-merge-button.tsxsrc/renderer/src/components/gitea-pr-tab-body.tsxsrc/renderer/src/components/right-sidebar/checks-panel-gitea.test.tssrc/renderer/src/components/right-sidebar/checks-panel-gitea.tssrc/renderer/src/components/settings/IntegrationsPane.tsxsrc/renderer/src/components/settings/TaskSourceSimpleSetup.tsxsrc/renderer/src/components/settings/TasksPane.test.tsxsrc/renderer/src/components/settings/TasksPane.tsxsrc/renderer/src/components/settings/gitea-integration-card.test.tsxsrc/renderer/src/components/settings/gitea-integration-card.tsxsrc/renderer/src/components/settings/task-provider-integration-section-ids.tssrc/renderer/src/components/settings/task-source-setup-state.test.tssrc/renderer/src/components/settings/tasks-search.tssrc/renderer/src/components/settings/use-task-source-provider-readiness.tssrc/renderer/src/components/task-page-list-chrome-visibility.tssrc/renderer/src/components/task-page-localized-options.tsxsrc/renderer/src/components/task-page/Content.tsxsrc/renderer/src/components/task-page/gitea/Content.tsxsrc/renderer/src/components/task-provider-identity-label.tssrc/renderer/src/components/task-source-context-summary.tssrc/renderer/src/hooks/composer-state/derived-model.tssrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/store/index.tssrc/renderer/src/store/slices/gitea.test.tssrc/renderer/src/store/slices/gitea.tssrc/renderer/src/store/slices/store-test-helpers.tssrc/renderer/src/store/slices/ui/ui-slice-contract-core.tssrc/renderer/src/store/types.tssrc/shared/gitea-types.tssrc/shared/integration-credential-errors.tssrc/shared/task-provider-identity.tssrc/shared/task-providers.test.tssrc/shared/task-providers.tssrc/shared/workspace-name.tssrc/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.
| 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)) |
There was a problem hiding this comment.
🎯 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.
| export function normalizeGiteaApiBaseUrl(value: string): string { | ||
| const trimmed = value.trim().replace(/\/+$/, '') | ||
| return /\/api\/v1$/i.test(trimmed) ? trimmed : `${trimmed}/api/v1` | ||
| } |
There was a problem hiding this comment.
🔒 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 beforefetchGiteaUsersends 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-L108src/main/gitea/request.ts#L102-L104src/main/gitea/request.ts#L151-L153src/main/gitea/request.ts#L196-L204
| const STATUS_LABELS: Record<GiteaPRFile['status'], string> = { | ||
| added: 'Added', | ||
| modified: 'Modified', | ||
| deleted: 'Deleted', | ||
| renamed: 'Renamed', | ||
| copied: 'Copied', | ||
| changed: 'Changed' | ||
| } |
There was a problem hiding this comment.
🎯 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/nullRepository: 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 -240Repository: 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.tsxRepository: 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 -160Repository: 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) |
There was a problem hiding this comment.
🎯 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.
| const lineNumber = Number.parseInt(line, 10) | |
| const lineNumber = Number(line) |
| setComments((prev) => [ | ||
| ...prev, | ||
| { id: Date.now(), body, createdAt: new Date().toISOString() } | ||
| ]) |
There was a problem hiding this comment.
🎯 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(() => {}) |
There was a problem hiding this comment.
🩺 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.tsxRepository: 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.tsxRepository: 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.
| "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.", |
There was a problem hiding this comment.
🎯 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
| "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.", |
There was a problem hiding this comment.
🗄️ 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.jsonRepository: 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.jsonRepository: 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.tsRepository: 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 -200Repository: 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>
Citations:
- 1: https://bollardai.com/resources/gitea
- 2: https://docs.gitea.com/1.26/development/oauth2-provider/
- 3: https://docs.gitea.com/development/oauth2-provider/
- 4: https://docs.gitea.com/api/1.23/operations/repo-merge-pull-request/
- 5: https://go-gitea-gitea.mintlify.app/api/pull-requests
- 6: https://docs.gitea.com/api/
- 7: https://docs.gitea.com/usage/access-control/permissions/
🏁 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.tsRepository: 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>
Citations:
- 1: https://docs.gitea.com/development/oauth2-provider/
- 2: https://docs.gitea.com/1.26/development/oauth2-provider/
- 3: https://docs.gitea.com/next/development/oauth2-provider/
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
| 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 } |
There was a problem hiding this comment.
🔒 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
| if (result.ok) { | ||
| const detailKey = `${scopeKey(scope, get().giteaStatus?.selectedServerId)}#${issueNumber}` | ||
| set((state) => ({ | ||
| giteaIssueDetail: Object.fromEntries( | ||
| Object.entries(state.giteaIssueDetail).filter(([key]) => key !== detailKey) | ||
| ) | ||
| })) |
There was a problem hiding this comment.
🎯 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.
|
+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 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. |
|
+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 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:
|
|
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. |
|
Looking forward to the further promotion of this function, I hope to see Gitea in the task as soon as possible. |
|
+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 ( Also volunteering as another macOS tester for this PR if that's still a gap. |
|
+1 from a private Forgejo user running Orca on linux |
|
Please consider merging this as another voice from the crowd running forgejo and vikunja. |
|
+1 from a self-hosted gitea user, running Orca on Windows |
|
+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 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. |
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.
Testing
Verified on Windows. No live Gitea/Forgejo server, macOS, Linux, or SSH end-to-end test is claimed.
Intl.NumberFormatdefault matching their expectations.tests/e2e/gitea-tasks.spec.ts).pnpm lintstops on five existing import-cycle warnings in unchangedmobile/src/transportandmobile/src/terminal/documentfiles. 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
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
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.