git-next/README.md
Paul Campbell 6a8d1bf817
All checks were successful
Rust / build (push) Successful in 1m39s
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
docs: add roadmap to readme
2024-07-05 18:55:36 +01:00

12 KiB

git-next

  • Status: BETA - dog-fooding

git-next is a combined server and command-line tool that enables trunk-based development workflows where each commit must pass CI before being included in the main branch.

Features

  • Allows enforce the requirement for each commit to pass the CI pipeline before being included in the main branch
  • Provides a server component that manages the trunk-based development process
  • Ensure a consistent, high-quality codebase by preventing untested changes from being added to main

See Behaviour to learn how we do this.

Prerequisits

x86_64-unknown-linux-gnu

Additionally for this platform, to improved compilation times:

  • clang-16
  • mold

See .cargo/config.toml for how they are configured.

Installation

You can install git-next using Cargo:

cargo install --path crates/cli

Not yet available to install from crates.io.

Roadmap

  • cli
  • server
  • notifications - notify user when intervention required (e.g. to rebase)
  • tui overview
  • webui overview

Branch Names

git-next uses three branches, main, next and dev, although they do not need to have those names. In the documentation we will use those names, but each repo must specify the names of the branches to use for each, even if they happen to have those same names.

Configuration

  • The branches to use for main, next and dev must be specified in either the .git-next.toml in the repo itself, or in the server configuration file, git-next-server.toml. See below for details.
  • CI checks should be configured to run when the next branch is pushed.
  • The dev branch must have the main branch as an ancestor.
  • The next branch must have the main branch as an ancestor.

Server

The server is configured by the git-next-server.toml file.

http

The server needs to be able to receive webhook notifications from your forge, (e.g. github.com). You can do this via any method that suits your environment, e.g. ngrok or a reverse proxy from a web server that itself can route traffic to the machine you are running the git-next server on.

Specify the address and port the server should listen to for incoming webhooks. This is the address and port that your reverse proxy should route traffic to.

  • addr - the IP address the server should bind to
  • port - the IP port the server should bind to

webhook

Your forges need to know where they should route webhooks to. This should be an address this is accessible to the forge. So, for github.com, it would need to be a publicly accessible HTTPS URL. For a self-hosted forge, e.g. ForgeJo, on your own network, then it only needs to be accessible from the server your forge is running on.

  • url - the HTTPS URL for forges to send webhook to

storage

git-next will create a bare clone of each repo that you configure it to monitor. They will all be created in the directory specified here. This data does not need to be backed up, as any missing information will be cloned when the server starts up.

  • path - directory to store local copies of monitored repos

forge

Within the forge tree, specify each forge you want to monitor repos on.

Give your forge an alias, e.g. default, gh, github.

e.g.

[forge.github]
forge_type = "GitHub"
hostname = "github.com"
user = "username"
token = "api-key"
  • forge_type - one of: ForgeJo or GitHub
  • hostname - the hostname for the forge.
  • user - the user to authenticate as
  • token - application token for the user. See below for the permissions required for on each forge.

Generally, the user will need to be able to push to main and to force-push to next.

repos

For each forge, you need to specify which repos on the forge you want to monitor. They do not need to be owned by the user, but they user must have the push and force-push permissions as mentioned above for each of the repositories.

e.g.

[forge.github.repos]
my-repo = { repo = "owner/repo", branch = "main", gitdir = "/home/pcampbell/project/my-repo" }

[forge.github.repos.other-repo]
repo = "user/other"
branch = "master"
main = "master"
next = "ci-testing"
dev = "trunk"

Note that toml allows specifying the values on one line, or across multiple lines. Both are equivalent. What is not equivalent between my-repo and other-repo, is that one will require a configuration file within the repo itself. other-repo specifies the main, next and dev branches to be used, but my-repo doesn't.

A sample .git-next-toml file that would need to exist in my-repo's owner/repo repo, on the main branch:

[branches]
main = "main"
next = "next"
dev = "dev"
  • repo - the owner and name of the repo to be monitored
  • branch - the branch to look for a .git-next.toml file if needed
  • gitdir - (optional) you can use a local copy of the repo
  • main - the branch to use as main
  • next - the branch to use as next
  • dev - the branch to use as dev
gitdir

Additional notes on using gitdir:

When you specify the gitdir value, the repo cloned in that directory will be used for perform the equivalent of git fetch, git push and git push --force-with-lease.

These commands will not affect the contents of your working tree, nor will it change any local branches. Only the details about branches on the remote forge will be updated.

Currently git-next can only use a gitdir if the forge and repo is the same one specified as the origin remote. Otherwise the behaviour is untested and undefined.

Behaviour

The branch names are configurable, but we will talk about main, next and dev.

Development happens on the dev branch, where each commit is expected to be able to pass the CI checks.

