Skip to content

feat: add Gitea account authentication in integrations settings#12901

Open
javery556 wants to merge 3 commits intogitbutlerapp:masterfrom
javery556:codex/2904-gitea-auth
Open

feat: add Gitea account authentication in integrations settings#12901
javery556 wants to merge 3 commits intogitbutlerapp:masterfrom
javery556:codex/2904-gitea-auth

Conversation

@javery556
Copy link

Summary

Adds a small Gitea authentication/settings slice to GitButler.

This PR lets users store a personal access token for Codeberg or another Gitea-compatible host, fetch the authenticated account, list saved Gitea accounts, and remove individual or all stored accounts from Settings.

Related to #2904.

What’s included

  • add a new but-gitea crate for Gitea auth requests and account token persistence
  • add backend commands for:
    • store_gitea_account
    • get_gitea_user
    • forget_gitea_account
    • list_known_gitea_accounts
    • clear_all_gitea_tokens
  • extend forge storage with persisted Gitea account metadata
  • add desktop-side Gitea account service wiring
  • add a Settings integration card for adding and removing Gitea accounts
  • add matching account-management routes in but-server

What’s not included

This PR does not add:

  • Gitea repo or remote detection
  • Gitea PR or review flows
  • publish, open, or merge support

The goal here is just the auth and account-management foundation as a small focused slice.

Validation

Repo checks:

  • cargo test -p but-gitea -p but-forge-storage
  • cargo test -p but-server -p but-api -p gitbutler-tauri --no-run
  • npx --yes pnpm@10.20.0 --filter @gitbutler/core check
  • npx --yes pnpm@10.20.0 --filter @gitbutler/desktop check
  • npx --yes pnpm@10.20.0 --filter @gitbutler/desktop exec vitest run --mode development src/lib/forge/gitea/giteaUserService.test.ts

Real Gitea smoke test:

  • ran a local Gitea 1.25.5 instance
  • created a real test user and PAT
  • verified /api/v1/user against the live instance
  • exercised the Rust path end-to-end with store_account, list_known_gitea_accounts, get_gitea_user, and forget_gitea_access_token

Notes

I kept this intentionally narrow so it can be reviewed independently from broader Gitea forge support work.

@vercel
Copy link

vercel bot commented Mar 18, 2026

@javery556 is attempting to deploy a commit to the GitButler Team on Vercel.

A member of the Team first needs to authorize it.

@Byron
Copy link
Collaborator

Byron commented Mar 18, 2026

Thanks for giving it a shot.

I think @estib-vega should decide if reviewing this makes sense for him. A cursory look shows new crates without any docs, so I don't think it's there just yet and needs quite some work.

In any case, could @javery556 provide demo videos that show all the functionality?

@Byron Byron added the feedback requested Feedback was requested to help resolve the issue label Mar 18, 2026
@javery556
Copy link
Author

Follow-up is pushed in 53e774bab.

I addressed the reviewability gaps Byron called out:

  • added crate-level docs and a crate README for crates/but-gitea
  • wired the missing but-server async routes for store_gitea_account and get_gitea_user, so the web/browser-backed path exercises the same auth flow
  • regenerated/formatted the related generated TS output and UI files

I also re-tested this against a real local Gitea 1.25.5 instance rather than synthetic-only data:

  • stored a PAT against a live local Gitea host
  • verified list_known_gitea_accounts, get_gitea_user, forget_gitea_account, and clear_all_gitea_tokens
  • drove the desktop web build through Settings -> Integrations -> Gitea add/forget flow

Window-only demo video:

@Byron Byron requested a review from Copilot March 19, 2026 03:01
@Byron
Copy link
Collaborator

Byron commented Mar 19, 2026

Thanks a lot!

Let's see what Esteban thinks when he gets to it.

@codex review

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds first-pass Gitea (e.g., Codeberg) authentication/account management support across the Rust backend, server/tauri command surfaces, and the Desktop Settings UI, including token persistence via OS keychain and forge-storage metadata.

Changes:

  • Introduces but-gitea crate for validating PATs, fetching /api/v1/user, and persisting account/token data.
  • Wires new Gitea commands into but-api, but-server routes, and Tauri command registration; extends but-forge-storage settings/controller for Gitea accounts.
  • Adds Desktop-side Gitea user service + Settings integration components, plus generated TS types.

Reviewed changes

