git-next/README.md
Paul Campbell 209b29d217
All checks were successful
Rust / build (push) Successful in 1m9s
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
fix: typos in mermaid diagram
2024-07-03 07:30:17 +01:00

439 lines
12 KiB
Markdown

# git-next
- Status: Alpha - 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
- Enforce the requirement for each commit to pass the CI pipeline before being
included in the main branch
- Provide a server component that manages the trunk-based development process
- Ensure a consistent, high-quality codebase by preventing untested changes
from being merged
## Prerequisits
- Rust 1.76.0 or later - https://www.rust-lang.org
- pgk-config
- libssl-dev
### 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:
```shell
cargo install --path .
```
Not yet available to install from `crates.io`.
## 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.
```toml
[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.
```toml
[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:
```toml
[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
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)
```mermaid
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.
```mermaid
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.
```mermaid
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.
```mermaid
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:
```shell
git next init
```
This will create a `.git-next.toml` file. [Default](./default.toml)
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:
```shell
git next server init
```
This will create a `git-next-server.toml` file. [Default](./server-default.toml)
Edit this file to your needs. See the [Configuration](#configuration) section above.
### Run the server
In the directory with your `git-next-server.toml` file, run the command:
```shell
git next server start
```
### Forges
The following forges are supported:
- [ForgeJo](https://forgejo.org) (probably compatible with Gitea, but not tested)
- [GitHub](https://github.com/)
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:
```toml
[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:
```toml
[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](https://github.com/settings/tokens/new) 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](https://git.kemitix.net/kemitix/git-next/issues/new).
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](https://just.systems/man/en/chapter_3.html))
## Crate Dependency
The following diagram shows the dependency between the crates that make up `git-next`:
```mermaid
stateDiagram-v2
cli --> server
cli --> git
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 --> git
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](./LICENSE).
```
```
```
```