Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 10 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,57 +13,22 @@ from a repository, with what changed in their environment; and it tells webhooks
Nothing has to reach in, no CI runner has to be registered, and every item is a YAML file in a
configuration directory.

```yaml
apis:
- name: portainer-users
project: infra # the watched repository
trigger: {paths: [portainer/users/*.json]}
credential: cred-portainer
api:
url: https://portainer.example.com/api/users/$file.stem
method: PUT
compare: {ignore_fields: [Id]} # skip what is already there
on_delete: {method: DELETE}

scripts:
- name: reload-dns
project: infra
trigger: {paths: [dns/**]}
script:
project: ops-scripts # the file can live in another repository
file: reload-dns.sh
args: [--zone, example.com]

webhooks:
- name: notify
project: infra
events: [commit_pulled, api_failed, script_failed]
webhook:
url: https://hooks.example.com/services/x
body: {text: "$event.project: $event.event at $event.revision_short"}
```
See [a complete example](docs/example.md): an API, a script and a webhook.

---

## ✨ Features

- **Pull, not push**: repositories are pulled on an interval, a cron schedule or on demand
- **Acts on what changed**: each item names the files it watches; a commit runs only the items it
touched, with only those files
- **APIs**: one request per changed file (its contents as the body) or per commit; upsert, compare
before writing, delete when a file is removed, order, dry run
- **Scripts**: from the watched repository or another; arguments, environment, stdin, exit codes,
timeouts that stop the whole process group, output kept
- **Webhooks**: any HTTP receiver, the body shaped by a template; pulls, API runs and script runs
as events
- **Every common request option**: bearer, basic, header, query, OAuth 2.0 client credentials,
login flows; HMAC signatures; retries with Retry-After; TLS with your own CA or a client
certificate; proxies; rate limits; following a job until it finishes
- **Pull, not push**: repositories pulled on an interval, a cron schedule or on demand
- **Acts on what changed**: a commit runs only the items whose files it touched
- **APIs**: a request per changed file or per commit; upsert, compare, delete, dry run
- **Scripts**: from any repository; args, env, stdin, exit codes, timeouts, output kept
- **Webhooks**: any HTTP receiver, the body shaped by a template over each event
- **Every request option**: OAuth 2.0, login flows, HMAC signing, retries, TLS, proxies
- **Triggers**: on a change, at startup, on a cron schedule or by hand, with a debounce
- **Run history**: every run, its commit and each step's outcome, in the API and the UI
- **Config as code**: one item per file, any folders; every template and setting checked at load
- **Web UI and LDAP sign-in**, API keys scoped by project, secrets from Vault or the environment,
Prometheus metrics, live config reload, single binary
- **Config as code**: one YAML item per file, every template and setting checked at load
- **Web UI**, LDAP sign-in, scoped API keys, Vault secrets, Prometheus metrics, one binary

---

Expand All @@ -72,6 +37,7 @@ webhooks:
| Document | What it covers |
| ----------------------------------------- | ---------------------------------------------------------------------- |
| [How it works](docs/how-it-works.md) | Pulls, changed files, triggers, runs and events |
| [Example](docs/example.md) | A complete configuration directory: an API, a script and a webhook |
| [APIs](docs/apis.md) | Calling an API with what changed: per file or commit, upsert, compare |
| [Scripts](docs/scripts.md) | Running a script on a change: its file, arguments and environment |
| [Webhooks](docs/webhooks.md) | Telling a receiver what happened: events, the message, body templates |
Expand Down
121 changes: 121 additions & 0 deletions docs/example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Example

A complete configuration directory: one repository watched, Portainer users kept in step with
its JSON files, a DNS reload script run from another repository, and a webhook told about
every pull and every failure. `unified-gitops --check-config` accepts it as it is.

```
config/
├── config.yaml
├── projects/
│ ├── infra.yaml
│ └── ops-scripts.yaml
├── credentials/
│ ├── cred-github.yaml
│ └── cred-portainer.yaml
├── apis/
│ └── portainer-users.yaml
├── scripts/
│ └── reload-dns.yaml
└── webhooks/
└── notify.yaml
```

## The server

```yaml
# config.yaml
server:
host: "0.0.0.0"
port: 8183
```

## The repositories

`infra` is the one watched; `ops-scripts` only holds the script.

```yaml
# projects/infra.yaml
projects:
- name: infra
git_url: https://github.example.com/ops/infra.git
credentials: [cred-github]

# projects/ops-scripts.yaml
projects:
- name: ops-scripts
git_url: https://github.example.com/ops/ops-scripts.git
credentials: [cred-github]
```

## The credentials

Each names the environment variable holding the secret, never the secret itself.

```yaml
# credentials/cred-github.yaml
credentials:
- name: cred-github
type: token
env_vars: {token: GITHUB_TOKEN}

# credentials/cred-portainer.yaml
credentials:
- name: cred-portainer
type: token
env_vars: {token: PORTAINER_TOKEN}
```

## An API

Every changed `portainer/users/*.json` file is sent as the body of a `PUT` to the user named after
it, unless Portainer already has the same contents; removing the file deletes the user.

```yaml
# apis/portainer-users.yaml
apis:
- name: portainer-users
project: infra # the watched repository
trigger: {paths: [portainer/users/*.json]}
credential: cred-portainer
api:
url: https://portainer.example.com/api/users/$file.stem
method: PUT
compare: {ignore_fields: [Id]} # skip what is already there
on_delete: {method: DELETE}
```

## A script

A commit touching `dns/` runs `reload-dns.sh` from `ops-scripts`, with the changed files in its
environment.

```yaml
# scripts/reload-dns.yaml
scripts:
- name: reload-dns
project: infra
trigger: {paths: [dns/**]}
script:
project: ops-scripts # the file can live in another repository
file: reload-dns.sh
args: [--zone, example.com]
```

## A webhook

Each pull that brings commits, and each failed API or script run, posts a one-line message.

```yaml
# webhooks/notify.yaml
webhooks:
- name: notify
project: infra
events: [commit_pulled, api_failed, script_failed]
webhook:
url: https://hooks.example.com/services/x
body: {text: "$event.project: $event.event at $event.revision_short"}
```

Every option of each is in [apis.md](apis.md), [scripts.md](scripts.md),
[webhooks.md](webhooks.md) and [configuration.md](configuration.md).
Loading