Copilot reviewed 21 out of 25 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/core/src/generated/index.ts Re-exports generated Gitea API/token types for frontend consumption.
packages/core/src/generated/gitea/token.ts Adds generated GiteaAccountIdentifier TS type.
packages/core/src/generated/gitea/index.ts Adds generated sensitive response/user TS types for Gitea auth flows.
crates/gitbutler-tauri/src/main.rs Registers new Tauri commands for Gitea account management.
crates/but-server/src/lib.rs Adds but-server HTTP routes for Gitea account management commands.
crates/but-gitea/src/token.rs Implements Gitea account identifier + token persistence/removal/listing.
crates/but-gitea/src/lib.rs Exposes high-level Gitea auth/account-management API + JSON transport types.
crates/but-gitea/src/client.rs Adds minimal reqwest-based Gitea client for /api/v1/user.
crates/but-gitea/README.md Documents scope and manual verification steps for the new crate.
crates/but-gitea/Cargo.toml Defines new crate, features (ts/schema export), and deps.
crates/but-forge-storage/src/settings.rs Extends forge settings with gitea settings + persisted account metadata.
crates/but-forge-storage/src/controller.rs Adds controller CRUD helpers for Gitea known accounts.
crates/but-api/src/lib.rs Exposes new but_api::gitea module.
crates/but-api/src/gitea.rs Implements API entrypoints/commands for storing/listing/forgetting/clearing Gitea accounts and fetching user.
crates/but-api/Cargo.toml Adds but-gitea dependency.
apps/desktop/src/lib/state/tags.ts Adds ReduxTag.GiteaUserList for RTK Query caching.
apps/desktop/src/lib/forge/gitea/giteaUserService.test.ts Adds helper tests for identifier (de)serialization/comparison.
apps/desktop/src/lib/forge/gitea/giteaUserService.svelte.ts Adds RTK Query endpoint wiring + DI service wrapper for Gitea.
apps/desktop/src/lib/bootstrap/deps.ts Registers GiteaUserService in dependency injection bootstrap.
apps/desktop/src/components/profileSettings/IntegrationsSettings.svelte Adds the Gitea integration card to Settings.
apps/desktop/src/components/GiteaUserLoginState.svelte Renders per-account login state with forget action + user fetch.
apps/desktop/src/components/GiteaIntegration.svelte Implements Settings UI flow for adding/removing/clearing Gitea accounts.
apps/desktop/src/components/GiteaAccountBadge.svelte Adds badge/tooltip for displaying the normalized Gitea instance host.
Cargo.toml Adds crates/but-gitea to workspace members and workspace deps.
Cargo.lock Adds lockfile entries for the new crate and dependency edges.

You can also share your feedback on Copilot code review. Take the survey.

Copy link
Contributor

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 53e774babf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@javery556
Copy link
Author

Pushed a follow-up in a08386e22 addressing the review comments:

  • changed the Gitea load-error "Try again" action to refetch instead of clearing accounts
  • fixed Gitea token persistence order so failed keychain writes do not leave phantom accounts
  • fixed Gitea deletion to remove stored metadata even if the secret is missing
  • updated the stale workspace comment for but-gitea

Re-verified with:

  • cargo test -p but-gitea -p but-forge-storage
  • cargo test -p but-server -p but-api -p gitbutler-tauri --no-run
  • real local Gitea smoke test on a fresh but-server
  • real browser flow through Settings -> Integrations -> Gitea add/forget

@Byron Byron requested a review from Copilot March 19, 2026 04:37
@Byron
Copy link
Collaborator

Byron commented Mar 19, 2026

@codex review

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a first-pass Gitea authentication + account-management integration across the Rust backend, server/Tauri command surfaces, and the desktop Settings UI.

Changes:

  • Introduces but-gitea for validating PATs against a Gitea host, persisting account metadata, and storing tokens in secure storage.
  • Wires new Gitea commands into but-api, but-server, and the Tauri invoke handler.
  • Adds a desktop Settings integration card + service layer to store/list/forget Gitea accounts.

Reviewed changes

