From 2dbe6491236f7461af419641679b4815c18b3613 Mon Sep 17 00:00:00 2001 From: Blai Peidro Date: Sat, 31 Oct 2026 02:32:50 +0100 Subject: [PATCH 1/2] docs: one-line README features, intro example moved to docs/example.md --- README.md | 54 ++++----------------- docs/example.md | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 44 deletions(-) create mode 100644 docs/example.md diff --git a/README.md b/README.md index e2d305e..fba37ff 100644 --- a/README.md +++ b/README.md @@ -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 on one repository. --- ## ✨ 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 --- @@ -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 | diff --git a/docs/example.md b/docs/example.md new file mode 100644 index 0000000..7f84295 --- /dev/null +++ b/docs/example.md @@ -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). From 607e4d53198a7f5f8780fff5358c1e89d60046ef Mon Sep 17 00:00:00 2001 From: Blai Peidro Date: Sat, 31 Oct 2026 02:33:19 +0100 Subject: [PATCH 2/2] docs: shorter example line --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fba37ff..db08430 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ 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. -See [a complete example](docs/example.md): an API, a script and a webhook on one repository. +See [a complete example](docs/example.md): an API, a script and a webhook. ---