(Note: in the diagrams, mermaid isn't capable of showing main and next on the same commit, so we show next as empty)

gitGraph
    commit
    commit

    branch next

    branch dev
    commit
    commit
    commit

When the git-next server sees that the dev branch is ahead of the next branch, it will push the next branch fast-forward one commit along the dev branch.

gitGraph
    commit
    commit

    branch next
    commit

    branch dev
    commit
    commit

It will then wait for the CI checks to pass for the newly updated next branch. When the CI checks for the next branch pass, it will push the main branch fast-forward to the next branch. We return to the top and start again.

gitGraph
    commit
    commit
    commit

    branch next

    branch dev
    commit
    commit

If the CI checks should fail for the next branch, the developer should amend that commit in the history of their dev branch. They should then force-push their rebased dev branch.

gitGraph
    commit
    commit

    branch next
    commit

    checkout main

    branch dev
    commit

    commit
    commit

git-next will then detect that the next branch is no longer part of the dev branch ancestory, and will reset next back to main. We then return to the top, where git-next sees that dev is ahead of next.

When the dev branch is on the same commit as the main branch, then there are no pending commits and git-next will wait until it receives a webhook indicating that there has been a push to one of the branches. At which point it will start at the top again.

Important

The dev branch should have the next branch as an ancestor.

However, when the commit on tip of the next branch has failed CI and is amended, this will not be the case. When this happens git-next will force-push the next branch back to the same commit as the main branch.

This is the only time a force-push will happen in git-next.

In short, the next branch belongs to git-next. Don't try to update it yourself. git-next will update the next it as it sees fit.

Getting Started

To use git-next for trunk-based development, follow these steps:

Initialise the repo (optional)

You need to specify which branches you are using. You can do this in the repo, or in the server configuration.

To create a default config file for the repo, run this command in the root of your repo:

git next init

This will create a .git-next.toml file. Default

By default the expected branches are main, next and dev. Each of these three branches must exist in your repo.

Initialise the server

The server uses the file git-next-server.toml for configuration. It expects to find this file the the current directory when executed.

The create the default config file, run this command:

git next server init

This will create a git-next-server.toml file. Default

Edit this file to your needs. See the Configuration section above.

Run the server

In the directory with your git-next-server.toml file, run the command:

git next server start

Forges

The following forges are supported:

  • ForgeJo (probably compatible with Gitea, but not tested)
  • GitHub

Note: ForgeJo is a hard fork of Gitea, but currently they are largely compatible. For now using a forge_type of ForgeJo with a Gitea instance will probably work okay. The only API calls we make are around registering and unregistering webhooks. So, as long as those APIs remain the same, they should be compatible.

ForgeJo

Configure the forge in git-next-server.toml like:

[forge.jo]
forge_type = "ForgeJo"
hostname = "git.myforgejo.com"
user = "bob"
token = "..."

[forge.jo.repos]
hello = { repo = "user/hello", branch = "main", gitdir = "/opt/git/projects/user/hello.git" }             # maps to https://git.example.net/user/hello on the branch 'main'
world = { repo = "user/world", branch = "master", main = "master", next = "upcoming", "dev" = "develop" } # maps to the 'master' branch

The token is created on your ForgeJo instance at (for example) https://git.myforgejo.com/user/settings/applications and requires the write:repository permission.

GitHub

Configure the forge in git-next-server.toml like:

[forge.gh]
forge_type = "GitHub"
hostname = "github.com" # required even for GitHub
user = "bob"
token = "..."

[forge.gh.repos]
hello = { repo = "user/hello", branch = "main", gitdir = "/opt/git/projects/user/hello.git" }             # maps to https://github.com/user/hello on the branch 'main'
world = { repo = "user/world", branch = "master", main = "master", next = "upcoming", "dev" = "develop" } # maps to the 'master' branch

The token is created here and requires the repo and admin:repo_hook permissions.

Contributing

Contributions to git-next are welcome! If you find a bug or have a feature request, please create an issue. If you'd like to contribute code, feel free to submit changes.

Before you start committing, run the just install-hooks command to setup the Git Hooks. (Get Just)

Crate Dependency

The following diagram shows the dependency between the crates that make up git-next:

stateDiagram-v2

    cli --> server

    file_watcher_actor --> config
    file_watcher_actor --> actor_macros

    forge --> config
    forge --> git
    forge --> forge_forgejo
    forge --> forge_github

    forge_forgejo --> config
    forge_forgejo --> git

    forge_github --> config
    forge_github --> git

    git --> config

    repo_actor --> config
    repo_actor --> git
    repo_actor --> forge
    repo_actor --> actor_macros

    server --> config
    server --> file_watcher_actor
    server --> server_actor

    server_actor --> config
    server_actor --> git
    server_actor --> forge
    server_actor --> repo_actor
    server_actor --> actor_macros
    server_actor --> file_watcher_actor
    server_actor --> webhook_actor

    webhook_actor --> config
    webhook_actor --> repo_actor

License

git-next is released under the MIT License.