Copilot reviewed 21 out of 25 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/core/src/generated/index.ts Exports generated Gitea SDK types from @gitbutler/core/api.
packages/core/src/generated/gitea/token.ts Adds generated TS type for GiteaAccountIdentifier.
packages/core/src/generated/gitea/index.ts Adds generated TS response types exposing tokens for UI handoff.
crates/gitbutler-tauri/src/main.rs Registers new Gitea Tauri commands.
crates/but-server/src/lib.rs Adds HTTP routes for Gitea account/token management commands.
crates/but-gitea/src/token.rs Implements persisted account identifiers + secure token storage helpers.
crates/but-gitea/src/lib.rs Provides the high-level Gitea auth/account API used by but-api.
crates/but-gitea/src/client.rs Implements minimal Gitea /api/v1/user client for auth validation/user fetch.
crates/but-gitea/README.md Documents scope, layout, and manual verification steps for but-gitea.
crates/but-gitea/Cargo.toml Adds new crate manifest + feature flags (ts/schema export).
crates/but-forge-storage/src/settings.rs Extends forge settings schema with Gitea settings/accounts.
crates/but-forge-storage/src/controller.rs Adds CRUD helpers for Gitea accounts in forge settings storage.
crates/but-api/src/lib.rs Exposes new gitea API module.
crates/but-api/src/gitea.rs Adds Gitea API commands for store/get/list/forget/clear.
crates/but-api/Cargo.toml Adds but-gitea dependency.
apps/desktop/src/lib/state/tags.ts Adds ReduxTag.GiteaUserList for cache tagging.
apps/desktop/src/lib/forge/gitea/giteaUserService.test.ts Adds unit tests for Gitea account identifier helpers.
apps/desktop/src/lib/forge/gitea/giteaUserService.svelte.ts Adds RTK Query endpoints + service wrapper for Gitea auth/account management.
apps/desktop/src/lib/bootstrap/deps.ts Registers GiteaUserService in dependency injection.
apps/desktop/src/components/profileSettings/IntegrationsSettings.svelte Adds the Gitea integration card to Settings.
apps/desktop/src/components/GiteaUserLoginState.svelte Renders per-account user state and forget action.
apps/desktop/src/components/GiteaIntegration.svelte Implements Settings UI flow to add/list Gitea accounts.
apps/desktop/src/components/GiteaAccountBadge.svelte Adds host badge rendering/normalization for Gitea accounts.
Cargo.toml Adds but-gitea to workspace members and workspace dependencies.
Cargo.lock Locks new crate and its dependency graph.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +150 to +158
let _one_at_a_time_to_prevent_races = FAIR_QUEUE.lock().unwrap();
secret::persist(
&secret_key,
&account.access_token(),
secret::Namespace::BuildKind,
)?;

if let Err(err) = storage.add_gitea_account(&account.into()) {
let _ = secret::delete(&secret_key, secret::Namespace::BuildKind);
Comment on lines +75 to +88
impl GiteaAccountIdentifier {
/// Build an identifier from the authenticated username and host.
pub fn new(username: &str, host: &str) -> Self {
Self {
username: username.to_string(),
host: host.to_string(),
}
}

/// Recreate a client for this stored account.
pub fn client(&self, access_token: &Sensitive<String>) -> Result<GiteaClient> {
crate::client::client_for(self, access_token)
}
}
Comment on lines +126 to +130
if settings.gitea.known_accounts.iter().any(|a| a == account) {
return Ok(());
}

settings.gitea.known_accounts.push(account.to_owned());
Copy link
Contributor

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a08386e220

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +58 to +60
token::persist_gitea_access_token(
&token::GiteaAccountIdentifier::new(&user.username, host),
access_token,
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 Badge Canonicalize Gitea hosts before persisting account IDs

normalize_api_base_url() already treats https://codeberg.org, https://codeberg.org/, and https://codeberg.org/api/v1 as the same API root, but this path stores the raw textbox value in the account identifier. As a result, the same Gitea account can be added multiple times under equivalent URLs, and the settings UI then renders those duplicates with the same normalized badge text while keeping separate stored secrets.

Useful? React with 👍 / 👎.

Comment on lines +113 to +114
query: (args) => args,
invalidatesTags: [providesList(ReduxTag.GiteaUserList)],
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 Badge Invalidate cached Gitea user queries after storing a token

This mutation only invalidates GiteaUserList, even though getGiteaUser caches each account separately. Because RTK Query serves existing cached query results on remount by default, saving the same host+username again (for example after rotating a PAT, or re-adding an account you just removed) can leave the card showing a stale success/error state instead of fetching /get_gitea_user with the new credentials until the cache expires or the app restarts.

Useful? React with 👍 / 👎.

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

Labels

feedback requested Feedback was requested to help resolve the issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants