# How to Hack on GitButler Alrighty, you want to get compiling. We love you already. Your parents raised you right. Let's get started. --- ## Table of Contents - [Overview](#overview) - [CLI-only development](#cli-only-development) - [The Basics](#the-basics) - [Prerequisites](#prerequisites) - [Install JavaScript dependencies](#install-javascript-dependencies) - [Run the app](#run-the-app) - [Lint & format](#lint--format) - [Debugging](#debugging) - [Logs](#logs) - [Repository](#Repository) - [Tokio](#tokio) - [Troubleshooting](#troubleshooting) - [Building](#building) - [Building on Windows](#building-on-windows) - [Perl](#perl) - [Crosscompilation](#crosscompilation) - [Design](#design) - [Contributing](#contributing) - [Some Other Random Notes](#some-other-random-notes) - [Icon generation](#icon-generation) - [Release](#release) - [Versioning](#versioning) - [Publishing](#publishing) - [Development mode OAuth login](#development-mode-oauth-login) - [Joining the GitButler Team](#joining-the-gitbutler-team) --- ## Overview So how does this whole thing work? It's a [Tauri app](https://tauri.app/), which is basically like an Electron app, in that we can develop a desktop app from one source with multiple OS targets and write the UI in HTML and Javascript. Except instead of Node for the filesystem access part, Tauri uses [Rust](https://www.rust-lang.org/). So everything that hits disk is in Rust, everything that the user sees is in HTML/JS. Specifically we use [Svelte](https://svelte.dev/) in Typescript for that layer. For a deep dive into the architecture, see [DEEPWIKI](https://deepwiki.com/gitbutlerapp/gitbutler). --- ## CLI-only development The easiest way to get started is by hacking on the `but` CLI. You only need Rust installed…[^cli-build-prereqs] ```bash $ cd gitbutler-repo $ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh ``` …to build and run the CLI: ```bash $ cargo build -p but $ cargo run -p but -- --help $ cargo run -p but -- -C /path/to/git-repo status ``` To test only the CLI: ```bash $ cargo test -p but ``` Useful locations in the source tree: - `crates/but/src/args` for CLI argument parsing and help text - `crates/but/src/command` for command implementations - `crates/but/tests` for integration tests [^cli-build-prereqs]: In practice, `cargo build -p but` also builds native dependencies such as `git2` with vendored `openssl` and `libgit2`, so you still need a working C toolchain. On Linux, that commonly means tools such as `build-essential`, `make`, `perl`, `cmake`, and `pkg-config`; on macOS, install Xcode Command Line Tools. On Windows, you’ll need a working MSVC-based toolchain and related native dependencies; see [Building on Windows](#building-on-windows) below for details (including `perl` for `openssl-sys`). If you already completed the desktop prerequisites in [The Basics](#the-basics), you already have the stricter setup. If you already completed the desktop prerequisites in [The Basics](#the-basics) below, you already have the stricter setup. ## The Basics OK, let's get it running. ### Prerequisites First of all, this is a Tauri app, which uses Rust for the backend and Javascript for the frontend. So let's make sure you have all the prerequisites installed. 1. Tauri Dev Deps (https://tauri.app/start/prerequisites/#system-dependencies) On Mac OS, ensure you've installed XCode and `cmake`. On Linux, if you're on Debian or one of its derivatives like Ubuntu, you can use the following command.
Linux Tauri dependencies ```bash $ sudo apt update $ sudo apt install libwebkit2gtk-4.1-dev \ build-essential \ curl \ wget \ file \ libxdo-dev \ libssl-dev \ libayatana-appindicator3-dev \ librsvg2-dev \ cmake ```
2. Rust For both Mac OS and Linux, you can use the following `rustup` quick install script to get all the necessary tools. ```bash $ cd gitbutler-repo $ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh ``` 3. pnpm Install standalone pnpm using the [pnpm installation instructions](https://pnpm.io/installation#using-a-standalone-script). It provides Node.js. ### Install JavaScript dependencies From the repository root, install the JavaScript dependencies. This also installs the project's Node.js runtime: ```bash $ pnpm install ``` Re-run it when dependencies change. Run project commands through `pnpm` scripts or `pnpm exec` to use that runtime. > [!NOTE] > We use [turborepo](https://turbo.build/repo) as our monorepo tooling and by default Vercel collects some [basic telemetry](https://turbo.build/repo/docs/telemetry). If you'd like to disable this, please run `pnpm exec turbo telemetry disable` once in the project's root directory after installing dependencies. ### Run the app First, run cargo build such that supplementary bins such as `gitbutler-git-askpass` are created (Note the default folder (`target`) will be used for the build): ```bash $ cargo build ``` Now you should be able to run the app in development mode: ```bash $ pnpm dev:desktop ``` By default it will not print debug logs to console. If you want debug logs, set `LOG_LEVEL` environment variable: ```bash $ LOG_LEVEL=debug pnpm dev:desktop ``` ### Lint & format In order to have a PR accepted, you need to make sure everything passes our Linters, so make sure to run these before submitting. Our CI will shame you if you don't. Javascript: ```bash $ pnpm lint $ pnpm format ``` Rust: ```bash $ cargo clippy # see linting errors $ cargo fmt # format code ``` --- ## Debugging Now that you have the app running, here are some hints for debugging whatever it is that you're working on. ### Logs The app writes logs into: 1. `stdout` in development mode 2. The Tauri [logs](https://tauri.app/v1/api/js/path/#platform-specific) directory One can get performance log when launching the application locally as follows: ```bash GITBUTLER_PERFORMANCE_LOG=1 LOG_LEVEL=debug pnpm tauri dev ``` For more realistic performance logging, use local release builds with `--release`. ```bash GITBUTLER_PERFORMANCE_LOG=1 LOG_LEVEL=debug pnpm tauri dev --release ``` ### Repository Often the behaviour depends on the current context: the repository being displayed. Next to _logs_, it's useful to learn how it's structured. To do that, _launch the application from a terminal_ as shown in the paragraph above this one, or right below, but with `graphviz` installed. The `dot` program should be available in `PATH` so it can run from the terminal. Doing so can look like this on macOS, for example: ###### For the stable build ```shell GITBUTLER_PERFORMANCE_LOG=1 /Applications/GitButler/Contents/MacOS/gitbutler-tauri ``` ###### For the nightly build ```shell GITBUTLER_PERFORMANCE_LOG=1 /Applications/GitButler\ Nightly.app/Contents/MacOS/gitbutler-tauri ``` ###### Producing a commit-graph visualization Then, within the application, type `dot` in any non-editable portion of the GUI, like the cheat code that it is. This will pop open an SVG version of the graph that GitButler uses to create the workspace projection. ### Tokio We are also collecting tokio's runtime tracing information that could be viewed using [tokio-console](https://github.com/tokio-rs/console#tokio-console-prototypes): - development: ```bash $ tokio-console ``` - nightly: ```bash $ tokio-console http://127.0.0.1:6668 ``` - production: ```bash $ tokio-console http://127.0.0.1:6667 ``` --- ## Troubleshooting Common issues and solutions when developing GitButler. ### Turbo/build issues #### Case-sensitive volume problems If you're experiencing issues with the `dev:desktop` target failing to start, especially on macOS with case-sensitive filesystems, this may be related to Turborepo's handling of case-sensitive volumes. **Solution:** See the related issue at [vercel/turborepo#8491](https://github.com/vercel/turborepo/issues/8491) for current workarounds. #### Turbo daemon issues If builds are hanging or behaving unexpectedly: ```bash # Stop the turbo daemon pnpm exec turbo daemon stop # Clear turbo cache pnpm exec turbo daemon clean # Restart development pnpm dev:desktop ``` ### Cache issues If you're seeing stale builds or unexpected behavior: ```bash rm -rf .turbo node_modules pnpm install # Optional (Rust artifacts): cargo clean ``` ### Additional resources For issues specific to our toolchain components: - [Turborepo issues](https://github.com/vercel/turborepo/issues) - [Tauri issues](https://github.com/tauri-apps/tauri/issues) If none of these solutions work, please check our [GitHub Issues](https://github.com/gitbutlerapp/gitbutler/issues) or create a new issue with detailed information about your system and the error you're encountering. --- ## Building To build the app in production mode, run: ```bash $ pnpm tauri build --features devtools,builtin-but,disable-auto-updates,nightly --config crates/gitbutler-tauri/tauri.conf.nightly-local.json ``` This will make an asset similar to our nightly build. ### Building on Windows Building on Windows is a bit of a tricky process. Here are some helpful tips. #### Perl A Perl interpreter is required to be installed in order to configure the `openssl-sys` crate. We've used [Strawberry Perl](https://strawberryperl.com/) without issue. Make sure it's installed and `perl` is available on the `PATH` (it is by default after installation, just make sure to restart the terminal after installing). [Scoop](https://scoop.sh/) users can install this via `scoop install perl`. Note that it might appear that the build has hung or frozen on the `openssl-sys` crate. It's not, it's just that Cargo can't report the status of a C/C++ build happening under the hood, and openssl is _large_. It'll take a while to compile. #### OpenSSL To build it, one needs to export the path to the perl installation to use. The line below will do the trick in a git-bash. ```bash export OPENSSL_SRC_PERL="c:/Strawberry/perl/bin/perl.exe" ``` To make this change permanent, one would one can add this change (and others) to the `~/.bash_profile`. ```bash echo 'export OPENSSL_SRC_PERL="c:/Strawberry/perl/bin/perl.exe"' >> ~/.bash_profile ``` #### Crosscompilation This paragraph is about crosscompilation to x86_64-MSVC from ARM Windows, a configuration typical for people with Apple Silicon and Parallels VMs, which only allow ARM Windows to be used. The `windows` dependency on `gitbutler-git` doesn't currently compile on ARM, which means cross-compilation to x86-64 is required to workaround that. Besides, most users will probably still be on INTEL machines, making this capability a common requirement. In a Git `bash`, _with MSVC for x86-64 installed on the system_, run the following to prepare the environment. ```bash export TRIPLE_OVERRIDE=x86_64-pc-windows-msvc export CARGO_BUILD_TARGET=x86_64-pc-windows-msvc # for good measure export OPENSSL_SRC_PERL="c:/Strawberry/perl/bin/perl.exe" ``` Here is how to produce a nightly release build: ``` pnpm tauri build --features windows,devtools,nightly --config crates/gitbutler-tauri/tauri.conf.nightly.json ``` And this is how to get a local developer debug build: ```bash pnpm tauri dev --features windows --target x86_64-pc-windows-msvc ``` Note that it's necessary to repeat the `--target` specification as otherwise the final copy operation doesn't work, triggered by `tauri` itself. --- ## Design We use [Figma](https://www.figma.com/) for our design work. If you're a designer (and even if you're not), you want to contribute to the design of GitButler, or your work involves UI, you could duplicate our design file. GitButler design: [Figma file](https://www.figma.com/file/FbeLt0yjY9RiNn8MXUXsYs/Client-Design?type=design&node-id=0%3A1&mode=design&t=MUDQhR3iOM3DpI9m-1) 🎨 --- ## Contributing Now that you're up and running, if you want to change something and open a PR for us, make sure to read [CONTRIBUTING.md](CONTRIBUTING.md) to make sure you're not wasting your time. --- ## Some Other Random Notes Most of this is for internal GitButler use, but maybe everyone else will find it interesting too. --- ### Icon generation I always forget how to do this, but when we update our app icon, run this to import it. ```bash $ pnpm tauri icon path/to/icon.png ``` ### Release Building is done via [GitHub Action](https://github.com/gitbutlerapp/gitbutler/actions/workflows/publish.yaml). Go to the link and select `Run workflow` from the desired branch. ### Versioning When running the [release action](https://github.com/gitbutlerapp/gitbutler/actions/workflows/publish.yaml), you will have to choose one of `major`, `minor`, or `patch` release type. Action will generate a new version based on your input and current version found at `https://app.gitbutler.com/releases`. ### Publishing To publish a version that you've just build, use [Release Manager](https://gitbutler.retool.com/apps/cb9cbed6-ae0a-11ed-918c-736c4335d3af/Release%20Manager). --- ## Development mode OAuth login By default, you will not be able to log into GitButler using Github/Google because the base url does not match. To be able to do this add ( or update ) the following line to your `.env.development` file. You will need to create the file if it does not exist. ``` PUBLIC_API_BASE_URL=https://app.gitbutler.com/ ``` The desktop frontend and local backend both honor `PUBLIC_API_BASE_URL`. If you need a backend-only override while debugging, `GITBUTLER_API_URL` still takes precedence on the Rust side. When you start the desktop app with `pnpm dev:desktop`, the Tauri launcher now loads `apps/desktop/.env`, `.env.local`, `.env.development`, and `.env.development.local` into the Rust process as well, so the frontend and backend see the same local overrides by default. --- ## Joining the GitButler Team If you are interested in joining our small but tightly knit engineering team, we are currently looking for the following roles: - [Senior Rust developer](https://jobs.gitbutler.com/jobs/backend-rust) (SF, Berlin or Remote) - [Senior TypeScript developer](https://jobs.gitbutler.com/jobs/frontend-typescript) (SF, Berlin or Remote) - [Gerrit developer](https://jobs.gitbutler.com/jobs/gerrit-developer) (SF, Berlin or Remote) ## Code Hitlist This is a list of crates/modules that we want to eliminate or split into smaller crates: - [gitbutler-reference](crates/gitbutler-reference/) (just bad) - [gitbutler-branch-actions](crates/gitbutler-branch-actions/) (contains functionality outside of the virtual branch domain (e.g. commit actions etc.)) - [gitbutler-branch](crates/gitbutler-branch/) (contains `diff` and `branch` contexts due to a cyclic dependency) - [gitbutler_repo::config](crates/gitbutler-repo/src/config.rs) (seems like the wrong abstraction)