From 27b1e7f8d90bf51ad0f01635989b2c8c16dcb2e0 Mon Sep 17 00:00:00 2001 From: Olblak Date: Wed, 23 Sep 2026 21:31:08 +0200 Subject: [PATCH 1/4] docs: add Udash documentation and document relative paths Add a Udash section covering the quick start, Helm installation, agent, configuration, authentication, sending reports, organising pipelines with labels, dashboards, the API, and troubleshooting. It describes udash v0.17.1 and later (oidc mode, roles, API tokens, data retention) and the token-based `updatecli udash login` from Updatecli v0.121.0. Also document the `options.relativepaths` manifest setting and the `UPDATECLI_RELATIVE_PATHS` variable, and point existing Udash links to the new section. --- .../core/configuration/relativepaths.yaml | 22 ++ .../code_example/docs/udash/agent/values.yaml | 32 +++ .../udash/authentication/values-oidc.yaml | 29 ++ .../udash/authentication/values-zitadel.yaml | 19 ++ .../docs/udash/configuration/config.json | 10 + .../docs/udash/configuration/config.yaml | 43 +++ .../installation/values-split-domain.yaml | 22 ++ .../udash/installation/values-subpath.yaml | 22 ++ .../docs/udash/installation/values.yaml | 27 ++ .../docs/udash/labels/autodiscovery.yaml | 23 ++ .../docs/udash/labels/updatecli-compose.yaml | 5 + .../docs/udash/labels/updatecli.yaml | 38 +++ .../docs/udash/labels/values.yaml | 17 ++ .../docs/udash/quick-start/config.json | 6 + .../docs/udash/quick-start/config.yaml | 9 + .../udash/quick-start/docker-compose.yaml | 50 ++++ .../udash/quick-start/updatecli-compose.yaml | 3 + .../docs/udash/sending-reports/updatecli.yaml | 37 +++ config/_default/menus/menus.en.toml | 6 + content/en/docs/core/configuration.adoc | 78 +++++ content/en/docs/core/label.adoc | 2 +- content/en/docs/core/scm.adoc | 3 +- content/en/docs/help/environment.adoc | 11 +- content/en/docs/help/experimental.adoc | 4 +- content/en/docs/udash/_index.md | 36 +++ content/en/docs/udash/agent.adoc | 195 +++++++++++++ content/en/docs/udash/api.adoc | 203 +++++++++++++ content/en/docs/udash/authentication.adoc | 263 +++++++++++++++++ content/en/docs/udash/configuration.adoc | 270 ++++++++++++++++++ content/en/docs/udash/dashboards.adoc | 115 ++++++++ content/en/docs/udash/installation.adoc | 170 +++++++++++ content/en/docs/udash/introduction.adoc | 93 ++++++ content/en/docs/udash/labels.adoc | 194 +++++++++++++ content/en/docs/udash/quick-start.adoc | 169 +++++++++++ content/en/docs/udash/sending-reports.adoc | 164 +++++++++++ content/en/docs/udash/troubleshooting.adoc | 190 ++++++++++++ 36 files changed, 2575 insertions(+), 5 deletions(-) create mode 100644 assets/code_example/docs/core/configuration/relativepaths.yaml create mode 100644 assets/code_example/docs/udash/agent/values.yaml create mode 100644 assets/code_example/docs/udash/authentication/values-oidc.yaml create mode 100644 assets/code_example/docs/udash/authentication/values-zitadel.yaml create mode 100644 assets/code_example/docs/udash/configuration/config.json create mode 100644 assets/code_example/docs/udash/configuration/config.yaml create mode 100644 assets/code_example/docs/udash/installation/values-split-domain.yaml create mode 100644 assets/code_example/docs/udash/installation/values-subpath.yaml create mode 100644 assets/code_example/docs/udash/installation/values.yaml create mode 100644 assets/code_example/docs/udash/labels/autodiscovery.yaml create mode 100644 assets/code_example/docs/udash/labels/updatecli-compose.yaml create mode 100644 assets/code_example/docs/udash/labels/updatecli.yaml create mode 100644 assets/code_example/docs/udash/labels/values.yaml create mode 100644 assets/code_example/docs/udash/quick-start/config.json create mode 100644 assets/code_example/docs/udash/quick-start/config.yaml create mode 100644 assets/code_example/docs/udash/quick-start/docker-compose.yaml create mode 100644 assets/code_example/docs/udash/quick-start/updatecli-compose.yaml create mode 100644 assets/code_example/docs/udash/sending-reports/updatecli.yaml create mode 100644 content/en/docs/udash/_index.md create mode 100644 content/en/docs/udash/agent.adoc create mode 100644 content/en/docs/udash/api.adoc create mode 100644 content/en/docs/udash/authentication.adoc create mode 100644 content/en/docs/udash/configuration.adoc create mode 100644 content/en/docs/udash/dashboards.adoc create mode 100644 content/en/docs/udash/installation.adoc create mode 100644 content/en/docs/udash/introduction.adoc create mode 100644 content/en/docs/udash/labels.adoc create mode 100644 content/en/docs/udash/quick-start.adoc create mode 100644 content/en/docs/udash/sending-reports.adoc create mode 100644 content/en/docs/udash/troubleshooting.adoc diff --git a/assets/code_example/docs/core/configuration/relativepaths.yaml b/assets/code_example/docs/core/configuration/relativepaths.yaml new file mode 100644 index 000000000..10cfc7667 --- /dev/null +++ b/assets/code_example/docs/core/configuration/relativepaths.yaml @@ -0,0 +1,22 @@ +name: Keep the chart image tag in sync + +options: + # "Chart.yaml" and "values.yaml" below are looked up next to this manifest, + # whatever directory updatecli was started from. + relativepaths: manifest + +sources: + version: + name: Read the chart version + kind: yaml + spec: + file: Chart.yaml + key: $.version + +targets: + tag: + name: Report it as the image tag + kind: yaml + spec: + file: values.yaml + key: $.image.tag diff --git a/assets/code_example/docs/udash/agent/values.yaml b/assets/code_example/docs/udash/agent/values.yaml new file mode 100644 index 000000000..5d65314fc --- /dev/null +++ b/assets/code_example/docs/udash/agent/values.yaml @@ -0,0 +1,32 @@ +# Credentials handed to every agent CronJob, stored in a Secret. +secrets: + agent: + environments: + GITHUB_TOKEN: "ghp_xxxxxxxxxxxxxxxxxxxx" + GITHUB_ACTOR: "my-github-user" + database: + stringdata: + # The AgentRelay writes to the same database as the Udash server. + uri: "postgres://udash:password@udash-postgres:5432/udash?sslmode=disable" + +# Applies to every agent that does not set its own schedule. +defaultSchedule: "0 * * * *" + +agents: + my-org-repos: + schedule: "*/30 * * * *" + # Each entry becomes /etc/updatecli/ inside the container. + valuesFiles: + scm.yaml: | + scm: + enabled: true + user: updatecli + owner: my-org + repository: my-repo + branch: main + composeFile: | + policies: + - name: Update Updatecli policies + policy: ghcr.io/updatecli/policies/autodiscovery/all + values: + - /etc/updatecli/scm.yaml diff --git a/assets/code_example/docs/udash/authentication/values-oidc.yaml b/assets/code_example/docs/udash/authentication/values-oidc.yaml new file mode 100644 index 000000000..163df72b9 --- /dev/null +++ b/assets/code_example/docs/udash/authentication/values-oidc.yaml @@ -0,0 +1,29 @@ +auth: + # Configures the server and the frontend at once. + enabled: true + mode: oidc + # public leaves the read endpoints open and requires a token for writes. + # private requires a token everywhere. + visibility: public + # Must match the "iss" claim of your provider exactly, trailing slash included. + issuer: https://example.eu.auth0.com/ + clientid: "xxxxxxxxxxxxxxxxxxxxxxxx" + audience: + - https://udash.example.com/api + # Empty requests "openid profile email offline_access". + scope: "" + roles: + # Without a claim, every signed-in user is a viewer and nobody can publish. + claim: "https://udash.example.com/roles" + +ingress: + enabled: true + hosts: + - host: udash.example.com + paths: + front: "/" + server: "/api" + +front: + apiBaseUrl: "/api" + appBasePath: "/" diff --git a/assets/code_example/docs/udash/authentication/values-zitadel.yaml b/assets/code_example/docs/udash/authentication/values-zitadel.yaml new file mode 100644 index 000000000..e1749469d --- /dev/null +++ b/assets/code_example/docs/udash/authentication/values-zitadel.yaml @@ -0,0 +1,19 @@ +auth: + enabled: true + mode: zitadel + visibility: public + # Used by the frontend for OIDC discovery. + issuer: https://my-instance.region.zitadel.cloud + clientid: "123456789012345678@udash" + audience: + - https://udash.example.com/api + # Zitadel rejects the token at the API unless the project audience scope is requested. + scope: "openid profile email offline_access urn:zitadel:iam:org:project:id:123456789012345678:aud" + zitadel: + domain: my-instance.region.zitadel.cloud + keyFile: + # Reference a Secret you manage yourself rather than inlining the key. + existingSecret: udash-zitadel-key + key: key.json + # No roles block needed: zitadel mode reads the project roles claim by default, + # and grants publisher to the udash.publisher project role. diff --git a/assets/code_example/docs/udash/configuration/config.json b/assets/code_example/docs/udash/configuration/config.json new file mode 100644 index 000000000..b85fd0859 --- /dev/null +++ b/assets/code_example/docs/udash/configuration/config.json @@ -0,0 +1,10 @@ +{ + "AUTH_ENABLED": true, + "AUTH_VISIBILITY": "public", + "OAUTH_DOMAIN": "https://example.eu.auth0.com/", + "OAUTH_CLIENTID": "xxxxxxxxxxxxxxxxxxxxxxxx", + "OAUTH_SCOPE": "openid profile email offline_access", + "API_BASE_URL": "/api", + "APP_BASE_PATH": "/", + "MAX_HISTORY_DAYS": 30 +} diff --git a/assets/code_example/docs/udash/configuration/config.yaml b/assets/code_example/docs/udash/configuration/config.yaml new file mode 100644 index 000000000..463ae522f --- /dev/null +++ b/assets/code_example/docs/udash/configuration/config.yaml @@ -0,0 +1,43 @@ +server: + auth: + # mode selects how incoming tokens are validated. + # "oidc", "zitadel", or "none". Unset behaves like "none". + # An unrecognised value stops the server. + mode: "oidc" + # visibility controls which endpoints require a token. + # "public" leaves the reads open, "private" locks everything. + visibility: "public" + oidc: + # Compared to the token "iss" claim verbatim, trailing slash included. + issuer: "https://example.eu.auth0.com/" + # A list: every entry is an accepted audience. + audience: + - "https://udash.example.com/api" + zitadel: + domain: "my-instance.region.zitadel.cloud" + keyfile: "/etc/udash/zitadel/key.json" + roles: + # Token claim holding the provider roles. Required in "oidc" mode for + # anyone to get more than the default permission. + claim: "https://udash.example.com/roles" + # Provider roles granting each Udash permission. + mapping: + admin: ["udash.admin"] + publisher: ["udash.publisher"] + viewer: ["udash.viewer"] + # Granted to an authenticated identity matching no role. + default: "viewer" + # "snapshot" or "zitadel", see the Authentication page. + resolver: "snapshot" + cachettl: "60s" + +database: + uri: "postgres://udash:password@db:5432/udash?sslmode=disable" + # Set to true to skip the schema migrations run at startup. + migrationdisabled: false + +gc: + # Days of reports to keep. 0, the default, keeps everything. + maxHistoryDays: 0 + interval: "24h" + batchSize: 5000 diff --git a/assets/code_example/docs/udash/installation/values-split-domain.yaml b/assets/code_example/docs/udash/installation/values-split-domain.yaml new file mode 100644 index 000000000..a64f71831 --- /dev/null +++ b/assets/code_example/docs/udash/installation/values-split-domain.yaml @@ -0,0 +1,22 @@ +# Split-domain routing: the API answers on its own hostname. +# A second Ingress is created for ingress.server.host, and the browser needs an +# absolute front.apiBaseUrl because the API is no longer same-origin. +ingress: + enabled: true + hosts: + - host: udash.example.com + paths: + front: "/" + server: "/api" + server: + host: api.example.com + # The server serves /api natively, so nothing needs rewriting here. + path: "/api" + tls: + - secretName: udash-api-tls + hosts: + - api.example.com + +front: + apiBaseUrl: "https://api.example.com/api" + appBasePath: "/" diff --git a/assets/code_example/docs/udash/installation/values-subpath.yaml b/assets/code_example/docs/udash/installation/values-subpath.yaml new file mode 100644 index 000000000..48c33e525 --- /dev/null +++ b/assets/code_example/docs/udash/installation/values-subpath.yaml @@ -0,0 +1,22 @@ +# Subpath routing: the frontend lives under /udash on a shared hostname. +# ingress.paths.front and front.appBasePath must carry the same value, and the +# prefix has to be stripped before the request reaches nginx. +# +# The API keeps its own top-level /api path: the server only ever serves /api, +# and nothing rewrites the path on same-host routing. +ingress: + enabled: true + hosts: + - host: tools.example.com + paths: + front: "/udash" + server: "/api" + traefik: + stripPrefix: + # Creates a StripPrefix Middleware for /udash and wires its annotation. + # Requires the Traefik CRDs (traefik.io/v1alpha1) in the cluster. + enabled: true + +front: + apiBaseUrl: "/api" + appBasePath: "/udash" diff --git a/assets/code_example/docs/udash/installation/values.yaml b/assets/code_example/docs/udash/installation/values.yaml new file mode 100644 index 000000000..4997d95b5 --- /dev/null +++ b/assets/code_example/docs/udash/installation/values.yaml @@ -0,0 +1,27 @@ +# Same-host routing: the frontend on / and the API on /api, one hostname. +ingress: + enabled: true + className: nginx + hosts: + - host: udash.example.com + tls: + - secretName: udash-tls + hosts: + - udash.example.com + paths: + front: "/" + server: "/api" + +front: + # Relative, because the API is reachable on the same host. + apiBaseUrl: "/api" + appBasePath: "/" + maxHistoryDays: 30 + +cnpg: + enabled: true + instances: 1 + database: udash + owner: udash + storage: + size: 10Gi diff --git a/assets/code_example/docs/udash/labels/autodiscovery.yaml b/assets/code_example/docs/udash/labels/autodiscovery.yaml new file mode 100644 index 000000000..ad2c64438 --- /dev/null +++ b/assets/code_example/docs/udash/labels/autodiscovery.yaml @@ -0,0 +1,23 @@ +name: Discover Go module updates +pipelineid: go/autodiscovery + +# Copied onto every pipeline the crawler generates, and stored with every report. +labels: + team: platform + ecosystem: go + monitor: passive + +# The scm is what files these reports under a Git repository on the Git dashboard. +scms: + default: + kind: github + spec: + owner: my-org + repository: my-service + branch: main + token: '{{ requiredEnv "GITHUB_TOKEN" }}' + +autodiscovery: + scmid: default + crawlers: + golang: diff --git a/assets/code_example/docs/udash/labels/updatecli-compose.yaml b/assets/code_example/docs/udash/labels/updatecli-compose.yaml new file mode 100644 index 000000000..627d47307 --- /dev/null +++ b/assets/code_example/docs/udash/labels/updatecli-compose.yaml @@ -0,0 +1,5 @@ +policies: + - name: Rust dependencies + policy: ghcr.io/updatecli/policies/autodiscovery/cargo:latest + values: + - values.yaml diff --git a/assets/code_example/docs/udash/labels/updatecli.yaml b/assets/code_example/docs/udash/labels/updatecli.yaml new file mode 100644 index 000000000..92a174db6 --- /dev/null +++ b/assets/code_example/docs/udash/labels/updatecli.yaml @@ -0,0 +1,38 @@ +name: Bump the base image +pipelineid: docker/base-image + +# Stored with every report, and offered by the Udash label filter. +labels: + team: platform + ecosystem: docker + +# The scm files the reports under this Git repository in Udash. +scms: + default: + kind: github + spec: + owner: my-org + repository: my-service + branch: main + token: '{{ requiredEnv "GITHUB_TOKEN" }}' + +sources: + alpine: + name: Get the latest Alpine version + kind: dockerimage + spec: + image: alpine + versionfilter: + kind: semver + +targets: + dockerfile: + name: Update the Alpine base image + kind: dockerfile + scmid: default + sourceid: alpine + spec: + file: Dockerfile + instruction: + keyword: FROM + matcher: alpine diff --git a/assets/code_example/docs/udash/labels/values.yaml b/assets/code_example/docs/udash/labels/values.yaml new file mode 100644 index 000000000..d500b1eac --- /dev/null +++ b/assets/code_example/docs/udash/labels/values.yaml @@ -0,0 +1,17 @@ +scm: + enabled: true + kind: github + owner: my-org + repository: my-crate + branch: main + +# Pull request labels, applied on GitHub. Udash never sees these. +labels: + - dependencies + +# Pipeline labels, stored with every report and offered by the Udash filters. +pipeline: + labels: + ecosystem: cargo + policy: autodiscovery + team: platform diff --git a/assets/code_example/docs/udash/quick-start/config.json b/assets/code_example/docs/udash/quick-start/config.json new file mode 100644 index 000000000..e9ff7d80a --- /dev/null +++ b/assets/code_example/docs/udash/quick-start/config.json @@ -0,0 +1,6 @@ +{ + "AUTH_ENABLED": false, + "API_BASE_URL": "/api", + "APP_BASE_PATH": "/", + "MAX_HISTORY_DAYS": 30 +} diff --git a/assets/code_example/docs/udash/quick-start/config.yaml b/assets/code_example/docs/udash/quick-start/config.yaml new file mode 100644 index 000000000..609720c1e --- /dev/null +++ b/assets/code_example/docs/udash/quick-start/config.yaml @@ -0,0 +1,9 @@ +server: + auth: + # No authentication: anyone reaching this instance can read and write reports. + # Fine on a laptop, never on anything reachable from outside. + mode: "none" + +database: + uri: postgres://udash:password@db:5432/udash?sslmode=disable + migrationdisabled: false diff --git a/assets/code_example/docs/udash/quick-start/docker-compose.yaml b/assets/code_example/docs/udash/quick-start/docker-compose.yaml new file mode 100644 index 000000000..9f4f773e2 --- /dev/null +++ b/assets/code_example/docs/udash/quick-start/docker-compose.yaml @@ -0,0 +1,50 @@ +services: + db: + image: postgres:17 + restart: always + environment: + - POSTGRES_USER=udash + - POSTGRES_PASSWORD=password + - POSTGRES_DB=udash + expose: + - 5432 + + server: + image: ghcr.io/updatecli/udash:v0.17.1 + command: server start + restart: always + environment: + - GIN_MODE=release + volumes: + - "./udash/config.yaml:/home/udash/.udash/config.yaml" + expose: + - 8080 + labels: + - "traefik.enable=true" + - "traefik.http.routers.server.rule=PathPrefix(`/api/`)" + - "traefik.http.routers.server.service=server" + - "traefik.http.services.server.loadbalancer.server.port=8080" + + front: + image: ghcr.io/updatecli/udash-front:v0.25.0 + restart: always + volumes: + - "./udash-front/config.json:/usr/share/nginx/html/config.json" + expose: + - 80 + labels: + - "traefik.enable=true" + - "traefik.http.routers.front.rule=PathPrefix(`/`)" + - "traefik.http.routers.front.service=front" + - "traefik.http.services.front.loadbalancer.server.port=80" + + traefik: + image: traefik:v3.0 + command: + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + - "--entrypoints.web.address=:80" + ports: + - "80:80" + volumes: + - "/var/run/docker.sock:/var/run/docker.sock:ro" diff --git a/assets/code_example/docs/udash/quick-start/updatecli-compose.yaml b/assets/code_example/docs/udash/quick-start/updatecli-compose.yaml new file mode 100644 index 000000000..86f47672b --- /dev/null +++ b/assets/code_example/docs/udash/quick-start/updatecli-compose.yaml @@ -0,0 +1,3 @@ +policies: + - name: Discover what could be updated here + policy: ghcr.io/updatecli/policies/autodiscovery/all:latest diff --git a/assets/code_example/docs/udash/sending-reports/updatecli.yaml b/assets/code_example/docs/udash/sending-reports/updatecli.yaml new file mode 100644 index 000000000..b1c9576f2 --- /dev/null +++ b/assets/code_example/docs/udash/sending-reports/updatecli.yaml @@ -0,0 +1,37 @@ +name: Bump the base image +pipelineid: docker/base-image + +labels: + ecosystem: docker + monitor: active + +# The scm files the reports under this Git repository in Udash. +scms: + default: + kind: github + spec: + owner: my-org + repository: my-service + branch: main + token: '{{ requiredEnv "GITHUB_TOKEN" }}' + +sources: + alpine: + name: Get the latest Alpine version + kind: dockerimage + spec: + image: alpine + versionfilter: + kind: semver + +targets: + dockerfile: + name: Update the Alpine base image + kind: dockerfile + scmid: default + sourceid: alpine + spec: + file: Dockerfile + instruction: + keyword: FROM + matcher: alpine diff --git a/config/_default/menus/menus.en.toml b/config/_default/menus/menus.en.toml index 1ba090411..3bcbaf2d8 100644 --- a/config/_default/menus/menus.en.toml +++ b/config/_default/menus/menus.en.toml @@ -32,6 +32,12 @@ weight = 40 identifier = "guide" pageRef = "/docs/guides" +[[sidebar_docs]] +name = "Udash" +weight = 45 +identifier = "udash" +pageRef = "/docs/udash" + [[sidebar_docs]] name = "Commands" weight = 50 diff --git a/content/en/docs/core/configuration.adoc b/content/en/docs/core/configuration.adoc index 3f17da6ee..b9625cb37 100644 --- a/content/en/docs/core/configuration.adoc +++ b/content/en/docs/core/configuration.adoc @@ -67,6 +67,10 @@ A manifest accepts the following root keys. All of them are optional except `nam | string | The minimum Updatecli version required by the manifest. Updatecli skips the manifest, with an error, when its own version is lower. +| `options` +| object +| Settings changing how Updatecli behaves for this manifest, as opposed to describing the pipeline itself. See <<_options,Options>>. + | `sources` | map | See link:/docs/core/source/[source]. @@ -147,6 +151,80 @@ This affects manifest execution order only and does not replace `pipelineid`. To learn more, see **link:/docs/core/order/[manifest order]**. +[#_options] +=== Options + +The `options` root key groups the settings that change how Updatecli behaves for a manifest, as opposed to the keys describing what the pipeline is made of. + +[cols="1,1,3",options=header] +|=== +| Key | Default | Description + +| `relativepaths` +| `workingdirectory` +| What the relative paths of the manifest resolve against. See <<_relative_paths,Relative paths>>. +|=== + +[#_relative_paths] +==== Relative paths + +By default, a relative path written in a manifest resolves against the directory Updatecli was started from, not against the manifest that declares it. +A manifest stored in `updatecli.d/nodejs.yaml` and asking for `file: package.json` therefore reads the `package.json` of whichever directory you happened to run the command in. + +Setting `relativepaths` to `manifest` makes those paths resolve against the directory holding the manifest instead, which is what allows a manifest, or a whole directory of them, to be moved, vendored into another repository, or distributed as a link:/docs/core/shareandreuse/[policy] without rewriting every path. + +[cols="1,3",options=header] +|=== +| Value | Behaviour + +| `workingdirectory` +| Relative paths resolve against the directory Updatecli was started from. This is the default, and the historical behaviour. + +| `manifest` +| Relative paths resolve against the directory holding the manifest that declares them. +|=== + +++++ +
updatecli.yaml +
+{{}}
+
+
+++++ + +The setting applies to the manifest that declares it, so a directory of manifests can be migrated one file at a time. +It can also be set for a whole run, with the `--relative-paths` flag or the `UPDATECLI_RELATIVE_PATHS` environment variable, which is the practical way to try it on an existing repository: + +[source,shell] +---- +updatecli diff --relative-paths manifest --config updatecli.d +---- + +A manifest declaring `options.relativepaths` wins over the flag and the environment variable. + +[IMPORTANT] +==== +`relativepaths` only affects resources that are **not** attached to an link:/docs/core/scm/[scm]. + +A source, condition, or target with an `scmid` always resolves its paths against the working directory of that scm, which is the clone Updatecli made for it. That is unchanged, and is what makes a target committed to the right repository. +==== + +Within a manifest that sets `relativepaths: manifest`, the manifest directory becomes the base for: + +* the file paths of every resource without an `scmid`, such as `spec.file` and `spec.files` +* the working directory of the link:/docs/plugins/resource/shell/[shell] resource, `spec.workdir` +* a relative `directory` in an `scms` entry, and the repository the `local` scm auto-detects +* the directory the link:/docs/core/autodiscovery/[autodiscovery] crawlers scan + +Absolute paths are never rewritten, and `http://` or `https://` locations are always fetched over the network rather than read from disk. + +NOTE: The values, secrets, and manifest files passed on the command line with `--values`, `--secrets`, and `--config` are resolved against the directory Updatecli was started from, whatever `relativepaths` says. They are arguments to the command, not content of a manifest. + +[TIP] +==== +The default stays `workingdirectory` on purpose. The most common layout is a `updatecli.d/` directory whose manifests reference paths from the root of the repository, and those manifests only work because paths resolve against the working directory. Changing the default would break them, so opting in per manifest is the supported path. +==== + === File Each Updatecli pipeline is defined in its own manifest file. diff --git a/content/en/docs/core/label.adoc b/content/en/docs/core/label.adoc index ae9147305..c65e58b6b 100644 --- a/content/en/docs/core/label.adoc +++ b/content/en/docs/core/label.adoc @@ -49,7 +49,7 @@ updatecli compose apply --labels="ecosystem:go" + Pipelines matching the filter will execute. Pipelines without the label or with a different value will be skipped. -2. **Filtering pipeline reports** in link:https://github.com/updatecli/udash/[Udash] +2. **Filtering pipeline reports** in link:/docs/udash/[Udash], where labels group pipelines from many repositories into one view. See link:/docs/udash/labels/[Organising with labels]. == Filtering with `--labels` diff --git a/content/en/docs/core/scm.adoc b/content/en/docs/core/scm.adoc index f2b2b2c89..39e938802 100644 --- a/content/en/docs/core/scm.adoc +++ b/content/en/docs/core/scm.adoc @@ -28,7 +28,8 @@ current working directory, and a target that changes a file there gets its chang to a branch named after the pipeline `pipelineid`. A resource without an `scmid` simply works on the current working directory, and Updatecli neither -clones nor commits anything for it. +clones nor commits anything for it. A manifest can ask for its own directory to be used instead, with +link:/docs/core/configuration/#_relative_paths[`options.relativepaths`]. === The `local` scm diff --git a/content/en/docs/help/environment.adoc b/content/en/docs/help/environment.adoc index 42342d1c0..965be3a77 100644 --- a/content/en/docs/help/environment.adoc +++ b/content/en/docs/help/environment.adoc @@ -107,11 +107,12 @@ fails with `you cannot use both token and app authentication methods`. |=== NOTE: Reporting to Udash requires `--experimental`. Without it the step is skipped silently, whatever -these variables say. See link:/docs/help/experimental/["Experimental features" page]. +these variables say. See link:/docs/help/experimental/["Experimental features" page] and +link:/docs/udash/sending-reports/["Sending reports"]. == Flag defaults -Two flags read a default from the environment, which is the practical way to set them once for a whole +These flags read a default from the environment, which is the practical way to set them once for a whole CI job rather than on every invocation. An unparsable value is ignored and logged at debug level. [cols="1,1,2", options="header"] @@ -125,6 +126,12 @@ CI job rather than on every invocation. An unparsable value is ignored and logge | `UPDATECLI_DISABLE_VERSION_CHECK` | `--disable-version-check` | Skip the check for a newer Updatecli release, which runs after the command completes. + +| `UPDATECLI_RELATIVE_PATHS` +| `--relative-paths` +| What the relative paths of a manifest resolve against, `workingdirectory` (the default) or +`manifest`. A manifest setting `options.relativepaths` itself wins over this. See +link:/docs/core/configuration/#_relative_paths["Relative paths"]. |=== == The `local` scm diff --git a/content/en/docs/help/experimental.adoc b/content/en/docs/help/experimental.adoc index 4cdf99fff..a0344a177 100644 --- a/content/en/docs/help/experimental.adoc +++ b/content/en/docs/help/experimental.adoc @@ -52,12 +52,14 @@ set. === Reporting to Udash -link:https://github.com/updatecli/udash[Udash] collects pipeline results for viewing over time. +link:/docs/udash/[Udash] collects pipeline results for viewing over time. Without the flag, publishing is skipped **silently**, no warning, no error, and the run reports success. Configuring `UPDATECLI_UDASH_*` or running `updatecli udash login` is not enough on its own. With the flag, results are published under a section headed `Udash - Experimental`. +See link:/docs/udash/sending-reports/["Sending reports"] for how to configure the endpoint. + `--disable-udash-report` switches it off again without dropping the rest of the flag. === WebAssembly autodiscovery plugins diff --git a/content/en/docs/udash/_index.md b/content/en/docs/udash/_index.md new file mode 100644 index 000000000..147cf8c2a --- /dev/null +++ b/content/en/docs/udash/_index.md @@ -0,0 +1,36 @@ +--- +title: "Udash" +description: "Deploy Udash, the Updatecli dashboard, and publish your pipeline reports to it for a central view of update activity across every repository." +lead: "" +date: 2026-08-07T10:00:00+02:00 +lastmod: 2026-08-07T10:00:00+02:00 +draft: false +images: [] +sidebar: + collapsed: true +--- + +Updatecli tells you what changed on one run, in one repository. [Udash](https://github.com/updatecli/udash) +collects those runs and keeps them, so you can see the same information across every repository and +over time. + +{{< alert icon="⚠️" text="Udash is experimental, and so is the Updatecli side of it. Publishing reports requires the --experimental flag, and both the API and the interface can change without going through the usual deprecation cycle." >}} + +- [Introduction](/docs/udash/introduction/) - what Udash is, what it stores, and how the pieces fit + together. +- [Quick start](/docs/udash/quick-start/) - run the whole stack locally with Docker Compose and + publish your first report. +- [Installation](/docs/udash/installation/) - deploy it for real with the Helm chart, including the + three ingress topologies. +- [Agent](/docs/udash/agent/) - run Updatecli on a schedule inside your cluster instead of from each + repository's CI. +- [Configuration](/docs/udash/configuration/) - every setting of the server and of the frontend. +- [Authentication](/docs/udash/authentication/) - the authentication modes, API visibility, and how + to register the application with your provider. +- [Sending reports](/docs/udash/sending-reports/) - the Updatecli side, from `udash login` to what + ends up in a report. +- [Organising with labels](/docs/udash/labels/) - group pipelines from many repositories into one + view with Updatecli labels. +- [Dashboards](/docs/udash/dashboards/) - a tour of the interface and its filters. +- [API](/docs/udash/api/) - the endpoints, and the limits an integrator meets first. +- [Troubleshooting](/docs/udash/troubleshooting/) - symptoms, causes, and fixes. diff --git a/content/en/docs/udash/agent.adoc b/content/en/docs/udash/agent.adoc new file mode 100644 index 000000000..fd26b2536 --- /dev/null +++ b/content/en/docs/udash/agent.adoc @@ -0,0 +1,195 @@ +--- +title: "Agent" +description: "Run Updatecli on a schedule inside your Kubernetes cluster with the udash-agent chart, instead of relying on each repository's CI to report." +lead: "Updatecli as a scheduled job in your cluster" +date: 2026-08-07T10:00:00+02:00 +lastmod: 2026-09-23T10:00:00+02:00 +draft: false +images: [] +menu: + docs: + parent: "udash" +weight: 35 +toc: true +--- +// +:toc: +// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key +:toclevels: 4 + +== Description + +The usual way to fill a Udash instance is for each repository's CI to run Updatecli and publish its +report. That works, but every repository has to be set up first, which is a lot of pipelines to +touch before the dashboard is worth looking at. + +The `udash-agent` chart takes the other route. It runs Updatecli itself, on a schedule, from inside +the cluster, against whichever repositories you list. Nothing has to change in those repositories. + +Both approaches write into the same database and appear side by side in the interface, so adopting +one does not rule out the other. + +=== Requirement + +. A Udash instance and its PostgreSQL database, see link:/docs/udash/installation/[Installation] +. Kubernetes 1.19 or later, and Helm 3 +. Credentials for whatever the agents will read, typically a GitHub token +. Argo Workflows, only for the optional workflow mode + +== Architecture + +[cols="1,3", options="header"] +|=== +| Component | Role + +| `agentrelay` +| A Udash server deployed *without authentication*, reachable only inside the cluster at +`http://agentrelay/api`. The agents publish to it, and it writes to the same PostgreSQL as the +Udash server users actually browse. + +| `agent-` +| One Kubernetes `CronJob` per entry in `agents`, running `updatecli compose diff --experimental` +from the `ghcr.io/updatecli/updatecli` image. + +| Argo `CronWorkflow` +| Optional. One per entry in `workflows`, cloning a Git repository and running Updatecli against the +compose file it finds there. +|=== + +[WARNING] +==== +The relay exists so agents do not need tokens to publish. It runs with `auth.mode: none`, which means +anything that can reach it can also delete reports. Keep it on a `ClusterIP` service, do not put an +ingress in front of it, and treat network access to it as write access to your report history. +==== + +== Installing + +[source,shell] +---- +helm repo add updatecli https://updatecli.github.io/charts +helm repo update +helm install udash-agent updatecli/udash-agent -f values.yaml +---- + +.values.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/agent/values.yaml" >}} +---- + +`secrets.database.stringdata.uri` has to be the same database the Udash server uses. That is the +only thing tying the two releases together; get it wrong and the agents will happily write into a +database nobody is reading. + +No agent is created by default. An install with an empty `agents` map deploys only the relay. + +== Defining an agent + +Each key under `agents` becomes one CronJob. Everything about it falls back to a chart-wide default, +so a minimal agent is just a name. + +[cols="1,3", options="header"] +|=== +| Field | Meaning + +| `schedule` +| Cron expression for this agent. Falls back to `defaultSchedule`, hourly. + +| `composeFile` +| The Updatecli compose file, mounted at `/etc/updatecli/updatecli-compose.yaml`. Falls back to +`defaultComposeFile`, which runs the `autodiscovery/all` policy. + +| `valuesFiles` +| A map of filename to content. Each entry becomes `/etc/updatecli/` in the container, +which is how the compose file's `values:` references resolve. + +| `udashConfig` +| The Updatecli credentials file, mounted at `/home/updatecli/.config/updatecli/udash.json`. Falls +back to `defaultUdashConfig`, which points at the relay. +|=== + +The default `udash.json` is what connects an agent to the relay: + +[source,json] +---- +{ + "Auths": { + "agentrelay": { + "Api": "http://agentrelay/api", + "URL": "http://localhost:3030" + } + }, + "Default": "agentrelay" +} +---- + +`Api` is where reports go. `URL` only builds the links printed in the job log, so it does not have +to resolve from inside the cluster, though pointing it at your real Udash hostname makes those logs +more useful. + +== Credentials + +Every key under `secrets.agent.environments` is stored in a Secret and injected as an environment +variable into every agent container: + +[source,yaml] +---- +secrets: + agent: + environments: + GITHUB_TOKEN: "ghp_xxxxxxxxxxxxxxxxxxxx" + GITHUB_ACTOR: "my-github-user" +---- + +These are the ordinary Updatecli variables, so anything on the +link:/docs/help/environment/["Environment variables" page] can go here. They are shared by all +agents; there is no per-agent override. + +== Argo Workflows + +The `workflows` list is an alternative to `agents` for cases where the compose file already lives in +the repository. Each entry creates a `CronWorkflow` that clones the repository, runs +`updatecli compose diff --experimental` against the named compose file, and publishes to the relay. + +[source,yaml] +---- +workflows: + - url: "https://github.com/updatecli/udash.git" + branch: "main" + composefile: "updatecli-compose.yaml" +---- + +[NOTE] +==== +The chart renders the Argo `WorkflowTemplate`, `ServiceAccount`, `Role`, and `RoleBinding` +unconditionally, even when `workflows` is empty. If Argo's CRDs are not installed, the install +fails on the `WorkflowTemplate` rather than skipping it. + +The `RoleBinding` also references the service account in a namespace fixed to `udash`, so installing +this chart into a different namespace leaves the binding pointing at nothing and the workflows +unable to run. Install into `udash`, or patch the binding afterwards. +==== + +== Verifying it works + +[source,shell] +---- +# The CronJobs exist and have a schedule +kubectl get cronjobs + +# Trigger one immediately rather than waiting +kubectl create job --from=cronjob/udash-agent-agent-my-org-repos manual-run + +# Follow it +kubectl logs -f job/manual-run +---- + +The log ends with the same `UDASH - EXPERIMENTAL` section a local run prints. If it does not, see +link:/docs/udash/troubleshooting/#_agent_cronjobs_run_but_nothing_appears[Troubleshooting]. + +== Go further + +* link:/docs/udash/installation/[Installation] - the Udash instance the agents feed. +* link:/docs/core/compose/[Compose] - what a compose file can express. +* link:/docs/udash/sending-reports/[Sending reports] - the CI-side alternative. diff --git a/content/en/docs/udash/api.adoc b/content/en/docs/udash/api.adoc new file mode 100644 index 000000000..5f2e8d9cb --- /dev/null +++ b/content/en/docs/udash/api.adoc @@ -0,0 +1,203 @@ +--- +title: "API" +description: "The Udash HTTP API: where the generated reference lives, what the endpoint groups are, and the query limits an integrator meets first." +lead: "Talking to Udash directly" +date: 2026-08-07T10:00:00+02:00 +lastmod: 2026-09-23T10:00:00+02:00 +draft: false +images: [] +menu: + docs: + parent: "udash" +weight: 80 +toc: true +--- +// +:toc: +// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key +:toclevels: 4 + +== Description + +Everything the interface does, it does through the API, so anything it shows can be pulled out +programmatically. + +The authoritative reference is generated from the source and served by every instance at +`/swagger/index.html`. This page is the orientation around it: which groups exist, which are reads +and which are writes, and the limits that will produce your first `400`. + +== Base path and health + +Everything lives under `/api/`. + +[cols="1,3", options="header"] +|=== +| Endpoint | Purpose + +| `GET /api/ping` +| Returns `pong`. The liveness check. + +| `GET /api/about` +| The API version, the Go version it was built with, and the build time. + +| `GET /swagger/index.html` +| The generated reference for this exact instance. +|=== + +== Endpoint groups + +[cols="2,1,3", options="header"] +|=== +| Group | Kind | Purpose + +| `GET /api/pipeline/reports` +| read +| List reports. See `latest` below. + +| `GET /api/pipeline/reports/{id}` +| read +| One report, in full. + +| `POST /api/pipeline/reports/search` +| read +| Search reports with a filter too structured for a query string. This is what the interface calls. + +| `POST /api/pipeline/reports/summary` +| read +| Aggregate reports into time buckets. Backs the activity chart. + +| `POST /api/pipeline/reports` +| write +| Create a report. This is the endpoint Updatecli publishes to. + +| `PUT /api/pipeline/reports/{id}` +| write +| Replace a report. + +| `DELETE /api/pipeline/reports/{id}` +| write +| Delete a report. + +| `GET /api/pipeline/labels`, `POST /api/pipeline/labels/search` +| read +| The labels seen across stored reports, which is what populates the label filter. + +| `GET /api/pipeline/scms`, `POST /api/pipeline/scms/search` +| read +| The Git repositories and branches seen across stored reports. + +| `GET /api/pipeline/config/{sources,conditions,targets}` and their `/search` +| read +| The resource configurations extracted from reports, so you can ask which pipelines use a given +resource kind. + +| `GET /api/pipeline/config/kinds` +| read +| The distinct resource kinds present. + +| `GET /api/whoami` +| read +| The identity, permission, and token scopes behind the credential used. Only exists when an +authentication mode is configured. Updatecli calls it to validate a token at login. + +| `GET /api/tokens` +| read +| List API tokens. + +| `POST /api/tokens` +| write +| Create an API token. Requires the `publisher` permission and a provider token, never an API token. + +| `DELETE /api/tokens/{id}`, `DELETE /api/tokens` +| write +| Revoke one token, or every token of an identity. The second form requires `admin`. +|=== + +[NOTE] +==== +Several reads are `POST` requests, because their filters do not fit in a query string. That does not +make them writes, and the visibility rules treat them as reads. See +link:/docs/udash/authentication/[Authentication]. +==== + +== Authentication + +Which endpoints need a token depends entirely on how the instance is configured: + +* no mode configured, the default: nothing requires a token, including the writes +* `visibility: public`: reads are open, writes need a token +* `visibility: private`: everything needs a token + +A token alone is not enough to write a report. The identity behind it needs the `publisher` +permission, and an API token needs the `reports:write` scope. The `/api/tokens` endpoints always +require authentication, whatever the visibility. + +Pass the token as `Authorization: Bearer `. See link:/docs/udash/authentication/[Authentication] for +obtaining one. + +== `latest` + +`GET /api/pipeline/reports` takes a `latest` parameter, defaulting to true, which returns only the +most recent report per pipeline ID. + +Left alone, you get the current state, one row per pipeline, which is what a dashboard needs. Set +to false, you get the history, every run of every pipeline, which is what a trend needs and is +considerably more data. + +== Pagination + +List and search endpoints take `limit` and `page`, and return the total count alongside the results, +so you can tell whether you are looking at everything. + +`limit` may not exceed *1000*. Beyond that the request is rejected rather than quietly truncated. + +== Time windows and their limits + +The API bounds its own queries, because the aggregations run over every matching row and an +unbounded range means scanning most of the table. + +[cols="2,1,3", options="header"] +|=== +| Rule | Limit | Error + +| Default search window when none is given +| 7 days +| n/a + +| Maximum span between `start_time` and `end_time` +| 366 days +| `requested time range exceeds the maximum allowed span` + +| Both boundaries required together +| n/a +| `both start_time and end_time must be provided` + +| `days` and `hours` are mutually exclusive +| n/a +| `days and hours cannot be combined` + +| Buckets a summary may return +| 1000 +| `requested time range and granularity produce too many buckets` + +| Records per page +| 1000 +| `invalid pagination parameters` +|=== + +The bucket limit depends on the granularity as well as the range: a year at daily granularity is +accepted, while a year at hourly granularity is more than eight thousand buckets and is refused. + +== Summaries + +`POST /api/pipeline/reports/summary` returns counts per time bucket. Each bucket carries its start +in UTC as RFC3339, a total, and a breakdown per Updatecli result. + +The only metric supported so far is `result`. Granularity and the time window are the parameters +you vary. + +== Go further + +* `/swagger/index.html` on your instance - the complete, generated reference. +* link:/docs/udash/authentication/[Authentication] - what a token is needed for. +* link:/docs/udash/troubleshooting/[Troubleshooting] - the `400` responses, in one table. diff --git a/content/en/docs/udash/authentication.adoc b/content/en/docs/udash/authentication.adoc new file mode 100644 index 000000000..b8271e4be --- /dev/null +++ b/content/en/docs/udash/authentication.adoc @@ -0,0 +1,263 @@ +--- +title: "Authentication" +description: "The Udash authentication modes, what API visibility changes, how to register the application with an OIDC provider, and how Updatecli authenticates against it." +lead: "Locking down a Udash instance" +date: 2026-08-07T10:00:00+02:00 +lastmod: 2026-09-23T10:00:00+02:00 +draft: false +images: [] +menu: + docs: + parent: "udash" +weight: 50 +toc: true +--- +// +:toc: +// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key +:toclevels: 4 + +== Description + +Authentication is off by default. With no mode configured the server installs no authentication +middleware at all, so anyone who can reach the API can read, create, and delete reports. That is fine +for the link:/docs/udash/quick-start/[quick start] on a laptop, and for nothing else. + +Turning it on means two things agreeing with each other: the server validating tokens, and the +frontend obtaining them. The Helm chart configures both from one `auth` block. + +== Modes + +[cols="1,3", options="header"] +|=== +| Mode | Behaviour + +| `none`, or unset +| No middleware. Every endpoint is open, including the ones that write. + +| `oidc` +| Generic OpenID Connect. Tokens are validated locally as JWTs against the issuer's signing keys +and the configured audience, so only JWT access tokens are accepted. Works with any compliant +provider, Zitadel included. + +| `zitadel` +| Validates tokens by introspection against link:https://zitadel.com[Zitadel], which also accepts +opaque tokens such as Zitadel personal access tokens. Needs a service account key. +|=== + +Any other value stops the server at startup. That includes `oauth`, the name of the generic mode +before Udash v0.17.1, and the Helm chart refuses to render it with a message pointing at the +rename. + +== Visibility + +Whichever mode is set, `visibility` decides how much of the API the token is needed for. + +[cols="1,3", options="header"] +|=== +| Value | Effect + +| `public` +| The default. `GET`, `HEAD`, and `OPTIONS` stay open to anyone. Everything else requires a valid +token. + +| `private` +| Every endpoint requires a valid token. +|=== + +`public` suits an instance whose dashboards are meant to be readable by the whole engineering +organisation while only CI may write into it. + +The frontend has its own `AUTH_VISIBILITY` setting, which defaults to `private`. It has to match +the server's value, otherwise a `public` instance still sends anonymous visitors to the login page. +The chart writes both from `auth.visibility`. + +[NOTE] +==== +Several read operations are `POST` requests, because their filters do not fit in a query string: +the `/search` endpoints and `reports/summary`. Under `public` visibility they are served outside the +authenticated group, so they stay readable like any `GET`. +==== + +== Permissions + +A valid token lets a request through, and the permission behind it decides what the request may do. + +[cols="1,3", options="header"] +|=== +| Permission | Grants + +| `viewer` +| Reading pipeline reports. + +| `publisher` +| Publishing, replacing, and deleting reports, and creating API tokens. + +| `admin` +| Everything, plus managing the API tokens of any identity. +|=== + +Permissions come from the roles your provider puts in the token. `roles.claim` names the claim that +holds them, and `roles.mapping` lists the provider roles that grant each permission, by default +`udash.admin`, `udash.publisher`, and `udash.viewer`. Both shapes providers use are accepted: an +object keyed by role name, as Zitadel emits, and an array of strings, as Keycloak and Auth0 emit. + +A signed-in identity that matches no role gets `roles.default`, which is `viewer` unless you change +it. + +[IMPORTANT] +==== +In `oidc` mode `roles.claim` has no default. Leave it empty and no roles are read at all, so every +user is a `viewer` and nobody can publish a report or create an API token. Set it to the claim your +provider uses, for example `realm_access.roles` on Keycloak or a namespaced claim such as +`https://udash.example.com/roles` on Auth0. + +In `zitadel` mode it defaults to `urn:zitadel:iam:org:project:roles`, so granting the +`udash.publisher` project role is enough. +==== + +== Configuring it with the chart + +The chart's `auth` block feeds both the server's `config.yaml` and the frontend's `config.json`. + +.values.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/authentication/values-oidc.yaml" >}} +---- + +The chart refuses to render rather than deploying something silently open: an unrecognised +`auth.mode`, `auth.visibility`, or `auth.roles.default`, a missing `auth.issuer` in `oidc` mode, or +a missing `auth.zitadel.domain` in `zitadel` mode all fail the template with an explicit message. + +Doing it without the chart means writing the same values into both files by hand, see +link:/docs/udash/configuration/[Configuration]. + +== The issuer, and its trailing slash + +`auth.issuer` may be given with or without a scheme, and `https://` is assumed when it is omitted. +Beyond that it is used verbatim, and the validator compares it to the token's `iss` claim exactly. +It is the most common reason an otherwise correct setup rejects every request. + +[IMPORTANT] +==== +The trailing slash is significant. Auth0 issues an `iss` with one, Zitadel and Keycloak do not. Get +it wrong and every token is rejected, with nothing in the logs pointing at the slash. + +Read the `iss` claim out of a real token from your provider and copy it exactly. +==== + +== Registering the application + +Register the frontend as a *User Agent* or *SPA* client, using *PKCE*. The provider must support the +Authorization Code with PKCE flow; Udash does not use any other. + +The redirect URI is not configurable. It is derived from the browser origin and `front.appBasePath`, +so register `https://` as *both* the allowed redirect URI and the +post-logout redirect URI. `helm install` prints the exact URL for your values. + +=== Scopes + +Left empty, the frontend requests `openid profile email offline_access`. Keep `offline_access`: +it is what lets the session renew silently instead of bouncing the user back to the provider. + +Zitadel needs one more. Without the project audience scope, the token is issued but the API rejects +it: + +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/authentication/values-zitadel.yaml" >}} +---- + +=== Profile fields + +The interface reads the profile from the provider's userinfo endpoint and merges it over the ID +token's own claims, because Zitadel omits `email`, `name`, `preferred_username`, and `picture` from +the ID token unless the application opts in. A userinfo failure is not fatal: the session survives +and simply shows a sparser profile. + +== API tokens + +A provider's access token always expires, and an unattended pipeline needs a credential it can +keep. Udash therefore issues its own API tokens and validates them itself. They never expire unless +an expiry is set when they are created. + +Anyone with the `publisher` permission creates them from *Profile* then *Tokens* in the interface, +at `/profile/tokens`. The token is shown once: only its SHA-256 hash is stored. Tokens start with +`udash_pat_`, which tells them apart from a provider token and lets secret scanners recognise one +that leaks. + +Each token carries scopes, `reports:read` and `reports:write`, chosen at creation and never wider +than what its creator is allowed. No scope allows managing tokens, and creating a token requires +signing in through the provider, so a leaked token cannot be used to create new ones. + +=== When a permission changes + +A token has no provider token of its own to read roles from, so `roles.resolver` decides where its +permission comes from: + +* `snapshot`, the default outside `zitadel` mode, trusts the permission recorded when the token was + created. Removing someone's role does not affect the tokens they already made, so offboarding + someone means deleting their tokens. +* `zitadel`, the default in `zitadel` mode, asks Zitadel for the creator's current grants, so a + revoked role also stops their tokens. The service user behind the key file must be allowed to + read user grants. + +A resolved permission is reused for `roles.cachettl`, 60 seconds by default, so a pipeline +publishing many reports does not query the provider for each one. + +=== Zitadel personal access tokens + +Because `zitadel` mode validates tokens by introspection, a Zitadel personal access token on a +machine user works as a permanent credential with no Udash-side setup. Create a service user in +Zitadel, grant it the project role mapped to `publisher`, and create a personal access token with +no expiration. + +Every request then costs an introspection round trip to Zitadel, and creating such a token needs +Zitadel administrator rights, so it does not scale to letting each team issue its own. + +== Authenticating Updatecli + +Once the instance requires a token, Updatecli needs one too. + +=== Interactively + +[source,shell] +---- +updatecli udash login "https://udash.example.com" --experimental +---- + +The command first calls `/whoami`. On an instance with authentication enabled, it prints the +`/profile/tokens` address, opens it in a browser, and prompts for the token. It validates the token +against the API before writing it to the Updatecli configuration file, so a wrong token fails here +rather than on the first publish. + +When standard input is not a terminal, the token is read from it: + +[source,shell] +---- +echo "$UDASH_TOKEN" | updatecli udash login "https://udash.example.com" --experimental +---- + +`--token` passes it as a flag instead, and `--api-url` sets the API endpoint when it is not the +given URL with `/api` appended. + +=== In CI + +Hand the token over through the environment: + +[source,shell] +---- +export UPDATECLI_UDASH_URL="https://udash.example.com" +export UPDATECLI_UDASH_API_URL="https://udash.example.com/api" +export UPDATECLI_UDASH_ACCESS_TOKEN="udash_pat_..." +updatecli apply --experimental +---- + +The token needs the `reports:write` scope, which a new token gets unless other scopes are chosen. + +== Go further + +* link:/docs/udash/configuration/[Configuration] - the underlying keys, for a deployment without the chart. +* link:/docs/udash/sending-reports/[Sending reports] - publishing once the token is in place. +* link:/docs/udash/api/[API] - which endpoints the visibility setting covers. diff --git a/content/en/docs/udash/configuration.adoc b/content/en/docs/udash/configuration.adoc new file mode 100644 index 000000000..59e1f84b0 --- /dev/null +++ b/content/en/docs/udash/configuration.adoc @@ -0,0 +1,270 @@ +--- +title: "Configuration" +description: "Every Udash setting: the server configuration file and its environment fallbacks, and the runtime config.json that configures the frontend." +lead: "Configuring the server and the frontend" +date: 2026-08-07T10:00:00+02:00 +lastmod: 2026-09-23T10:00:00+02:00 +draft: false +images: [] +menu: + docs: + parent: "udash" + identifier: "udash-configuration" +weight: 40 +toc: true +--- +// +:toc: +// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key +:toclevels: 4 + +== Description + +Udash has two configuration surfaces that do not overlap. The server reads a YAML file, the frontend +reads a JSON file at runtime in the browser. Neither knows about the other, so an authenticated +deployment has to be told the same thing twice. The link:/docs/udash/installation/[Helm chart] +does that for you from a single `auth` block. + +== The server + +=== Where the file is read from + +The file is named `config.yaml` and is looked up in this order: + +. the working directory +. `$HOME/.udash/` +. `/etc/udash/` + +The first one found wins. `--config` takes the path of a different file instead, and that file +then has to exist. The file is watched, and a change is logged, so a `ConfigMap` update does not go +unnoticed. + +=== Settings + +.config.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/configuration/config.yaml" >}} +---- + +[cols="1,3", options="header"] +|=== +| Key | Meaning + +| `server.auth.mode` +| `oidc`, `zitadel`, or `none`. Unset behaves like `none`, which means no authentication middleware +at all. Any other value, including the former `oauth`, stops the server at startup. See +link:/docs/udash/authentication/[Authentication]. + +| `server.auth.visibility` +| `public` or `private`. Defaults to `public`. Only meaningful when a mode is set. + +| `server.auth.oidc.issuer` +| The OIDC issuer, compared verbatim to the token's `iss` claim. Required in `oidc` mode. + +| `server.auth.oidc.audience` +| A list. Every entry is an accepted audience. + +| `server.auth.zitadel.domain` +| The Zitadel instance domain, for example `xxx.region.zitadel.cloud`. + +| `server.auth.zitadel.keyfile` +| Path to the Zitadel service account key file. + +| `server.auth.roles.claim` +| The token claim holding the provider roles. Defaults to `urn:zitadel:iam:org:project:roles` in +`zitadel` mode, and has to be set in `oidc` mode for any role to be read. + +| `server.auth.roles.mapping` +| Per Udash permission (`admin`, `publisher`, `viewer`), the provider roles that grant it. Defaults +to `udash.admin`, `udash.publisher`, and `udash.viewer`. + +| `server.auth.roles.default` +| The permission granted to a signed-in identity matching no role. Defaults to `viewer`. + +| `server.auth.roles.resolver` +| How the permission behind a Udash API token is resolved: `snapshot` or `zitadel`. Defaults to +`zitadel` in `zitadel` mode and `snapshot` otherwise. + +| `server.auth.roles.cachettl` +| How long a resolved permission is reused. Defaults to `60s`. + +| `database.uri` +| The PostgreSQL connection URI. + +| `database.migrationdisabled` +| Set to `true` to skip the schema migrations the server otherwise runs at startup. + +| `gc.maxHistoryDays` +| How many days of reports to keep. `0`, the default, keeps everything. See +<<_data_retention,Data retention>>. + +| `gc.interval` +| Time between two garbage collections. Defaults to `24h`. + +| `gc.batchSize` +| Maximum number of reports deleted by one statement. Defaults to `5000`. +|=== + +[NOTE] +==== +`issuer` and `audience` sit under `oidc:`, not directly under `auth:`. The same applies to the +Zitadel settings under `zitadel:` and the role settings under `roles:`. +==== + +=== Environment fallbacks + +[cols="1,2", options="header"] +|=== +| Variable | Sets + +| `UDASH_DB_URI` +| `database.uri` + +| `UDASH_AUTH_MODE` +| `server.auth.mode` + +| `UDASH_AUTH_OIDC_ISSUER` +| `server.auth.oidc.issuer` + +| `UDASH_AUTH_OIDC_AUDIENCE` +| `server.auth.oidc.audience`, as a single audience + +| `UDASH_AUTH_ZITADEL_DOMAIN` +| `server.auth.zitadel.domain` + +| `UDASH_AUTH_ZITADEL_KEYFILE` +| `server.auth.zitadel.keyfile` + +| `UDASH_AUTH_ROLES_CLAIM` +| `server.auth.roles.claim` + +| `UDASH_AUTH_ROLES_DEFAULT` +| `server.auth.roles.default` + +| `UDASH_AUTH_ROLES_RESOLVER` +| `server.auth.roles.resolver` + +| `UDASH_GC_MAX_HISTORY_DAYS` +| `gc.maxHistoryDays` + +| `UDASH_GC_INTERVAL` +| `gc.interval` + +| `UDASH_GC_BATCH_SIZE` +| `gc.batchSize` +|=== + +[IMPORTANT] +==== +These are fallbacks, not overrides. Each one is read only when the matching key is absent from the +configuration file, so the file always wins. This is the opposite of the precedence Updatecli +applies to its own `UPDATECLI_UDASH_*` variables, where the environment supersedes the file. +==== + +=== Data retention + +Data retention requires Udash v0.18.0 or later. Udash keeps every report forever unless +`gc.maxHistoryDays` is set. With it, the server runs a +garbage collector a minute after it starts and then every `gc.interval`. It deletes every report +older than the retention, including the last report of a pipeline that stopped running, so that +pipeline disappears from the dashboard. It then deletes the scms, labels, and resource +configurations that no remaining report references and that were not used within the retention +period. + +When several servers share a database, a PostgreSQL advisory lock ensures only one of them collects +at a time. + +The same collection can run once from the command line, for example from a scheduled job. +`--dry-run` only counts what would be deleted, and `--max-history-days` overrides the retention: + +[source,shell] +---- +udash gc --config config.yaml --max-history-days 90 --dry-run +---- + +Unlike the server, `udash gc` never migrates the schema, so run it against a database the server +has already migrated. + +== The frontend + +The frontend is a static bundle. Everything about it, including whether authentication exists at +all, comes from a `config.json` served next to it at `/usr/share/nginx/html/config.json`. The page +fetches that file before importing the bundle and exposes it as `window.config`. + +* Changing configuration never requires rebuilding the image. One published image serves an open + deployment and an authenticated one. +* The file is fetched by probing the parent path prefixes of the current URL, so the same image + works unchanged at the root or under any subpath. + +.config.json +[source,json] +---- +{{< include "assets/code_example/docs/udash/configuration/config.json" >}} +---- + +[cols="1,1,3", options="header"] +|=== +| Key | Default | Meaning + +| `AUTH_ENABLED` +| `false` +| Whether the application has a login. When false there is no login and no route guard. + +| `AUTH_VISIBILITY` +| `private` +| Mirrors `server.auth.visibility`, and only matters when `AUTH_ENABLED` is true. `public` lets +anonymous visitors browse reports, `private` sends them to the login first. An unknown value falls +back to `private`. + +| `OAUTH_DOMAIN` +| none +| The provider's issuer URL, used for OIDC discovery. + +| `OAUTH_CLIENTID` +| none +| The client ID of the SPA application registered with the provider. + +| `OAUTH_SCOPE` +| `openid profile email offline_access` +| Requested scopes. `offline_access` is what enables silent token renewal. + +| `API_BASE_URL` +| `/api` +| Where the browser reaches the API. Relative for same-host routing, absolute when the API has its +own hostname. + +| `APP_BASE_PATH` +| `/` +| The base path of the application, for mounting it under a subpath such as `/udash/`. + +| `MAX_HISTORY_DAYS` +| `30` +| How far back the date filter and the activity chart may reach. +|=== + +[NOTE] +==== +`AUTH_VISIBILITY` defaults to `private` while the server defaults to `public`. The difference is +intended: upgrading the frontend must not start showing data to anonymous visitors because a +configuration file was left unchanged. When you write `config.json` by hand, set it to the same +value as `server.auth.visibility`. The Helm chart does this for you. +==== + +=== `MAX_HISTORY_DAYS` + +It only limits the interface. The API enforces its own ceiling of 366 days regardless, and the +interface clamps a larger value to 366 before sending it. + +Raising it does not make the instance work harder by default: the filter still opens on the last +day whatever the maximum is, so a wider range only costs something once someone deliberately asks +for one. Lower it where a large report history makes those wider queries expensive. + +Keep it at or below `gc.maxHistoryDays` when retention is enabled, otherwise the date filter offers +a range that no longer holds any report. + +== Go further + +* link:/docs/udash/authentication/[Authentication] - what the auth settings above actually do. +* link:/docs/udash/installation/[Installation] - how the chart writes both files for you. +* link:/docs/udash/troubleshooting/[Troubleshooting] - symptoms of a mismatch between the two. diff --git a/content/en/docs/udash/dashboards.adoc b/content/en/docs/udash/dashboards.adoc new file mode 100644 index 000000000..da9b38503 --- /dev/null +++ b/content/en/docs/udash/dashboards.adoc @@ -0,0 +1,115 @@ +--- +title: "Dashboards" +description: "A tour of the Udash interface: the reports view and its filters, a single pipeline report, the Git dashboard, and how to share a filtered view as a link." +lead: "Finding things in the interface" +date: 2026-08-07T10:00:00+02:00 +lastmod: 2026-09-23T10:00:00+02:00 +draft: false +images: [] +menu: + docs: + parent: "udash" +weight: 70 +toc: true +--- +// +:toc: +// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key +:toclevels: 4 + +== Description + +Most of the work in Udash happens on three screens: the reports list, a single report, and the Git +dashboard. This page describes what each one answers and how to narrow it down. + +== Home + +The landing page carries a *pipeline activity* chart, showing report volume and results over the +recent window, and a *Get Started* panel with the exact commands to connect a runner to this +instance, filled in with this deployment's own URLs. If you are onboarding a colleague, sending them +that page is faster than sending them documentation. + +The window the activity chart covers is bounded by `MAX_HISTORY_DAYS`, see +link:/docs/udash/configuration/[Configuration]. + +== Reports + +The main view, at `/pipeline/reports`. Three sections: the filter, a *Latest Status Overview* +summarising the most recent reports for the current repository and branch, and the *Detailed +Reports* list itself. + +=== Filtering + +[cols="1,3", options="header"] +|=== +| Filter | What it does + +| Git repository and branch +| Narrows to one repository, and optionally one branch. Populated from the `scm` of the reports that +have been received, so a pipeline with no `scmid` never appears here. + +| Labels +| Key and value pairs, several at a time, all of which must match. The values offered for a key are +the ones actually present in the data. See link:/docs/udash/labels/[Organising with labels]. + +| Pipeline result +| Success, Changed, Failed, or Skipped. Empty means all of them. + +| Open pull request +| Whether the pipeline currently carries an action left open. Orthogonal to the result: a pipeline +can have succeeded and still be waiting on a pull request nobody merged. + +| Date range +| A slider over the window allowed by `MAX_HISTORY_DAYS`. It opens on the most recent day, so a +wider range is only ever queried when you ask for one. +|=== + +[TIP] +==== +On pipeline results, `⚠` means Updatecli *applied a change*, not that something went wrong. It is +the result you want to look at when asking what actually moved. `✔` means the run was fine and +there was nothing to do. +==== + +=== Sharing a filtered view + +The whole filter state travels in the URL, encoded as a single blob, so a filtered view is a plain +link. Copy the address bar once the filter is how you want it, and whoever opens it sees the same +view. + +This is the practical way to hand over "every failing docker pipeline in this repository over the +last fortnight" without writing instructions for reproducing it. An unreadable or outdated blob +falls back to the default filter rather than breaking the page, so a stale bookmark is harmless. + +== A single report + +At `/pipeline/reports/`, the full run: its sources, conditions, targets, and actions, each with +the configuration it ran with. Alongside them, the changelog Updatecli retrieved, the console output +of the run, and a graph of how the pipeline's stages relate. + +This is where a link published by a CI job lands. Updatecli prints exactly this URL at the end of a +run that publishes, see link:/docs/udash/sending-reports/[Sending reports]. + +== Git dashboard + +At `/scm/dashboard`, the same data organised the other way round: per Git repository rather than per +run. A doughnut and a summary per repository answer "how up to date is this codebase", which is the +question the report list is bad at. + +A pipeline only appears here if it declared an `scm`, so give an `scmid` to the pipelines you +intend to watch. + +== About and profile + +`/about` describes the project and its community, and is reachable without logging in. To check +which server version an instance runs, call `GET /api/about`, see link:/docs/udash/api/[API]. + +`/profile` shows the signed-in identity, and `/profile/tokens` is where you create and revoke the +API tokens Updatecli publishes with. Both only exist when authentication is enabled. See +link:/docs/udash/authentication/#_api_tokens[API tokens]. + +== Go further + +* link:/docs/udash/sending-reports/[Sending reports] - the labels and `scmid` these filters depend on. +* link:/docs/udash/api/[API] - the same queries, programmatically. +* link:/docs/udash/configuration/[Configuration] - `MAX_HISTORY_DAYS` and the rest. diff --git a/content/en/docs/udash/installation.adoc b/content/en/docs/udash/installation.adoc new file mode 100644 index 000000000..5cfdfc1ef --- /dev/null +++ b/content/en/docs/udash/installation.adoc @@ -0,0 +1,170 @@ +--- +title: "Installation" +description: "Deploy Udash on Kubernetes with the Helm chart: the PostgreSQL prerequisite, and the three ingress topologies the chart supports." +lead: "Deploying Udash with the Helm chart" +date: 2026-08-07T10:00:00+02:00 +lastmod: 2026-09-23T10:00:00+02:00 +draft: false +images: [] +menu: + docs: + parent: "udash" + identifier: "udash-installation" +weight: 30 +toc: true +--- +// +:toc: +// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key +:toclevels: 4 + +== Description + +The `udash` chart deploys both workloads, the frontend and the API server, and by default provisions +the PostgreSQL database as well. You still have to choose how traffic reaches the two services, +and that part of the values file needs the most care. + +Authentication is off by default. Enabling it is covered on its own page, +link:/docs/udash/authentication/[Authentication]. + +=== Requirement + +. Kubernetes 1.19 or later +. Helm 3 +. The CloudNative-PG operator, unless you bring your own PostgreSQL + +== The database + +`cnpg.enabled` defaults to `true`, so the chart provisions a link:https://cloudnative-pg.io/[CloudNative-PG] +`Cluster` and injects the credentials into the server itself. The operator is not installed by the +chart and has to be there first: + +[source,shell] +---- +helm repo add cnpg https://cloudnative-pg.github.io/charts +helm upgrade --install cnpg \ + --namespace cnpg-system \ + --create-namespace \ + cnpg/cloudnative-pg \ + --wait +---- + +[IMPORTANT] +==== +Without `--wait`, the operator pod may still be starting when the chart creates the `Cluster`, and +the install fails on the admission webhook: + +[source,text] +---- +failed calling webhook "mcluster.cnpg.io": no endpoints available for service "cnpg-webhook-service" +---- +==== + +To use a PostgreSQL you already run, turn CNPG off and supply the URI: + +[source,shell] +---- +helm install udash updatecli/udash \ + --set cnpg.enabled=false \ + --set secrets.database.stringdata.uri="postgres://user:pass@postgres:5432/udash?sslmode=disable" +---- + +The server runs its schema migrations at startup, so the database only has to exist and be +reachable. + +== Installing + +[source,shell] +---- +helm repo add updatecli https://updatecli.github.io/charts +helm repo update +helm install udash updatecli/udash +---- + +That gets you a running instance with no ingress, reachable through `kubectl port-forward`. The +notes printed by `helm install` give the exact command for your values. + +== Routing + +The chart supports three topologies. Picking one is mostly about what hostnames you have. + +=== Same host + +The default, and the simplest: one hostname, the frontend on `/`, the API on `/api`. The browser +reaches the API on the same origin, so `front.apiBaseUrl` stays relative. + +.values.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/installation/values.yaml" >}} +---- + +=== Subpath + +Udash below a prefix on a shared hostname. Two rules apply: + +. `ingress.paths.front` and `front.appBasePath` must carry the same value. +. The prefix must be stripped before the request reaches nginx, which serves the application at its + own root. + +.values.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/installation/values-subpath.yaml" >}} +---- + +`ingress.traefik.stripPrefix.enabled` creates the Traefik `Middleware` and wires its annotation for +you, which needs the Traefik CRDs (`traefik.io/v1alpha1`) in the cluster. On nginx, do the same by +hand: + +[source,yaml] +---- +ingress: + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$2 + nginx.ingress.kubernetes.io/use-regex: "true" + paths: + front: "/udash(/|$)(.*)" +---- + +The API keeps its own top-level `/api` path even in this topology. The server only ever +serves `/api`, and nothing rewrites the path for the API rule on same-host routing. + +=== Split domain + +The API on its own hostname. Setting `ingress.server.host` creates a second `Ingress`, and because +the API is no longer same-origin, `front.apiBaseUrl` has to be absolute. + +.values.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/installation/values-split-domain.yaml" >}} +---- + +If the external path has to differ from `/api`, enable `ingress.traefik.stripPrefix` and the chart +renders a strip-then-add chain for the server: an external `/updatecli/*` is stripped to `/*` and +then prefixed to `/api/*`, which is what the backend expects. That chain is only rendered when +`ingress.server.host` is set. + +== Images + +[cols="1,2", options="header"] +|=== +| Component | Image + +| Server +| `ghcr.io/updatecli/udash` + +| Frontend +| `ghcr.io/updatecli/udash-front` +|=== + +Both tags default to values pinned by the chart, so take the chart defaults rather than pinning +your own unless you have a reason to. + +== Go further + +* link:/docs/udash/configuration/[Configuration] - every server and frontend setting. +* link:/docs/udash/authentication/[Authentication] - turning authentication on. +* link:/docs/udash/agent/[Agent] - running Updatecli itself inside the cluster. +* link:/docs/udash/troubleshooting/[Troubleshooting] - when the install does not come up. diff --git a/content/en/docs/udash/introduction.adoc b/content/en/docs/udash/introduction.adoc new file mode 100644 index 000000000..b9f48068f --- /dev/null +++ b/content/en/docs/udash/introduction.adoc @@ -0,0 +1,93 @@ +--- +title: "Introduction" +description: "What Udash is, the three pieces it is made of, what a pipeline report contains once it is stored, and how it relates to Updatecli itself." +lead: "The Updatecli dashboard" +date: 2026-08-07T10:00:00+02:00 +lastmod: 2026-09-23T10:00:00+02:00 +draft: false +images: [] +menu: + docs: + parent: "udash" + identifier: "udash-introduction" +weight: 10 +toc: true +--- +// +:toc: +// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key +:toclevels: 4 + +== Description + +An Updatecli run answers one question, in one repository, at one moment: is this dependency up to +date, and if not, what would change. The answer is printed to the console, and it is lost unless the +CI logs happen to be kept. + +Udash stores that answer. Updatecli publishes its pipeline report at the end of a run, and the +interface lets you look at every repository at once, filter by label or by result, and follow how a +dependency moved over the past weeks. + +[WARNING] +==== +Udash is experimental, on both sides. Publishing reports from Updatecli requires the +`--experimental` flag, and the API, the stored schema, and the interface can all change without +going through the deprecation cycle described on the +link:/docs/help/deprecations/["Deprecations" page]. +==== + +== Architecture + +Udash is three components, plus Updatecli itself as the thing that feeds it. + +[cols="1,3", options="header"] +|=== +| Component | Role + +| `udash server` +| The API. A Go service listening on port `8080`, serving everything under `/api/`, plus a generated +Swagger UI at `/swagger/index.html`. Published as `ghcr.io/updatecli/udash`. + +| `udash-front` +| The interface. A Vue 3 and Vuetify single-page application served by nginx on port `80`. It is +configured entirely at runtime, so one image serves every deployment. Published as +`ghcr.io/updatecli/udash-front`. + +| PostgreSQL +| Where the reports live. The server runs its own schema migrations at startup. +|=== + +The frontend never talks to the database, only to the API. Updatecli never talks to the database +either, it POSTs its report to the API like any other client. + +There is a fourth, optional piece: the link:/docs/udash/agent/[agent], which runs Updatecli on a +schedule inside a Kubernetes cluster rather than from each repository's CI. + +== What is stored + +The server splits each report into parts so the interface can search across runs: + +* the pipeline report itself, with its name, its result, and its execution date +* the resource configurations, split into sources, conditions, and targets, each recorded with its + kind and its specification +* the scm, meaning the Git repository URL and the branch a pipeline acted on +* the labels attached to the pipeline, which most of the filtering is built on + +This is why link:/docs/core/label/[labels] and an `scmid` matter more in Udash than they do in a +plain Updatecli run. A report with neither is stored correctly, but it is hard to find again, since +the interface is organised by Git repository and filtered by label. + +== Trying it + +The fastest path is link:/docs/udash/quick-start/[the quick start], which brings the whole stack up +locally with Docker Compose and needs nothing besides Docker. + +For a real deployment, the link:/docs/udash/installation/[Helm chart] covers the database, both +workloads, and the ingress. + +== Go further + +* link:/docs/udash/quick-start/[Quick start] - run it locally in a few minutes. +* link:/docs/udash/sending-reports/[Sending reports] - the Updatecli side of the integration. +* link:/docs/udash/dashboards/[Dashboards] - what the interface offers once reports arrive. +* link:https://github.com/updatecli/udash[updatecli/udash] - the server source and issue tracker. diff --git a/content/en/docs/udash/labels.adoc b/content/en/docs/udash/labels.adoc new file mode 100644 index 000000000..5b9f645f3 --- /dev/null +++ b/content/en/docs/udash/labels.adoc @@ -0,0 +1,194 @@ +--- +title: "Organising with labels" +description: "Use Updatecli pipeline labels to group pipelines from many repositories into one Udash view, and share that view as a link." +lead: "Grouping pipelines into dashboards" +date: 2026-09-23T10:00:00+02:00 +lastmod: 2026-09-23T10:00:00+02:00 +draft: false +images: [] +menu: + docs: + parent: "udash" +weight: 65 +toc: true +--- +// +:toc: +// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key +:toclevels: 4 + +== Description + +Udash does not store named dashboards or groups. A group of pipelines is whatever a label filter +selects, and the view it produces is a link you can bookmark or share. The labels come from your +Updatecli manifests, so deciding how pipelines are grouped in Udash means deciding which labels +they carry. + +This page covers how Udash uses labels and how to choose them, then how to set them on pipelines +you do not write by hand. The general rules for labels, and a suggested vocabulary, are on the +link:/docs/core/label/[Labels] page. + +== From manifest to dashboard + +A pipeline declares its labels at the top level of its manifest: + +.updatecli.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/labels/updatecli.yaml" >}} +---- + +Updatecli copies them into the pipeline report, and Udash stores them with that report. Every +filter in the interface then offers the label keys it has seen, and for each key the values it has +seen, over the selected date range. + +Two views use them: + +[cols="1,3", options="header"] +|=== +| View | What labels do there + +| Git dashboard, `/scm/dashboard` +| Covers every Git repository at once. The label filter decides which pipelines count, so a label +filter here gives you a dashboard for one team or one ecosystem across the whole organisation. +Each branch shows its status and an activity chart for the matching pipelines only. + +| Reports, `/pipeline/reports` +| Works on one repository and branch at a time. Labels narrow the report list within it. +|=== + +The Git dashboard only shows pipelines that declare an `scm`. A pipeline with labels and no `scm` +is still stored and still matches the filter in the API, but it never appears on that dashboard. +Give an `scm` to every pipeline you want to see there. See +link:/docs/udash/sending-reports/#_making_reports_findable[Making reports findable]. + +== How the filter matches + +[cols="1,3", options="header"] +|=== +| Filter | Selects + +| A key and a value +| Pipelines whose label has exactly that value. The comparison is case-sensitive. + +| A key with no value +| Pipelines that carry the key, whatever its value. + +| Several rows +| Pipelines matching all of them. Rows are combined with AND. +|=== + +There is no OR. Each key takes one value, so "every `docker` or `helm` pipeline" cannot be one +view. When you need that grouping regularly, add a key whose value is shared by both, or open two +views. + +These rules are the same as the `--labels` flag of Updatecli, apart from the missing value: in the +Udash filter you leave the value empty, and on the command line you write `--labels="team:"`. + +== Choosing labels for your dashboards + +Start from the views you want, and give each question they answer its own key: + +[cols="2,2", options="header"] +|=== +| View you want | Label that produces it + +| Everything the platform team owns +| `team: platform` + +| Every container image, in every repository +| `ecosystem: docker` + +| The pipelines that must never fall behind +| `monitor: active` + +| The platform team's patch updates +| `team: platform` and `update.channel: patch` +|=== + +Because rows combine with AND, independent keys can be stacked into narrower views without +planning each combination in advance. That only works if a key answers one question: a single +`group: platform-docker` label cannot be split back into a team and an ecosystem. + +Keep the values few and stable. The filter lists every value it has seen for a key, so a label +holding a version number or a file path fills the list with entries nobody will pick, and it +groups nothing. + +The keys used above (`team`, `ecosystem`, `monitor`, and `update.channel`) come from the +link:/docs/core/label/#_suggested_vocabulary[suggested vocabulary]. It is a recommendation. +Updatecli and Udash accept any key and value and validate none of them. Its use is that pipelines +written by different people in different repositories end up under the same filter values. + +== Setting labels on pipelines you do not write + +=== Autodiscovery + +Labels on a manifest that declares `autodiscovery` are copied onto every pipeline the crawlers +generate. One manifest therefore labels all the updates discovered in a repository: + +.updatecli.d/autodiscovery.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/labels/autodiscovery.yaml" >}} +---- + +Every generated pipeline carries the same labels. A label that has to differ between dependencies +needs one autodiscovery manifest per value, see +link:/docs/core/label/#_labels_and_autodiscovery[Labels and autodiscovery]. + +=== Published policies + +A policy's manifests are not yours to edit. Many published policies set their pipeline labels from +a `pipeline.labels` value instead, with defaults such as `ecosystem` and `policy`. Check the +`values.yaml` of the policy you use to see whether it reads that value. + +.updatecli-compose.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/labels/updatecli-compose.yaml" >}} +---- + +.values.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/labels/values.yaml" >}} +---- + +[IMPORTANT] +==== +Policies often have a second, top-level `labels` value. That one is the list of labels put on the +pull request, and it has no effect in Udash. Only `pipeline.labels` reaches the report. +==== + +The same values files work from the link:/docs/udash/agent/[agent chart], through `valuesFiles`, so +pipelines run inside the cluster can carry the same labels as the ones run from CI. + +== Sharing a grouped view + +Once the filter selects the group you want, copy the address from the browser. The whole filter, +labels included, is encoded in the URL, so the link opens the same view for anyone who can reach +the instance. A list of such links, one per team or per ecosystem, is the closest Udash has to a +set of saved dashboards. See link:/docs/udash/dashboards/#_sharing_a_filtered_view[Sharing a +filtered view]. + +== Pitfalls + +Udash rejects a report that contains a label with an empty value. Omit the label instead of +setting it to `""`. + +Changing a label's value splits the history. Reports are stored with the labels they had when they +were published, so after renaming `team: infra` to `team: platform`, the new value only matches +reports published since the change. The older reports stay under the old value until they fall out +of the date range, or until retention deletes them. + +Casing is part of the value. `Team: Platform` and `team: platform` are two different labels, and +the filter lists both. + +Pull request labels, set under `actions..spec.labels`, are applied on the Git provider and +never reach Udash. + +== Go further + +* link:/docs/core/label/[Labels] - the `labels` field, `--labels`, and the suggested vocabulary. +* link:/docs/udash/dashboards/[Dashboards] - the filter and the views it drives. +* link:/docs/udash/api/[API] - the same `labels` filter on the search and summary endpoints. diff --git a/content/en/docs/udash/quick-start.adoc b/content/en/docs/udash/quick-start.adoc new file mode 100644 index 000000000..fbf596c50 --- /dev/null +++ b/content/en/docs/udash/quick-start.adoc @@ -0,0 +1,169 @@ +--- +title: "Quick start" +description: "Run the whole Udash stack locally with Docker Compose, point Updatecli at it, and publish your first pipeline report." +lead: "Udash on your laptop in five minutes" +date: 2026-08-07T10:00:00+02:00 +lastmod: 2026-09-23T10:00:00+02:00 +draft: false +images: [] +menu: + docs: + parent: "udash" +weight: 20 +toc: true +--- +// +:toc: +// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key +:toclevels: 4 + +== Description + +This page brings up PostgreSQL, the Udash API, the interface, and a Traefik in front of them, then +publishes a report into it from your own Updatecli runs. Everything runs locally and nothing is +authenticated. That is convenient for a first look, but do not deploy this setup anywhere other +people can reach it. + +=== Requirement + +. Docker and Docker Compose +. Updatecli, see link:/docs/prologue/installation/[Installation] + +== 1. Create the files + +Three files in one directory: + +[source,text] +---- +. +├── docker-compose.yaml +├── udash +│ └── config.yaml +└── udash-front + └── config.json +---- + +.docker-compose.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/quick-start/docker-compose.yaml" >}} +---- + +Traefik publishes port `80` and routes `/api/` to the server and everything else to the frontend. +The database is not published at all, only the two services reach it. + +.udash/config.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/quick-start/config.yaml" >}} +---- + +.udash-front/config.json +[source,json] +---- +{{< include "assets/code_example/docs/udash/quick-start/config.json" >}} +---- + +`config.json` is fetched by the browser before the application bundle loads, so the same image can +serve an open deployment like this one and an authenticated one. `AUTH_ENABLED` set to `false` is +what removes the login flow, and `API_BASE_URL` tells the browser where the API is. + +== 2. Start the stack + +[source,shell] +---- +docker compose up -d +---- + +Then open `http://localhost`. The API answers on `http://localhost/api`, and +`http://localhost/api/ping` is the quickest way to confirm it is up. + +[NOTE] +==== +If the server exits immediately on the first start, PostgreSQL was not accepting connections yet. +Run `docker compose restart server`. +==== + +== 3. Point Updatecli at it + +[source,shell] +---- +updatecli udash login "http://localhost" --experimental +---- + +The instance has no authentication, so the command does not ask for a token and only records the +endpoint in the Updatecli configuration file. `--api-url` defaults to the given URL with `/api` +appended, which is right here. + +`updatecli udash config` prints where that file was written. + +== 4. Publish a report + +Run Updatecli as you normally would, with `--experimental` added: + +[source,shell] +---- +updatecli diff --experimental +---- + +The run ends with a section of its own, and one URL per pipeline: + +[source,text] +---- +UDASH - EXPERIMENTAL +===================== + +Publishing report to Udash +my pipeline: + => "http://localhost/pipeline/reports/8f2b1c94-...." +---- + +[WARNING] +==== +Without `--experimental` nothing is published, no warning is printed, and the run reports success. +Configuring the endpoint is not enough on its own. See the +link:/docs/help/experimental/["Experimental features" page]. +==== + +Refresh `http://localhost` and the report is there. + +== Nothing to publish yet? + +If you have no manifest at hand, a published policy will do. Drop an `updatecli-compose.yaml` next +to a repository you care about: + +.updatecli-compose.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/quick-start/updatecli-compose.yaml" >}} +---- + +then run it, still without changing anything: + +[source,shell] +---- +updatecli compose diff --experimental +---- + +Pulling a policy from the GitHub Container Registry may require `docker login ghcr.io` first. See +link:/docs/core/compose/[Compose] for what else the file can express. + +== A note on discoverability + +The interface is organised per Git repository, and its filters are built on +link:/docs/core/label/[labels]. A pipeline that declares neither an `scmid` nor any label is stored +correctly but is awkward to find again. Add an `scm` to the pipelines you intend to watch before +you load a lot of reports. + +== Cleaning up + +[source,shell] +---- +docker compose down --volumes +---- + +== Go further + +* link:/docs/udash/installation/[Installation] - the same thing, deployed properly, with the Helm chart. +* link:/docs/udash/sending-reports/[Sending reports] - authentication, CI, and what a report carries. +* link:/docs/udash/dashboards/[Dashboards] - what to do with the reports once they are in. diff --git a/content/en/docs/udash/sending-reports.adoc b/content/en/docs/udash/sending-reports.adoc new file mode 100644 index 000000000..6ebf43628 --- /dev/null +++ b/content/en/docs/udash/sending-reports.adoc @@ -0,0 +1,164 @@ +--- +title: "Sending reports" +description: "How Updatecli publishes pipeline reports to Udash: the experimental flag, udash login, the environment variables, and what makes a report findable." +lead: "The Updatecli side of the integration" +date: 2026-08-07T10:00:00+02:00 +lastmod: 2026-09-23T10:00:00+02:00 +draft: false +images: [] +menu: + docs: + parent: "udash" +weight: 60 +toc: true +--- +// +:toc: +// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key +:toclevels: 4 + +== Description + +Nothing about a manifest changes when you publish to Udash. Updatecli runs as it always did, and at +the end of the run it POSTs each pipeline's report to the API. What you have to give it is where to +publish, whether it needs a token, and the `--experimental` flag. + +== The experimental flag + +[WARNING] +==== +Without `--experimental`, publishing is skipped *silently*. Updatecli prints no warning and no +error, and the run reports success. Configuring the endpoint, logging in, or setting every `UPDATECLI_UDASH_*` variable +changes nothing on its own. +==== + +With the flag, the run gains a section of its own: + +[source,text] +---- +UDASH - EXPERIMENTAL +===================== + +Publishing report to Udash +my pipeline: + => "https://udash.example.com/pipeline/reports/8f2b1c94-...." +---- + +That URL is the stored report, and it is also attached to the report itself, so it can appear in +whatever consumes Updatecli's output. See the link:/docs/help/experimental/["Experimental features" +page] for everything else the flag turns on. + +== Telling Updatecli where to publish + +Two ways, and they can be mixed. The environment wins over the configuration file. + +=== With `udash login` + +[source,shell] +---- +updatecli udash login "https://udash.example.com" --experimental +---- + +The command calls the API's `/whoami` endpoint first. An instance with authentication disabled +does not have that endpoint, so the command records the endpoint and asks for nothing. + +`--api-url` sets the API endpoint and defaults to the given URL with `/api` appended, which is +correct for a default deployment. Point it elsewhere when the API lives on its own hostname: + +[source,shell] +---- +updatecli udash login --api-url "https://api.example.com/api" "https://udash.example.com" --experimental +---- + +Against an authenticated instance, the command prompts for a Udash API token, or takes it from +`--token` or standard input, and validates it before saving it. See +link:/docs/udash/authentication/#_authenticating_updatecli[Authenticating Updatecli]. + +`udash login` also reads the `UPDATECLI_UDASH_*` variables below, and a flag wins over the matching +variable. + +The token and endpoints land in the Updatecli configuration file. `updatecli udash config` prints +its path, and `updatecli udash logout` removes the entry. + +=== With environment variables + +Better suited to CI, where there is no browser to redirect to. + +[cols="1,3", options="header"] +|=== +| Variable | Purpose + +| `UPDATECLI_UDASH_URL` +| The Udash instance URL, used to build the report links. + +| `UPDATECLI_UDASH_API_URL` +| The API URL. This is the one that decides whether anything is published at all. + +| `UPDATECLI_UDASH_ACCESS_TOKEN` +| The Udash API token, when the instance requires one. +|=== + +A variable that is set supersedes the corresponding value in the configuration file, and the choice +is logged at debug level. If `UPDATECLI_UDASH_API_URL` resolves to nothing, from either source, the +run logs `no Udash endpoint detected, skipping` and carries on. + +These are also listed on the link:/docs/help/environment/["Environment variables" page]. + +== In a CI pipeline + +Nothing beyond the two or three variables and the flag: + +[source,yaml] +---- +- name: Run Updatecli + env: + UPDATECLI_UDASH_URL: https://udash.example.com + UPDATECLI_UDASH_API_URL: https://udash.example.com/api + UPDATECLI_UDASH_ACCESS_TOKEN: ${{ secrets.UDASH_TOKEN }} + run: updatecli apply --experimental +---- + +See link:/docs/automate/github_action/[GitHub Actions] and link:/docs/automate/jenkins/[Jenkins] for +the surrounding job. + +== Making reports findable + +A report that arrives with no context is stored correctly and is then hard to retrieve. Two things +decide how easily you will find it again. + +=== An scm + +The interface is organised per Git repository. A pipeline that declares an `scm` and references it +through `scmid` is filed under that repository, appears on the Git dashboard, and can be compared +with the other pipelines touching the same code. A pipeline with none of that is only reachable +through the report list. + +=== Labels + +link:/docs/core/label/[Labels] are what the filters are built on, in Udash exactly as in +`--labels` locally. link:/docs/udash/labels/[Organising with labels] explains how to choose them so +they group pipelines into useful views. + +.updatecli.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/sending-reports/updatecli.yaml" >}} +---- + +[IMPORTANT] +==== +The label vocabularies suggested on the link:/docs/core/label/[Labels] page are recommendations. +Updatecli accepts any key and value, never validates or rewrites them, and never generates them for +you. Following the suggestions helps pipelines written by different people in different +repositories line up in the same filter. +==== + +One thing Udash does enforce: a label with an empty value is rejected when the report is stored. Omit +the label instead of giving it `""`. + +== Go further + +* link:/docs/udash/authentication/[Authentication] - publishing to an instance that requires a token. +* link:/docs/udash/dashboards/[Dashboards] - the filters these labels feed. +* link:/docs/commands/updatecli_udash_login/[`updatecli udash login`] - the full flag reference. +* link:/docs/udash/troubleshooting/[Troubleshooting] - when nothing arrives. diff --git a/content/en/docs/udash/troubleshooting.adoc b/content/en/docs/udash/troubleshooting.adoc new file mode 100644 index 000000000..be54cc068 --- /dev/null +++ b/content/en/docs/udash/troubleshooting.adoc @@ -0,0 +1,190 @@ +--- +title: "Troubleshooting" +description: "Symptoms you may hit running Udash, from reports that never arrive to tokens that are always rejected, with the cause and the fix for each." +lead: "When Udash does not behave" +date: 2026-08-07T10:00:00+02:00 +lastmod: 2026-09-23T10:00:00+02:00 +draft: false +images: [] +menu: + docs: + parent: "udash" + identifier: "udash-troubleshooting" +weight: 90 +toc: true +--- +// +:toc: +// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key +:toclevels: 4 + +== Description + +One symptom per section, with what causes it. For problems with Updatecli itself rather than with +Udash, see the link:/docs/help/troubleshooting/[Troubleshooting] page. + +== Nothing is published, and nothing is logged + +The run succeeds, the Udash section never appears, and there is no warning anywhere. + +`--experimental` is missing. Publishing requires it, whatever else is configured, and the skip is +silent. See link:/docs/help/experimental/["Experimental features"]. + +== `no Udash endpoint detected, skipping` + +Updatecli found no API URL, from either the configuration file or the environment. + +Check, in order: + +* `updatecli udash config` prints the configuration file path. Is there an entry, and does it carry + an `api` value? +* Is `UPDATECLI_UDASH_API_URL` set in this shell or job? + +Run with `--debug` to see which source was used. + +== Every token is rejected + +Requests that should be authorised come back rejected, and the logs say nothing useful about why. + +Almost always the issuer. It is compared to the token's `iss` claim verbatim, and the trailing slash +is part of the comparison: Auth0 issues one, Zitadel and Keycloak do not. Decode a real token from +your provider, read its `iss`, and copy that exact string into `auth.issuer`. + +Against Zitadel, also check the scope. Without the project audience scope +`urn:zitadel:iam:org:project:id::aud`, the provider issues a token that the API will +not accept. See link:/docs/udash/authentication/[Authentication]. + +== Signed in, but publishing is refused + +The API answers a report upload with `403` and `token is not allowed to perform this action`. + +The credential cannot write reports. Either the identity behind it is only a `viewer`, or it is an +API token without the `reports:write` scope. In `oidc` mode the first case applies to everyone when +`roles.claim` is empty, since no role is read at all. Set the claim and map a provider role to +`publisher`, or create a new token with the `reports:write` scope. + +Creating an API token on `/profile/tokens` also requires `publisher`, and a `viewer` gets +`insufficient permission` instead. See link:/docs/udash/authentication/#_permissions[Permissions]. + +== The server refuses to start after an upgrade + +`unknown authentication mode "oauth"` in the server log means the configuration still uses the +name the generic mode had before Udash v0.17.1. Rename `mode: oauth` to `mode: oidc`, and the +`oauth:` block holding `issuer` and `audience` to `oidc:`. The Helm chart fails with an explicit message for the same +reason. + +== Anyone can delete reports + +Expected, if no authentication mode is configured. Unset or `none` installs no middleware at all, +which leaves every endpoint open including the writes. + +Set `auth.enabled: true` with a mode, and pick a `visibility`. `public` keeps the dashboards +readable while requiring a token for anything that writes. + +== The server exits right after starting + +Usually PostgreSQL was not accepting connections yet when the server tried to connect. + +On Docker Compose, `docker compose restart server`. On Kubernetes the pod restarts on its own until +the database is ready, so a few restarts at install time are not a problem. + +If it persists, the URI is wrong or the database is unreachable. The connection string is logged at +debug level. + +== `failed calling webhook "mcluster.cnpg.io"` + +[source,text] +---- +failed calling webhook "mcluster.cnpg.io": no endpoints available for service "cnpg-webhook-service" +---- + +The CloudNative-PG operator is installed but its admission webhook is not serving yet, so the +`Cluster` the chart creates cannot be admitted. + +Install the operator with `--wait` before installing Udash, or simply retry the install once the +operator pod is ready. See link:/docs/udash/installation/[Installation]. + +== Migrations fail at startup + +The server runs its schema migrations before serving. A failure there stops it. + +If you migrate out of band, or you are rolling back to an older server against a newer schema, set +`database.migrationdisabled: true` to leave the schema alone. Be aware that a server running against +a schema it does not expect will fail in less obvious ways. + +== A blank page, or 404s on the assets + +Nearly always a subpath mismatch. Three things have to agree: + +. `ingress.paths.front`, where the ingress routes the frontend +. `front.appBasePath`, which the application uses to build its own URLs +. a strip-prefix, so nginx receives `/` rather than `/udash/` + +Miss the strip-prefix and the requests reach nginx with the prefix still attached. Miss +`appBasePath` and the application builds links from the root. See +link:/docs/udash/installation/#_subpath[Subpath routing]. + +== The interface loads but shows no data + +Open the browser console and look at where the API calls are going. + +* On split-domain routing, `front.apiBaseUrl` has to be an absolute URL. A relative `/api` sends the + calls to the frontend's own host, where nothing answers them. +* Check CORS, and that the API hostname resolves and has a valid certificate. +* `https:///api/ping` should return `pong`. + +== The API returns 400 + +The API bounds its own queries, and an integrator usually meets one of these first: + +[cols="2,3", options="header"] +|=== +| Message | Cause + +| `requested time range exceeds the maximum allowed span` +| More than 366 days between `start_time` and `end_time`. + +| `both start_time and end_time must be provided` +| Only one boundary was given. + +| `days and hours cannot be combined` +| Pick one window unit. + +| `requested time range and granularity produce too many buckets` +| More than 1000 buckets, for example an hourly granularity over a year. + +| `invalid pagination parameters` +| A page size above 1000, or a malformed `limit` or `page`. +|=== + +See link:/docs/udash/api/[API]. + +== Reports arrive but are hard to find + +The reports are stored, but nothing files them under a repository or a label. + +The interface is organised per Git repository and its filters are built on labels. A pipeline with +no `scmid` does not appear on the Git dashboard, and one with no labels matches no label filter. Add +an `scm` to the pipelines you intend to watch, and give them labels. See +link:/docs/udash/sending-reports/#_making_reports_findable[Making reports findable]. + +A label with an empty value is rejected outright when the report is stored. Omit the label rather +than giving it `""`. + +== Agent CronJobs run but nothing appears + +The jobs succeed, the database stays empty. + +* The agent publishes to the AgentRelay, not to the Udash server. Check `udash.json` in the agent's + ConfigMap points at `http://agentrelay/api`. +* The relay and the Udash server must share the same database. Compare + `secrets.database.stringdata.uri` in both releases. +* The agent's arguments must include `--experimental`, which the chart's defaults do carry. A custom + `images.agent.args` that drops it publishes nothing, silently. + +See link:/docs/udash/agent/[Agent]. + +== Go further + +* link:/docs/help/troubleshooting/[Updatecli troubleshooting] - for problems in the run itself. +* link:/support/[Support] - where to ask, and how to report a bug. From 9c417f32a4c396ff75b90c869b73862331e71d5b Mon Sep 17 00:00:00 2001 From: Olblak Date: Thu, 24 Sep 2026 09:25:57 +0200 Subject: [PATCH 2/4] chore: trim down udash documentation Signed-off-by: Olblak --- .../code_example/docs/udash/agent/values.yaml | 32 --- .../udash/authentication/values-oidc.yaml | 29 -- .../udash/authentication/values-zitadel.yaml | 19 -- .../docs/udash/configuration/config.json | 10 - .../docs/udash/configuration/config.yaml | 43 --- .../updatecli-compose.yaml | 0 .../udash/{labels => dashboards}/values.yaml | 0 .../installation/values-split-domain.yaml | 22 -- .../udash/installation/values-subpath.yaml | 22 -- .../docs/udash/installation/values.yaml | 27 -- .../docs/udash/labels/autodiscovery.yaml | 23 -- .../docs/udash/labels/updatecli.yaml | 38 --- content/en/docs/core/label.adoc | 2 +- content/en/docs/udash/_index.md | 35 +-- content/en/docs/udash/agent.adoc | 195 ------------- content/en/docs/udash/api.adoc | 203 ------------- content/en/docs/udash/authentication.adoc | 263 ----------------- content/en/docs/udash/configuration.adoc | 270 ------------------ content/en/docs/udash/dashboards.adoc | 169 +++++++---- content/en/docs/udash/installation.adoc | 170 ----------- content/en/docs/udash/introduction.adoc | 81 ++---- content/en/docs/udash/labels.adoc | 194 ------------- content/en/docs/udash/quick-start.adoc | 69 ++--- content/en/docs/udash/sending-reports.adoc | 152 +++++----- content/en/docs/udash/troubleshooting.adoc | 190 ------------ 25 files changed, 241 insertions(+), 2017 deletions(-) delete mode 100644 assets/code_example/docs/udash/agent/values.yaml delete mode 100644 assets/code_example/docs/udash/authentication/values-oidc.yaml delete mode 100644 assets/code_example/docs/udash/authentication/values-zitadel.yaml delete mode 100644 assets/code_example/docs/udash/configuration/config.json delete mode 100644 assets/code_example/docs/udash/configuration/config.yaml rename assets/code_example/docs/udash/{labels => dashboards}/updatecli-compose.yaml (100%) rename assets/code_example/docs/udash/{labels => dashboards}/values.yaml (100%) delete mode 100644 assets/code_example/docs/udash/installation/values-split-domain.yaml delete mode 100644 assets/code_example/docs/udash/installation/values-subpath.yaml delete mode 100644 assets/code_example/docs/udash/installation/values.yaml delete mode 100644 assets/code_example/docs/udash/labels/autodiscovery.yaml delete mode 100644 assets/code_example/docs/udash/labels/updatecli.yaml delete mode 100644 content/en/docs/udash/agent.adoc delete mode 100644 content/en/docs/udash/api.adoc delete mode 100644 content/en/docs/udash/authentication.adoc delete mode 100644 content/en/docs/udash/configuration.adoc delete mode 100644 content/en/docs/udash/installation.adoc delete mode 100644 content/en/docs/udash/labels.adoc delete mode 100644 content/en/docs/udash/troubleshooting.adoc diff --git a/assets/code_example/docs/udash/agent/values.yaml b/assets/code_example/docs/udash/agent/values.yaml deleted file mode 100644 index 5d65314fc..000000000 --- a/assets/code_example/docs/udash/agent/values.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# Credentials handed to every agent CronJob, stored in a Secret. -secrets: - agent: - environments: - GITHUB_TOKEN: "ghp_xxxxxxxxxxxxxxxxxxxx" - GITHUB_ACTOR: "my-github-user" - database: - stringdata: - # The AgentRelay writes to the same database as the Udash server. - uri: "postgres://udash:password@udash-postgres:5432/udash?sslmode=disable" - -# Applies to every agent that does not set its own schedule. -defaultSchedule: "0 * * * *" - -agents: - my-org-repos: - schedule: "*/30 * * * *" - # Each entry becomes /etc/updatecli/ inside the container. - valuesFiles: - scm.yaml: | - scm: - enabled: true - user: updatecli - owner: my-org - repository: my-repo - branch: main - composeFile: | - policies: - - name: Update Updatecli policies - policy: ghcr.io/updatecli/policies/autodiscovery/all - values: - - /etc/updatecli/scm.yaml diff --git a/assets/code_example/docs/udash/authentication/values-oidc.yaml b/assets/code_example/docs/udash/authentication/values-oidc.yaml deleted file mode 100644 index 163df72b9..000000000 --- a/assets/code_example/docs/udash/authentication/values-oidc.yaml +++ /dev/null @@ -1,29 +0,0 @@ -auth: - # Configures the server and the frontend at once. - enabled: true - mode: oidc - # public leaves the read endpoints open and requires a token for writes. - # private requires a token everywhere. - visibility: public - # Must match the "iss" claim of your provider exactly, trailing slash included. - issuer: https://example.eu.auth0.com/ - clientid: "xxxxxxxxxxxxxxxxxxxxxxxx" - audience: - - https://udash.example.com/api - # Empty requests "openid profile email offline_access". - scope: "" - roles: - # Without a claim, every signed-in user is a viewer and nobody can publish. - claim: "https://udash.example.com/roles" - -ingress: - enabled: true - hosts: - - host: udash.example.com - paths: - front: "/" - server: "/api" - -front: - apiBaseUrl: "/api" - appBasePath: "/" diff --git a/assets/code_example/docs/udash/authentication/values-zitadel.yaml b/assets/code_example/docs/udash/authentication/values-zitadel.yaml deleted file mode 100644 index e1749469d..000000000 --- a/assets/code_example/docs/udash/authentication/values-zitadel.yaml +++ /dev/null @@ -1,19 +0,0 @@ -auth: - enabled: true - mode: zitadel - visibility: public - # Used by the frontend for OIDC discovery. - issuer: https://my-instance.region.zitadel.cloud - clientid: "123456789012345678@udash" - audience: - - https://udash.example.com/api - # Zitadel rejects the token at the API unless the project audience scope is requested. - scope: "openid profile email offline_access urn:zitadel:iam:org:project:id:123456789012345678:aud" - zitadel: - domain: my-instance.region.zitadel.cloud - keyFile: - # Reference a Secret you manage yourself rather than inlining the key. - existingSecret: udash-zitadel-key - key: key.json - # No roles block needed: zitadel mode reads the project roles claim by default, - # and grants publisher to the udash.publisher project role. diff --git a/assets/code_example/docs/udash/configuration/config.json b/assets/code_example/docs/udash/configuration/config.json deleted file mode 100644 index b85fd0859..000000000 --- a/assets/code_example/docs/udash/configuration/config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "AUTH_ENABLED": true, - "AUTH_VISIBILITY": "public", - "OAUTH_DOMAIN": "https://example.eu.auth0.com/", - "OAUTH_CLIENTID": "xxxxxxxxxxxxxxxxxxxxxxxx", - "OAUTH_SCOPE": "openid profile email offline_access", - "API_BASE_URL": "/api", - "APP_BASE_PATH": "/", - "MAX_HISTORY_DAYS": 30 -} diff --git a/assets/code_example/docs/udash/configuration/config.yaml b/assets/code_example/docs/udash/configuration/config.yaml deleted file mode 100644 index 463ae522f..000000000 --- a/assets/code_example/docs/udash/configuration/config.yaml +++ /dev/null @@ -1,43 +0,0 @@ -server: - auth: - # mode selects how incoming tokens are validated. - # "oidc", "zitadel", or "none". Unset behaves like "none". - # An unrecognised value stops the server. - mode: "oidc" - # visibility controls which endpoints require a token. - # "public" leaves the reads open, "private" locks everything. - visibility: "public" - oidc: - # Compared to the token "iss" claim verbatim, trailing slash included. - issuer: "https://example.eu.auth0.com/" - # A list: every entry is an accepted audience. - audience: - - "https://udash.example.com/api" - zitadel: - domain: "my-instance.region.zitadel.cloud" - keyfile: "/etc/udash/zitadel/key.json" - roles: - # Token claim holding the provider roles. Required in "oidc" mode for - # anyone to get more than the default permission. - claim: "https://udash.example.com/roles" - # Provider roles granting each Udash permission. - mapping: - admin: ["udash.admin"] - publisher: ["udash.publisher"] - viewer: ["udash.viewer"] - # Granted to an authenticated identity matching no role. - default: "viewer" - # "snapshot" or "zitadel", see the Authentication page. - resolver: "snapshot" - cachettl: "60s" - -database: - uri: "postgres://udash:password@db:5432/udash?sslmode=disable" - # Set to true to skip the schema migrations run at startup. - migrationdisabled: false - -gc: - # Days of reports to keep. 0, the default, keeps everything. - maxHistoryDays: 0 - interval: "24h" - batchSize: 5000 diff --git a/assets/code_example/docs/udash/labels/updatecli-compose.yaml b/assets/code_example/docs/udash/dashboards/updatecli-compose.yaml similarity index 100% rename from assets/code_example/docs/udash/labels/updatecli-compose.yaml rename to assets/code_example/docs/udash/dashboards/updatecli-compose.yaml diff --git a/assets/code_example/docs/udash/labels/values.yaml b/assets/code_example/docs/udash/dashboards/values.yaml similarity index 100% rename from assets/code_example/docs/udash/labels/values.yaml rename to assets/code_example/docs/udash/dashboards/values.yaml diff --git a/assets/code_example/docs/udash/installation/values-split-domain.yaml b/assets/code_example/docs/udash/installation/values-split-domain.yaml deleted file mode 100644 index a64f71831..000000000 --- a/assets/code_example/docs/udash/installation/values-split-domain.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Split-domain routing: the API answers on its own hostname. -# A second Ingress is created for ingress.server.host, and the browser needs an -# absolute front.apiBaseUrl because the API is no longer same-origin. -ingress: - enabled: true - hosts: - - host: udash.example.com - paths: - front: "/" - server: "/api" - server: - host: api.example.com - # The server serves /api natively, so nothing needs rewriting here. - path: "/api" - tls: - - secretName: udash-api-tls - hosts: - - api.example.com - -front: - apiBaseUrl: "https://api.example.com/api" - appBasePath: "/" diff --git a/assets/code_example/docs/udash/installation/values-subpath.yaml b/assets/code_example/docs/udash/installation/values-subpath.yaml deleted file mode 100644 index 48c33e525..000000000 --- a/assets/code_example/docs/udash/installation/values-subpath.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Subpath routing: the frontend lives under /udash on a shared hostname. -# ingress.paths.front and front.appBasePath must carry the same value, and the -# prefix has to be stripped before the request reaches nginx. -# -# The API keeps its own top-level /api path: the server only ever serves /api, -# and nothing rewrites the path on same-host routing. -ingress: - enabled: true - hosts: - - host: tools.example.com - paths: - front: "/udash" - server: "/api" - traefik: - stripPrefix: - # Creates a StripPrefix Middleware for /udash and wires its annotation. - # Requires the Traefik CRDs (traefik.io/v1alpha1) in the cluster. - enabled: true - -front: - apiBaseUrl: "/api" - appBasePath: "/udash" diff --git a/assets/code_example/docs/udash/installation/values.yaml b/assets/code_example/docs/udash/installation/values.yaml deleted file mode 100644 index 4997d95b5..000000000 --- a/assets/code_example/docs/udash/installation/values.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Same-host routing: the frontend on / and the API on /api, one hostname. -ingress: - enabled: true - className: nginx - hosts: - - host: udash.example.com - tls: - - secretName: udash-tls - hosts: - - udash.example.com - paths: - front: "/" - server: "/api" - -front: - # Relative, because the API is reachable on the same host. - apiBaseUrl: "/api" - appBasePath: "/" - maxHistoryDays: 30 - -cnpg: - enabled: true - instances: 1 - database: udash - owner: udash - storage: - size: 10Gi diff --git a/assets/code_example/docs/udash/labels/autodiscovery.yaml b/assets/code_example/docs/udash/labels/autodiscovery.yaml deleted file mode 100644 index ad2c64438..000000000 --- a/assets/code_example/docs/udash/labels/autodiscovery.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: Discover Go module updates -pipelineid: go/autodiscovery - -# Copied onto every pipeline the crawler generates, and stored with every report. -labels: - team: platform - ecosystem: go - monitor: passive - -# The scm is what files these reports under a Git repository on the Git dashboard. -scms: - default: - kind: github - spec: - owner: my-org - repository: my-service - branch: main - token: '{{ requiredEnv "GITHUB_TOKEN" }}' - -autodiscovery: - scmid: default - crawlers: - golang: diff --git a/assets/code_example/docs/udash/labels/updatecli.yaml b/assets/code_example/docs/udash/labels/updatecli.yaml deleted file mode 100644 index 92a174db6..000000000 --- a/assets/code_example/docs/udash/labels/updatecli.yaml +++ /dev/null @@ -1,38 +0,0 @@ -name: Bump the base image -pipelineid: docker/base-image - -# Stored with every report, and offered by the Udash label filter. -labels: - team: platform - ecosystem: docker - -# The scm files the reports under this Git repository in Udash. -scms: - default: - kind: github - spec: - owner: my-org - repository: my-service - branch: main - token: '{{ requiredEnv "GITHUB_TOKEN" }}' - -sources: - alpine: - name: Get the latest Alpine version - kind: dockerimage - spec: - image: alpine - versionfilter: - kind: semver - -targets: - dockerfile: - name: Update the Alpine base image - kind: dockerfile - scmid: default - sourceid: alpine - spec: - file: Dockerfile - instruction: - keyword: FROM - matcher: alpine diff --git a/content/en/docs/core/label.adoc b/content/en/docs/core/label.adoc index c65e58b6b..d29743f19 100644 --- a/content/en/docs/core/label.adoc +++ b/content/en/docs/core/label.adoc @@ -49,7 +49,7 @@ updatecli compose apply --labels="ecosystem:go" + Pipelines matching the filter will execute. Pipelines without the label or with a different value will be skipped. -2. **Filtering pipeline reports** in link:/docs/udash/[Udash], where labels group pipelines from many repositories into one view. See link:/docs/udash/labels/[Organising with labels]. +2. **Filtering pipeline reports** in link:/docs/udash/[Udash], where labels group pipelines from many repositories into one view. See link:/docs/udash/dashboards/#_filtering_by_label[Filtering by label]. == Filtering with `--labels` diff --git a/content/en/docs/udash/_index.md b/content/en/docs/udash/_index.md index 147cf8c2a..886c5ddaf 100644 --- a/content/en/docs/udash/_index.md +++ b/content/en/docs/udash/_index.md @@ -1,9 +1,9 @@ --- title: "Udash" -description: "Deploy Udash, the Updatecli dashboard, and publish your pipeline reports to it for a central view of update activity across every repository." +description: "Publish your Updatecli pipeline reports to Udash, the Updatecli dashboard, and follow update activity across every repository." lead: "" date: 2026-08-07T10:00:00+02:00 -lastmod: 2026-08-07T10:00:00+02:00 +lastmod: 2026-09-24T10:00:00+02:00 draft: false images: [] sidebar: @@ -11,26 +11,17 @@ sidebar: --- Updatecli tells you what changed on one run, in one repository. [Udash](https://github.com/updatecli/udash) -collects those runs and keeps them, so you can see the same information across every repository and -over time. +keeps those runs, so you can see the same information across every repository and over time. {{< alert icon="⚠️" text="Udash is experimental, and so is the Updatecli side of it. Publishing reports requires the --experimental flag, and both the API and the interface can change without going through the usual deprecation cycle." >}} -- [Introduction](/docs/udash/introduction/) - what Udash is, what it stores, and how the pieces fit - together. -- [Quick start](/docs/udash/quick-start/) - run the whole stack locally with Docker Compose and - publish your first report. -- [Installation](/docs/udash/installation/) - deploy it for real with the Helm chart, including the - three ingress topologies. -- [Agent](/docs/udash/agent/) - run Updatecli on a schedule inside your cluster instead of from each - repository's CI. -- [Configuration](/docs/udash/configuration/) - every setting of the server and of the frontend. -- [Authentication](/docs/udash/authentication/) - the authentication modes, API visibility, and how - to register the application with your provider. -- [Sending reports](/docs/udash/sending-reports/) - the Updatecli side, from `udash login` to what - ends up in a report. -- [Organising with labels](/docs/udash/labels/) - group pipelines from many repositories into one - view with Updatecli labels. -- [Dashboards](/docs/udash/dashboards/) - a tour of the interface and its filters. -- [API](/docs/udash/api/) - the endpoints, and the limits an integrator meets first. -- [Troubleshooting](/docs/udash/troubleshooting/) - symptoms, causes, and fixes. +- [Introduction](/docs/udash/introduction/) - what Udash is and how Updatecli feeds it. +- [Quick start](/docs/udash/quick-start/) - run Udash locally with Docker Compose and publish your + first report. +- [Sending reports](/docs/udash/sending-reports/) - connect Updatecli to an instance, locally or + from CI. +- [Dashboards](/docs/udash/dashboards/) - find your pipelines, filter them with labels, and share a + view. + +Deploying, configuring, and securing a Udash instance, the agent, and the API are documented in the +[updatecli/udash](https://github.com/updatecli/udash) repository. diff --git a/content/en/docs/udash/agent.adoc b/content/en/docs/udash/agent.adoc deleted file mode 100644 index fd26b2536..000000000 --- a/content/en/docs/udash/agent.adoc +++ /dev/null @@ -1,195 +0,0 @@ ---- -title: "Agent" -description: "Run Updatecli on a schedule inside your Kubernetes cluster with the udash-agent chart, instead of relying on each repository's CI to report." -lead: "Updatecli as a scheduled job in your cluster" -date: 2026-08-07T10:00:00+02:00 -lastmod: 2026-09-23T10:00:00+02:00 -draft: false -images: [] -menu: - docs: - parent: "udash" -weight: 35 -toc: true ---- -// -:toc: -// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key -:toclevels: 4 - -== Description - -The usual way to fill a Udash instance is for each repository's CI to run Updatecli and publish its -report. That works, but every repository has to be set up first, which is a lot of pipelines to -touch before the dashboard is worth looking at. - -The `udash-agent` chart takes the other route. It runs Updatecli itself, on a schedule, from inside -the cluster, against whichever repositories you list. Nothing has to change in those repositories. - -Both approaches write into the same database and appear side by side in the interface, so adopting -one does not rule out the other. - -=== Requirement - -. A Udash instance and its PostgreSQL database, see link:/docs/udash/installation/[Installation] -. Kubernetes 1.19 or later, and Helm 3 -. Credentials for whatever the agents will read, typically a GitHub token -. Argo Workflows, only for the optional workflow mode - -== Architecture - -[cols="1,3", options="header"] -|=== -| Component | Role - -| `agentrelay` -| A Udash server deployed *without authentication*, reachable only inside the cluster at -`http://agentrelay/api`. The agents publish to it, and it writes to the same PostgreSQL as the -Udash server users actually browse. - -| `agent-` -| One Kubernetes `CronJob` per entry in `agents`, running `updatecli compose diff --experimental` -from the `ghcr.io/updatecli/updatecli` image. - -| Argo `CronWorkflow` -| Optional. One per entry in `workflows`, cloning a Git repository and running Updatecli against the -compose file it finds there. -|=== - -[WARNING] -==== -The relay exists so agents do not need tokens to publish. It runs with `auth.mode: none`, which means -anything that can reach it can also delete reports. Keep it on a `ClusterIP` service, do not put an -ingress in front of it, and treat network access to it as write access to your report history. -==== - -== Installing - -[source,shell] ----- -helm repo add updatecli https://updatecli.github.io/charts -helm repo update -helm install udash-agent updatecli/udash-agent -f values.yaml ----- - -.values.yaml -[source,yaml] ----- -{{< include "assets/code_example/docs/udash/agent/values.yaml" >}} ----- - -`secrets.database.stringdata.uri` has to be the same database the Udash server uses. That is the -only thing tying the two releases together; get it wrong and the agents will happily write into a -database nobody is reading. - -No agent is created by default. An install with an empty `agents` map deploys only the relay. - -== Defining an agent - -Each key under `agents` becomes one CronJob. Everything about it falls back to a chart-wide default, -so a minimal agent is just a name. - -[cols="1,3", options="header"] -|=== -| Field | Meaning - -| `schedule` -| Cron expression for this agent. Falls back to `defaultSchedule`, hourly. - -| `composeFile` -| The Updatecli compose file, mounted at `/etc/updatecli/updatecli-compose.yaml`. Falls back to -`defaultComposeFile`, which runs the `autodiscovery/all` policy. - -| `valuesFiles` -| A map of filename to content. Each entry becomes `/etc/updatecli/` in the container, -which is how the compose file's `values:` references resolve. - -| `udashConfig` -| The Updatecli credentials file, mounted at `/home/updatecli/.config/updatecli/udash.json`. Falls -back to `defaultUdashConfig`, which points at the relay. -|=== - -The default `udash.json` is what connects an agent to the relay: - -[source,json] ----- -{ - "Auths": { - "agentrelay": { - "Api": "http://agentrelay/api", - "URL": "http://localhost:3030" - } - }, - "Default": "agentrelay" -} ----- - -`Api` is where reports go. `URL` only builds the links printed in the job log, so it does not have -to resolve from inside the cluster, though pointing it at your real Udash hostname makes those logs -more useful. - -== Credentials - -Every key under `secrets.agent.environments` is stored in a Secret and injected as an environment -variable into every agent container: - -[source,yaml] ----- -secrets: - agent: - environments: - GITHUB_TOKEN: "ghp_xxxxxxxxxxxxxxxxxxxx" - GITHUB_ACTOR: "my-github-user" ----- - -These are the ordinary Updatecli variables, so anything on the -link:/docs/help/environment/["Environment variables" page] can go here. They are shared by all -agents; there is no per-agent override. - -== Argo Workflows - -The `workflows` list is an alternative to `agents` for cases where the compose file already lives in -the repository. Each entry creates a `CronWorkflow` that clones the repository, runs -`updatecli compose diff --experimental` against the named compose file, and publishes to the relay. - -[source,yaml] ----- -workflows: - - url: "https://github.com/updatecli/udash.git" - branch: "main" - composefile: "updatecli-compose.yaml" ----- - -[NOTE] -==== -The chart renders the Argo `WorkflowTemplate`, `ServiceAccount`, `Role`, and `RoleBinding` -unconditionally, even when `workflows` is empty. If Argo's CRDs are not installed, the install -fails on the `WorkflowTemplate` rather than skipping it. - -The `RoleBinding` also references the service account in a namespace fixed to `udash`, so installing -this chart into a different namespace leaves the binding pointing at nothing and the workflows -unable to run. Install into `udash`, or patch the binding afterwards. -==== - -== Verifying it works - -[source,shell] ----- -# The CronJobs exist and have a schedule -kubectl get cronjobs - -# Trigger one immediately rather than waiting -kubectl create job --from=cronjob/udash-agent-agent-my-org-repos manual-run - -# Follow it -kubectl logs -f job/manual-run ----- - -The log ends with the same `UDASH - EXPERIMENTAL` section a local run prints. If it does not, see -link:/docs/udash/troubleshooting/#_agent_cronjobs_run_but_nothing_appears[Troubleshooting]. - -== Go further - -* link:/docs/udash/installation/[Installation] - the Udash instance the agents feed. -* link:/docs/core/compose/[Compose] - what a compose file can express. -* link:/docs/udash/sending-reports/[Sending reports] - the CI-side alternative. diff --git a/content/en/docs/udash/api.adoc b/content/en/docs/udash/api.adoc deleted file mode 100644 index 5f2e8d9cb..000000000 --- a/content/en/docs/udash/api.adoc +++ /dev/null @@ -1,203 +0,0 @@ ---- -title: "API" -description: "The Udash HTTP API: where the generated reference lives, what the endpoint groups are, and the query limits an integrator meets first." -lead: "Talking to Udash directly" -date: 2026-08-07T10:00:00+02:00 -lastmod: 2026-09-23T10:00:00+02:00 -draft: false -images: [] -menu: - docs: - parent: "udash" -weight: 80 -toc: true ---- -// -:toc: -// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key -:toclevels: 4 - -== Description - -Everything the interface does, it does through the API, so anything it shows can be pulled out -programmatically. - -The authoritative reference is generated from the source and served by every instance at -`/swagger/index.html`. This page is the orientation around it: which groups exist, which are reads -and which are writes, and the limits that will produce your first `400`. - -== Base path and health - -Everything lives under `/api/`. - -[cols="1,3", options="header"] -|=== -| Endpoint | Purpose - -| `GET /api/ping` -| Returns `pong`. The liveness check. - -| `GET /api/about` -| The API version, the Go version it was built with, and the build time. - -| `GET /swagger/index.html` -| The generated reference for this exact instance. -|=== - -== Endpoint groups - -[cols="2,1,3", options="header"] -|=== -| Group | Kind | Purpose - -| `GET /api/pipeline/reports` -| read -| List reports. See `latest` below. - -| `GET /api/pipeline/reports/{id}` -| read -| One report, in full. - -| `POST /api/pipeline/reports/search` -| read -| Search reports with a filter too structured for a query string. This is what the interface calls. - -| `POST /api/pipeline/reports/summary` -| read -| Aggregate reports into time buckets. Backs the activity chart. - -| `POST /api/pipeline/reports` -| write -| Create a report. This is the endpoint Updatecli publishes to. - -| `PUT /api/pipeline/reports/{id}` -| write -| Replace a report. - -| `DELETE /api/pipeline/reports/{id}` -| write -| Delete a report. - -| `GET /api/pipeline/labels`, `POST /api/pipeline/labels/search` -| read -| The labels seen across stored reports, which is what populates the label filter. - -| `GET /api/pipeline/scms`, `POST /api/pipeline/scms/search` -| read -| The Git repositories and branches seen across stored reports. - -| `GET /api/pipeline/config/{sources,conditions,targets}` and their `/search` -| read -| The resource configurations extracted from reports, so you can ask which pipelines use a given -resource kind. - -| `GET /api/pipeline/config/kinds` -| read -| The distinct resource kinds present. - -| `GET /api/whoami` -| read -| The identity, permission, and token scopes behind the credential used. Only exists when an -authentication mode is configured. Updatecli calls it to validate a token at login. - -| `GET /api/tokens` -| read -| List API tokens. - -| `POST /api/tokens` -| write -| Create an API token. Requires the `publisher` permission and a provider token, never an API token. - -| `DELETE /api/tokens/{id}`, `DELETE /api/tokens` -| write -| Revoke one token, or every token of an identity. The second form requires `admin`. -|=== - -[NOTE] -==== -Several reads are `POST` requests, because their filters do not fit in a query string. That does not -make them writes, and the visibility rules treat them as reads. See -link:/docs/udash/authentication/[Authentication]. -==== - -== Authentication - -Which endpoints need a token depends entirely on how the instance is configured: - -* no mode configured, the default: nothing requires a token, including the writes -* `visibility: public`: reads are open, writes need a token -* `visibility: private`: everything needs a token - -A token alone is not enough to write a report. The identity behind it needs the `publisher` -permission, and an API token needs the `reports:write` scope. The `/api/tokens` endpoints always -require authentication, whatever the visibility. - -Pass the token as `Authorization: Bearer `. See link:/docs/udash/authentication/[Authentication] for -obtaining one. - -== `latest` - -`GET /api/pipeline/reports` takes a `latest` parameter, defaulting to true, which returns only the -most recent report per pipeline ID. - -Left alone, you get the current state, one row per pipeline, which is what a dashboard needs. Set -to false, you get the history, every run of every pipeline, which is what a trend needs and is -considerably more data. - -== Pagination - -List and search endpoints take `limit` and `page`, and return the total count alongside the results, -so you can tell whether you are looking at everything. - -`limit` may not exceed *1000*. Beyond that the request is rejected rather than quietly truncated. - -== Time windows and their limits - -The API bounds its own queries, because the aggregations run over every matching row and an -unbounded range means scanning most of the table. - -[cols="2,1,3", options="header"] -|=== -| Rule | Limit | Error - -| Default search window when none is given -| 7 days -| n/a - -| Maximum span between `start_time` and `end_time` -| 366 days -| `requested time range exceeds the maximum allowed span` - -| Both boundaries required together -| n/a -| `both start_time and end_time must be provided` - -| `days` and `hours` are mutually exclusive -| n/a -| `days and hours cannot be combined` - -| Buckets a summary may return -| 1000 -| `requested time range and granularity produce too many buckets` - -| Records per page -| 1000 -| `invalid pagination parameters` -|=== - -The bucket limit depends on the granularity as well as the range: a year at daily granularity is -accepted, while a year at hourly granularity is more than eight thousand buckets and is refused. - -== Summaries - -`POST /api/pipeline/reports/summary` returns counts per time bucket. Each bucket carries its start -in UTC as RFC3339, a total, and a breakdown per Updatecli result. - -The only metric supported so far is `result`. Granularity and the time window are the parameters -you vary. - -== Go further - -* `/swagger/index.html` on your instance - the complete, generated reference. -* link:/docs/udash/authentication/[Authentication] - what a token is needed for. -* link:/docs/udash/troubleshooting/[Troubleshooting] - the `400` responses, in one table. diff --git a/content/en/docs/udash/authentication.adoc b/content/en/docs/udash/authentication.adoc deleted file mode 100644 index b8271e4be..000000000 --- a/content/en/docs/udash/authentication.adoc +++ /dev/null @@ -1,263 +0,0 @@ ---- -title: "Authentication" -description: "The Udash authentication modes, what API visibility changes, how to register the application with an OIDC provider, and how Updatecli authenticates against it." -lead: "Locking down a Udash instance" -date: 2026-08-07T10:00:00+02:00 -lastmod: 2026-09-23T10:00:00+02:00 -draft: false -images: [] -menu: - docs: - parent: "udash" -weight: 50 -toc: true ---- -// -:toc: -// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key -:toclevels: 4 - -== Description - -Authentication is off by default. With no mode configured the server installs no authentication -middleware at all, so anyone who can reach the API can read, create, and delete reports. That is fine -for the link:/docs/udash/quick-start/[quick start] on a laptop, and for nothing else. - -Turning it on means two things agreeing with each other: the server validating tokens, and the -frontend obtaining them. The Helm chart configures both from one `auth` block. - -== Modes - -[cols="1,3", options="header"] -|=== -| Mode | Behaviour - -| `none`, or unset -| No middleware. Every endpoint is open, including the ones that write. - -| `oidc` -| Generic OpenID Connect. Tokens are validated locally as JWTs against the issuer's signing keys -and the configured audience, so only JWT access tokens are accepted. Works with any compliant -provider, Zitadel included. - -| `zitadel` -| Validates tokens by introspection against link:https://zitadel.com[Zitadel], which also accepts -opaque tokens such as Zitadel personal access tokens. Needs a service account key. -|=== - -Any other value stops the server at startup. That includes `oauth`, the name of the generic mode -before Udash v0.17.1, and the Helm chart refuses to render it with a message pointing at the -rename. - -== Visibility - -Whichever mode is set, `visibility` decides how much of the API the token is needed for. - -[cols="1,3", options="header"] -|=== -| Value | Effect - -| `public` -| The default. `GET`, `HEAD`, and `OPTIONS` stay open to anyone. Everything else requires a valid -token. - -| `private` -| Every endpoint requires a valid token. -|=== - -`public` suits an instance whose dashboards are meant to be readable by the whole engineering -organisation while only CI may write into it. - -The frontend has its own `AUTH_VISIBILITY` setting, which defaults to `private`. It has to match -the server's value, otherwise a `public` instance still sends anonymous visitors to the login page. -The chart writes both from `auth.visibility`. - -[NOTE] -==== -Several read operations are `POST` requests, because their filters do not fit in a query string: -the `/search` endpoints and `reports/summary`. Under `public` visibility they are served outside the -authenticated group, so they stay readable like any `GET`. -==== - -== Permissions - -A valid token lets a request through, and the permission behind it decides what the request may do. - -[cols="1,3", options="header"] -|=== -| Permission | Grants - -| `viewer` -| Reading pipeline reports. - -| `publisher` -| Publishing, replacing, and deleting reports, and creating API tokens. - -| `admin` -| Everything, plus managing the API tokens of any identity. -|=== - -Permissions come from the roles your provider puts in the token. `roles.claim` names the claim that -holds them, and `roles.mapping` lists the provider roles that grant each permission, by default -`udash.admin`, `udash.publisher`, and `udash.viewer`. Both shapes providers use are accepted: an -object keyed by role name, as Zitadel emits, and an array of strings, as Keycloak and Auth0 emit. - -A signed-in identity that matches no role gets `roles.default`, which is `viewer` unless you change -it. - -[IMPORTANT] -==== -In `oidc` mode `roles.claim` has no default. Leave it empty and no roles are read at all, so every -user is a `viewer` and nobody can publish a report or create an API token. Set it to the claim your -provider uses, for example `realm_access.roles` on Keycloak or a namespaced claim such as -`https://udash.example.com/roles` on Auth0. - -In `zitadel` mode it defaults to `urn:zitadel:iam:org:project:roles`, so granting the -`udash.publisher` project role is enough. -==== - -== Configuring it with the chart - -The chart's `auth` block feeds both the server's `config.yaml` and the frontend's `config.json`. - -.values.yaml -[source,yaml] ----- -{{< include "assets/code_example/docs/udash/authentication/values-oidc.yaml" >}} ----- - -The chart refuses to render rather than deploying something silently open: an unrecognised -`auth.mode`, `auth.visibility`, or `auth.roles.default`, a missing `auth.issuer` in `oidc` mode, or -a missing `auth.zitadel.domain` in `zitadel` mode all fail the template with an explicit message. - -Doing it without the chart means writing the same values into both files by hand, see -link:/docs/udash/configuration/[Configuration]. - -== The issuer, and its trailing slash - -`auth.issuer` may be given with or without a scheme, and `https://` is assumed when it is omitted. -Beyond that it is used verbatim, and the validator compares it to the token's `iss` claim exactly. -It is the most common reason an otherwise correct setup rejects every request. - -[IMPORTANT] -==== -The trailing slash is significant. Auth0 issues an `iss` with one, Zitadel and Keycloak do not. Get -it wrong and every token is rejected, with nothing in the logs pointing at the slash. - -Read the `iss` claim out of a real token from your provider and copy it exactly. -==== - -== Registering the application - -Register the frontend as a *User Agent* or *SPA* client, using *PKCE*. The provider must support the -Authorization Code with PKCE flow; Udash does not use any other. - -The redirect URI is not configurable. It is derived from the browser origin and `front.appBasePath`, -so register `https://` as *both* the allowed redirect URI and the -post-logout redirect URI. `helm install` prints the exact URL for your values. - -=== Scopes - -Left empty, the frontend requests `openid profile email offline_access`. Keep `offline_access`: -it is what lets the session renew silently instead of bouncing the user back to the provider. - -Zitadel needs one more. Without the project audience scope, the token is issued but the API rejects -it: - -[source,yaml] ----- -{{< include "assets/code_example/docs/udash/authentication/values-zitadel.yaml" >}} ----- - -=== Profile fields - -The interface reads the profile from the provider's userinfo endpoint and merges it over the ID -token's own claims, because Zitadel omits `email`, `name`, `preferred_username`, and `picture` from -the ID token unless the application opts in. A userinfo failure is not fatal: the session survives -and simply shows a sparser profile. - -== API tokens - -A provider's access token always expires, and an unattended pipeline needs a credential it can -keep. Udash therefore issues its own API tokens and validates them itself. They never expire unless -an expiry is set when they are created. - -Anyone with the `publisher` permission creates them from *Profile* then *Tokens* in the interface, -at `/profile/tokens`. The token is shown once: only its SHA-256 hash is stored. Tokens start with -`udash_pat_`, which tells them apart from a provider token and lets secret scanners recognise one -that leaks. - -Each token carries scopes, `reports:read` and `reports:write`, chosen at creation and never wider -than what its creator is allowed. No scope allows managing tokens, and creating a token requires -signing in through the provider, so a leaked token cannot be used to create new ones. - -=== When a permission changes - -A token has no provider token of its own to read roles from, so `roles.resolver` decides where its -permission comes from: - -* `snapshot`, the default outside `zitadel` mode, trusts the permission recorded when the token was - created. Removing someone's role does not affect the tokens they already made, so offboarding - someone means deleting their tokens. -* `zitadel`, the default in `zitadel` mode, asks Zitadel for the creator's current grants, so a - revoked role also stops their tokens. The service user behind the key file must be allowed to - read user grants. - -A resolved permission is reused for `roles.cachettl`, 60 seconds by default, so a pipeline -publishing many reports does not query the provider for each one. - -=== Zitadel personal access tokens - -Because `zitadel` mode validates tokens by introspection, a Zitadel personal access token on a -machine user works as a permanent credential with no Udash-side setup. Create a service user in -Zitadel, grant it the project role mapped to `publisher`, and create a personal access token with -no expiration. - -Every request then costs an introspection round trip to Zitadel, and creating such a token needs -Zitadel administrator rights, so it does not scale to letting each team issue its own. - -== Authenticating Updatecli - -Once the instance requires a token, Updatecli needs one too. - -=== Interactively - -[source,shell] ----- -updatecli udash login "https://udash.example.com" --experimental ----- - -The command first calls `/whoami`. On an instance with authentication enabled, it prints the -`/profile/tokens` address, opens it in a browser, and prompts for the token. It validates the token -against the API before writing it to the Updatecli configuration file, so a wrong token fails here -rather than on the first publish. - -When standard input is not a terminal, the token is read from it: - -[source,shell] ----- -echo "$UDASH_TOKEN" | updatecli udash login "https://udash.example.com" --experimental ----- - -`--token` passes it as a flag instead, and `--api-url` sets the API endpoint when it is not the -given URL with `/api` appended. - -=== In CI - -Hand the token over through the environment: - -[source,shell] ----- -export UPDATECLI_UDASH_URL="https://udash.example.com" -export UPDATECLI_UDASH_API_URL="https://udash.example.com/api" -export UPDATECLI_UDASH_ACCESS_TOKEN="udash_pat_..." -updatecli apply --experimental ----- - -The token needs the `reports:write` scope, which a new token gets unless other scopes are chosen. - -== Go further - -* link:/docs/udash/configuration/[Configuration] - the underlying keys, for a deployment without the chart. -* link:/docs/udash/sending-reports/[Sending reports] - publishing once the token is in place. -* link:/docs/udash/api/[API] - which endpoints the visibility setting covers. diff --git a/content/en/docs/udash/configuration.adoc b/content/en/docs/udash/configuration.adoc deleted file mode 100644 index 59e1f84b0..000000000 --- a/content/en/docs/udash/configuration.adoc +++ /dev/null @@ -1,270 +0,0 @@ ---- -title: "Configuration" -description: "Every Udash setting: the server configuration file and its environment fallbacks, and the runtime config.json that configures the frontend." -lead: "Configuring the server and the frontend" -date: 2026-08-07T10:00:00+02:00 -lastmod: 2026-09-23T10:00:00+02:00 -draft: false -images: [] -menu: - docs: - parent: "udash" - identifier: "udash-configuration" -weight: 40 -toc: true ---- -// -:toc: -// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key -:toclevels: 4 - -== Description - -Udash has two configuration surfaces that do not overlap. The server reads a YAML file, the frontend -reads a JSON file at runtime in the browser. Neither knows about the other, so an authenticated -deployment has to be told the same thing twice. The link:/docs/udash/installation/[Helm chart] -does that for you from a single `auth` block. - -== The server - -=== Where the file is read from - -The file is named `config.yaml` and is looked up in this order: - -. the working directory -. `$HOME/.udash/` -. `/etc/udash/` - -The first one found wins. `--config` takes the path of a different file instead, and that file -then has to exist. The file is watched, and a change is logged, so a `ConfigMap` update does not go -unnoticed. - -=== Settings - -.config.yaml -[source,yaml] ----- -{{< include "assets/code_example/docs/udash/configuration/config.yaml" >}} ----- - -[cols="1,3", options="header"] -|=== -| Key | Meaning - -| `server.auth.mode` -| `oidc`, `zitadel`, or `none`. Unset behaves like `none`, which means no authentication middleware -at all. Any other value, including the former `oauth`, stops the server at startup. See -link:/docs/udash/authentication/[Authentication]. - -| `server.auth.visibility` -| `public` or `private`. Defaults to `public`. Only meaningful when a mode is set. - -| `server.auth.oidc.issuer` -| The OIDC issuer, compared verbatim to the token's `iss` claim. Required in `oidc` mode. - -| `server.auth.oidc.audience` -| A list. Every entry is an accepted audience. - -| `server.auth.zitadel.domain` -| The Zitadel instance domain, for example `xxx.region.zitadel.cloud`. - -| `server.auth.zitadel.keyfile` -| Path to the Zitadel service account key file. - -| `server.auth.roles.claim` -| The token claim holding the provider roles. Defaults to `urn:zitadel:iam:org:project:roles` in -`zitadel` mode, and has to be set in `oidc` mode for any role to be read. - -| `server.auth.roles.mapping` -| Per Udash permission (`admin`, `publisher`, `viewer`), the provider roles that grant it. Defaults -to `udash.admin`, `udash.publisher`, and `udash.viewer`. - -| `server.auth.roles.default` -| The permission granted to a signed-in identity matching no role. Defaults to `viewer`. - -| `server.auth.roles.resolver` -| How the permission behind a Udash API token is resolved: `snapshot` or `zitadel`. Defaults to -`zitadel` in `zitadel` mode and `snapshot` otherwise. - -| `server.auth.roles.cachettl` -| How long a resolved permission is reused. Defaults to `60s`. - -| `database.uri` -| The PostgreSQL connection URI. - -| `database.migrationdisabled` -| Set to `true` to skip the schema migrations the server otherwise runs at startup. - -| `gc.maxHistoryDays` -| How many days of reports to keep. `0`, the default, keeps everything. See -<<_data_retention,Data retention>>. - -| `gc.interval` -| Time between two garbage collections. Defaults to `24h`. - -| `gc.batchSize` -| Maximum number of reports deleted by one statement. Defaults to `5000`. -|=== - -[NOTE] -==== -`issuer` and `audience` sit under `oidc:`, not directly under `auth:`. The same applies to the -Zitadel settings under `zitadel:` and the role settings under `roles:`. -==== - -=== Environment fallbacks - -[cols="1,2", options="header"] -|=== -| Variable | Sets - -| `UDASH_DB_URI` -| `database.uri` - -| `UDASH_AUTH_MODE` -| `server.auth.mode` - -| `UDASH_AUTH_OIDC_ISSUER` -| `server.auth.oidc.issuer` - -| `UDASH_AUTH_OIDC_AUDIENCE` -| `server.auth.oidc.audience`, as a single audience - -| `UDASH_AUTH_ZITADEL_DOMAIN` -| `server.auth.zitadel.domain` - -| `UDASH_AUTH_ZITADEL_KEYFILE` -| `server.auth.zitadel.keyfile` - -| `UDASH_AUTH_ROLES_CLAIM` -| `server.auth.roles.claim` - -| `UDASH_AUTH_ROLES_DEFAULT` -| `server.auth.roles.default` - -| `UDASH_AUTH_ROLES_RESOLVER` -| `server.auth.roles.resolver` - -| `UDASH_GC_MAX_HISTORY_DAYS` -| `gc.maxHistoryDays` - -| `UDASH_GC_INTERVAL` -| `gc.interval` - -| `UDASH_GC_BATCH_SIZE` -| `gc.batchSize` -|=== - -[IMPORTANT] -==== -These are fallbacks, not overrides. Each one is read only when the matching key is absent from the -configuration file, so the file always wins. This is the opposite of the precedence Updatecli -applies to its own `UPDATECLI_UDASH_*` variables, where the environment supersedes the file. -==== - -=== Data retention - -Data retention requires Udash v0.18.0 or later. Udash keeps every report forever unless -`gc.maxHistoryDays` is set. With it, the server runs a -garbage collector a minute after it starts and then every `gc.interval`. It deletes every report -older than the retention, including the last report of a pipeline that stopped running, so that -pipeline disappears from the dashboard. It then deletes the scms, labels, and resource -configurations that no remaining report references and that were not used within the retention -period. - -When several servers share a database, a PostgreSQL advisory lock ensures only one of them collects -at a time. - -The same collection can run once from the command line, for example from a scheduled job. -`--dry-run` only counts what would be deleted, and `--max-history-days` overrides the retention: - -[source,shell] ----- -udash gc --config config.yaml --max-history-days 90 --dry-run ----- - -Unlike the server, `udash gc` never migrates the schema, so run it against a database the server -has already migrated. - -== The frontend - -The frontend is a static bundle. Everything about it, including whether authentication exists at -all, comes from a `config.json` served next to it at `/usr/share/nginx/html/config.json`. The page -fetches that file before importing the bundle and exposes it as `window.config`. - -* Changing configuration never requires rebuilding the image. One published image serves an open - deployment and an authenticated one. -* The file is fetched by probing the parent path prefixes of the current URL, so the same image - works unchanged at the root or under any subpath. - -.config.json -[source,json] ----- -{{< include "assets/code_example/docs/udash/configuration/config.json" >}} ----- - -[cols="1,1,3", options="header"] -|=== -| Key | Default | Meaning - -| `AUTH_ENABLED` -| `false` -| Whether the application has a login. When false there is no login and no route guard. - -| `AUTH_VISIBILITY` -| `private` -| Mirrors `server.auth.visibility`, and only matters when `AUTH_ENABLED` is true. `public` lets -anonymous visitors browse reports, `private` sends them to the login first. An unknown value falls -back to `private`. - -| `OAUTH_DOMAIN` -| none -| The provider's issuer URL, used for OIDC discovery. - -| `OAUTH_CLIENTID` -| none -| The client ID of the SPA application registered with the provider. - -| `OAUTH_SCOPE` -| `openid profile email offline_access` -| Requested scopes. `offline_access` is what enables silent token renewal. - -| `API_BASE_URL` -| `/api` -| Where the browser reaches the API. Relative for same-host routing, absolute when the API has its -own hostname. - -| `APP_BASE_PATH` -| `/` -| The base path of the application, for mounting it under a subpath such as `/udash/`. - -| `MAX_HISTORY_DAYS` -| `30` -| How far back the date filter and the activity chart may reach. -|=== - -[NOTE] -==== -`AUTH_VISIBILITY` defaults to `private` while the server defaults to `public`. The difference is -intended: upgrading the frontend must not start showing data to anonymous visitors because a -configuration file was left unchanged. When you write `config.json` by hand, set it to the same -value as `server.auth.visibility`. The Helm chart does this for you. -==== - -=== `MAX_HISTORY_DAYS` - -It only limits the interface. The API enforces its own ceiling of 366 days regardless, and the -interface clamps a larger value to 366 before sending it. - -Raising it does not make the instance work harder by default: the filter still opens on the last -day whatever the maximum is, so a wider range only costs something once someone deliberately asks -for one. Lower it where a large report history makes those wider queries expensive. - -Keep it at or below `gc.maxHistoryDays` when retention is enabled, otherwise the date filter offers -a range that no longer holds any report. - -== Go further - -* link:/docs/udash/authentication/[Authentication] - what the auth settings above actually do. -* link:/docs/udash/installation/[Installation] - how the chart writes both files for you. -* link:/docs/udash/troubleshooting/[Troubleshooting] - symptoms of a mismatch between the two. diff --git a/content/en/docs/udash/dashboards.adoc b/content/en/docs/udash/dashboards.adoc index da9b38503..c65139e53 100644 --- a/content/en/docs/udash/dashboards.adoc +++ b/content/en/docs/udash/dashboards.adoc @@ -1,15 +1,15 @@ --- title: "Dashboards" -description: "A tour of the Udash interface: the reports view and its filters, a single pipeline report, the Git dashboard, and how to share a filtered view as a link." +description: "Find your pipelines in Udash, group them with labels, and share a filtered view as a link." lead: "Finding things in the interface" date: 2026-08-07T10:00:00+02:00 -lastmod: 2026-09-23T10:00:00+02:00 +lastmod: 2026-09-24T10:00:00+02:00 draft: false images: [] menu: docs: parent: "udash" -weight: 70 +weight: 40 toc: true --- // @@ -19,97 +19,150 @@ toc: true == Description -Most of the work in Udash happens on three screens: the reports list, a single report, and the Git -dashboard. This page describes what each one answers and how to narrow it down. +Udash has three main screens: the reports list, a single report, and the Git dashboard. Labels +from your Updatecli manifests drive the filters on all of them. -== Home - -The landing page carries a *pipeline activity* chart, showing report volume and results over the -recent window, and a *Get Started* panel with the exact commands to connect a runner to this -instance, filled in with this deployment's own URLs. If you are onboarding a colleague, sending them -that page is faster than sending them documentation. - -The window the activity chart covers is bounded by `MAX_HISTORY_DAYS`, see -link:/docs/udash/configuration/[Configuration]. +The home page also has a *Get Started* panel with the commands to connect Updatecli to this +instance, filled in with its own URLs. Send that page to a colleague you are onboarding. == Reports -The main view, at `/pipeline/reports`. Three sections: the filter, a *Latest Status Overview* -summarising the most recent reports for the current repository and branch, and the *Detailed -Reports* list itself. - -=== Filtering +At `/pipeline/reports`, the reports for one Git repository and branch, with a summary of their +latest status. [cols="1,3", options="header"] |=== | Filter | What it does | Git repository and branch -| Narrows to one repository, and optionally one branch. Populated from the `scm` of the reports that -have been received, so a pipeline with no `scmid` never appears here. +| Narrows to one repository, and optionally one branch. Only pipelines with an `scm` are listed. | Labels -| Key and value pairs, several at a time, all of which must match. The values offered for a key are -the ones actually present in the data. See link:/docs/udash/labels/[Organising with labels]. +| Key and value pairs. See <<_filtering_by_label,Filtering by label>>. | Pipeline result | Success, Changed, Failed, or Skipped. Empty means all of them. | Open pull request -| Whether the pipeline currently carries an action left open. Orthogonal to the result: a pipeline -can have succeeded and still be waiting on a pull request nobody merged. +| Whether the pipeline left a pull request open. A successful pipeline can still be waiting on a +pull request nobody merged. | Date range -| A slider over the window allowed by `MAX_HISTORY_DAYS`. It opens on the most recent day, so a -wider range is only ever queried when you ask for one. +| Opens on the most recent day. Widen it to look further back. |=== [TIP] ==== -On pipeline results, `⚠` means Updatecli *applied a change*, not that something went wrong. It is -the result you want to look at when asking what actually moved. `✔` means the run was fine and -there was nothing to do. +`⚠` means Updatecli *applied a change*, not that something went wrong. `✔` means there was nothing +to do. ==== -=== Sharing a filtered view +== A single report -The whole filter state travels in the URL, encoded as a single blob, so a filtered view is a plain -link. Copy the address bar once the filter is how you want it, and whoever opens it sees the same -view. +At `/pipeline/reports/`, the full run: its sources, conditions, targets, and actions with the +configuration each one ran with, the changelog Updatecli retrieved, and the console output. This +is the page Updatecli links to at the end of a run. -This is the practical way to hand over "every failing docker pipeline in this repository over the -last fortnight" without writing instructions for reproducing it. An unreadable or outdated blob -falls back to the default filter rather than breaking the page, so a stale bookmark is harmless. +== Git dashboard -== A single report +At `/scm/dashboard`, the status of every Git repository and branch at once, which answers "how up +to date is this codebase". A label filter here turns it into a dashboard for one team or one +ecosystem across the whole organisation. -At `/pipeline/reports/`, the full run: its sources, conditions, targets, and actions, each with -the configuration it ran with. Alongside them, the changelog Updatecli retrieved, the console output -of the run, and a graph of how the pipeline's stages relate. +Only pipelines with an `scm` appear here. -This is where a link published by a CI job lands. Updatecli prints exactly this URL at the end of a -run that publishes, see link:/docs/udash/sending-reports/[Sending reports]. +== Filtering by label -== Git dashboard +Udash has no saved dashboards or groups. A group of pipelines is whatever a label filter selects, +and the labels come from your manifests. + +[cols="1,3", options="header"] +|=== +| Filter | Selects + +| A key and a value +| Pipelines whose label has exactly that value. Case matters. + +| A key with no value +| Pipelines that carry the key, whatever its value. + +| Several rows +| Pipelines matching all of them. +|=== + +There is no OR, so "every `docker` or `helm` pipeline" cannot be one view. The rules match the +link:/docs/core/label/[`--labels` flag] of Updatecli. + +=== Choosing labels + +Start from the views you want, and give each question its own key: -At `/scm/dashboard`, the same data organised the other way round: per Git repository rather than per -run. A doughnut and a summary per repository answer "how up to date is this codebase", which is the -question the report list is bad at. +[cols="2,2", options="header"] +|=== +| View you want | Label that produces it + +| Everything the platform team owns +| `team: platform` + +| Every container image, in every repository +| `ecosystem: docker` + +| The pipelines that must never fall behind +| `monitor: active` + +| The platform team's patch updates +| `team: platform` and `update.channel: patch` +|=== + +Separate keys can be combined into narrower views later. A single `group: platform-docker` label +cannot be split back into a team and an ecosystem. + +Keep the values few and stable. A label holding a version number or a file path fills the filter +with values nobody picks. + +These keys come from the link:/docs/core/label/#_suggested_vocabulary[suggested vocabulary]. It is a +recommendation, and neither Updatecli nor Udash validates labels. Following it helps pipelines +written by different people line up under the same filter. + +=== Labels on pipelines you do not write + +Labels on an `autodiscovery` manifest are copied onto every pipeline it generates, see +link:/docs/core/label/#_labels_and_autodiscovery[Labels and autodiscovery]. + +Many published policies take their pipeline labels from a `pipeline.labels` value: + +.updatecli-compose.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/dashboards/updatecli-compose.yaml" >}} +---- + +.values.yaml +[source,yaml] +---- +{{< include "assets/code_example/docs/udash/dashboards/values.yaml" >}} +---- + +[IMPORTANT] +==== +The top-level `labels` value of a policy sets the pull request labels, which Udash never sees. +Only `pipeline.labels` reaches the report. Check the policy's own `values.yaml` to see which values +it reads. +==== -A pipeline only appears here if it declared an `scm`, so give an `scmid` to the pipelines you -intend to watch. +=== Renaming a label -== About and profile +Reports keep the labels they were published with. After renaming `team: infra` to +`team: platform`, the new value only matches reports published since the change. -`/about` describes the project and its community, and is reachable without logging in. To check -which server version an instance runs, call `GET /api/about`, see link:/docs/udash/api/[API]. +== Sharing a view -`/profile` shows the signed-in identity, and `/profile/tokens` is where you create and revoke the -API tokens Updatecli publishes with. Both only exist when authentication is enabled. See -link:/docs/udash/authentication/#_api_tokens[API tokens]. +The whole filter, labels included, is stored in the URL. Once a view shows what you want, copy the +address and share it. A list of such links, one per team or ecosystem, is the closest Udash has to +saved dashboards. == Go further -* link:/docs/udash/sending-reports/[Sending reports] - the labels and `scmid` these filters depend on. -* link:/docs/udash/api/[API] - the same queries, programmatically. -* link:/docs/udash/configuration/[Configuration] - `MAX_HISTORY_DAYS` and the rest. +* link:/docs/core/label/[Labels] - the `labels` field and the suggested vocabulary. +* link:/docs/udash/sending-reports/[Sending reports] - the `scm` and labels these views depend on. +* link:https://github.com/updatecli/udash[updatecli/udash] - the API, for querying reports programmatically. diff --git a/content/en/docs/udash/installation.adoc b/content/en/docs/udash/installation.adoc deleted file mode 100644 index 5cfdfc1ef..000000000 --- a/content/en/docs/udash/installation.adoc +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: "Installation" -description: "Deploy Udash on Kubernetes with the Helm chart: the PostgreSQL prerequisite, and the three ingress topologies the chart supports." -lead: "Deploying Udash with the Helm chart" -date: 2026-08-07T10:00:00+02:00 -lastmod: 2026-09-23T10:00:00+02:00 -draft: false -images: [] -menu: - docs: - parent: "udash" - identifier: "udash-installation" -weight: 30 -toc: true ---- -// -:toc: -// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key -:toclevels: 4 - -== Description - -The `udash` chart deploys both workloads, the frontend and the API server, and by default provisions -the PostgreSQL database as well. You still have to choose how traffic reaches the two services, -and that part of the values file needs the most care. - -Authentication is off by default. Enabling it is covered on its own page, -link:/docs/udash/authentication/[Authentication]. - -=== Requirement - -. Kubernetes 1.19 or later -. Helm 3 -. The CloudNative-PG operator, unless you bring your own PostgreSQL - -== The database - -`cnpg.enabled` defaults to `true`, so the chart provisions a link:https://cloudnative-pg.io/[CloudNative-PG] -`Cluster` and injects the credentials into the server itself. The operator is not installed by the -chart and has to be there first: - -[source,shell] ----- -helm repo add cnpg https://cloudnative-pg.github.io/charts -helm upgrade --install cnpg \ - --namespace cnpg-system \ - --create-namespace \ - cnpg/cloudnative-pg \ - --wait ----- - -[IMPORTANT] -==== -Without `--wait`, the operator pod may still be starting when the chart creates the `Cluster`, and -the install fails on the admission webhook: - -[source,text] ----- -failed calling webhook "mcluster.cnpg.io": no endpoints available for service "cnpg-webhook-service" ----- -==== - -To use a PostgreSQL you already run, turn CNPG off and supply the URI: - -[source,shell] ----- -helm install udash updatecli/udash \ - --set cnpg.enabled=false \ - --set secrets.database.stringdata.uri="postgres://user:pass@postgres:5432/udash?sslmode=disable" ----- - -The server runs its schema migrations at startup, so the database only has to exist and be -reachable. - -== Installing - -[source,shell] ----- -helm repo add updatecli https://updatecli.github.io/charts -helm repo update -helm install udash updatecli/udash ----- - -That gets you a running instance with no ingress, reachable through `kubectl port-forward`. The -notes printed by `helm install` give the exact command for your values. - -== Routing - -The chart supports three topologies. Picking one is mostly about what hostnames you have. - -=== Same host - -The default, and the simplest: one hostname, the frontend on `/`, the API on `/api`. The browser -reaches the API on the same origin, so `front.apiBaseUrl` stays relative. - -.values.yaml -[source,yaml] ----- -{{< include "assets/code_example/docs/udash/installation/values.yaml" >}} ----- - -=== Subpath - -Udash below a prefix on a shared hostname. Two rules apply: - -. `ingress.paths.front` and `front.appBasePath` must carry the same value. -. The prefix must be stripped before the request reaches nginx, which serves the application at its - own root. - -.values.yaml -[source,yaml] ----- -{{< include "assets/code_example/docs/udash/installation/values-subpath.yaml" >}} ----- - -`ingress.traefik.stripPrefix.enabled` creates the Traefik `Middleware` and wires its annotation for -you, which needs the Traefik CRDs (`traefik.io/v1alpha1`) in the cluster. On nginx, do the same by -hand: - -[source,yaml] ----- -ingress: - annotations: - nginx.ingress.kubernetes.io/rewrite-target: /$2 - nginx.ingress.kubernetes.io/use-regex: "true" - paths: - front: "/udash(/|$)(.*)" ----- - -The API keeps its own top-level `/api` path even in this topology. The server only ever -serves `/api`, and nothing rewrites the path for the API rule on same-host routing. - -=== Split domain - -The API on its own hostname. Setting `ingress.server.host` creates a second `Ingress`, and because -the API is no longer same-origin, `front.apiBaseUrl` has to be absolute. - -.values.yaml -[source,yaml] ----- -{{< include "assets/code_example/docs/udash/installation/values-split-domain.yaml" >}} ----- - -If the external path has to differ from `/api`, enable `ingress.traefik.stripPrefix` and the chart -renders a strip-then-add chain for the server: an external `/updatecli/*` is stripped to `/*` and -then prefixed to `/api/*`, which is what the backend expects. That chain is only rendered when -`ingress.server.host` is set. - -== Images - -[cols="1,2", options="header"] -|=== -| Component | Image - -| Server -| `ghcr.io/updatecli/udash` - -| Frontend -| `ghcr.io/updatecli/udash-front` -|=== - -Both tags default to values pinned by the chart, so take the chart defaults rather than pinning -your own unless you have a reason to. - -== Go further - -* link:/docs/udash/configuration/[Configuration] - every server and frontend setting. -* link:/docs/udash/authentication/[Authentication] - turning authentication on. -* link:/docs/udash/agent/[Agent] - running Updatecli itself inside the cluster. -* link:/docs/udash/troubleshooting/[Troubleshooting] - when the install does not come up. diff --git a/content/en/docs/udash/introduction.adoc b/content/en/docs/udash/introduction.adoc index b9f48068f..d8b10e53f 100644 --- a/content/en/docs/udash/introduction.adoc +++ b/content/en/docs/udash/introduction.adoc @@ -1,9 +1,9 @@ --- title: "Introduction" -description: "What Udash is, the three pieces it is made of, what a pipeline report contains once it is stored, and how it relates to Updatecli itself." +description: "What Udash is, what it shows you, and how Updatecli publishes its pipeline reports to it." lead: "The Updatecli dashboard" date: 2026-08-07T10:00:00+02:00 -lastmod: 2026-09-23T10:00:00+02:00 +lastmod: 2026-09-24T10:00:00+02:00 draft: false images: [] menu: @@ -20,74 +20,47 @@ toc: true == Description -An Updatecli run answers one question, in one repository, at one moment: is this dependency up to -date, and if not, what would change. The answer is printed to the console, and it is lost unless the -CI logs happen to be kept. - -Udash stores that answer. Updatecli publishes its pipeline report at the end of a run, and the -interface lets you look at every repository at once, filter by label or by result, and follow how a -dependency moved over the past weeks. +An Updatecli run prints its result to the console, and the result is lost unless the CI logs are +kept. Udash stores it. At the end of a run, Updatecli publishes each pipeline's report to Udash, and +the interface lets you look at every repository at once, filter by label or by result, and follow +how a dependency moved over the past weeks. [WARNING] ==== Udash is experimental, on both sides. Publishing reports from Updatecli requires the -`--experimental` flag, and the API, the stored schema, and the interface can all change without -going through the deprecation cycle described on the -link:/docs/help/deprecations/["Deprecations" page]. +`--experimental` flag, and the API and the interface can change without going through the +deprecation cycle described on the link:/docs/help/deprecations/["Deprecations" page]. ==== -== Architecture - -Udash is three components, plus Updatecli itself as the thing that feeds it. - -[cols="1,3", options="header"] -|=== -| Component | Role - -| `udash server` -| The API. A Go service listening on port `8080`, serving everything under `/api/`, plus a generated -Swagger UI at `/swagger/index.html`. Published as `ghcr.io/updatecli/udash`. - -| `udash-front` -| The interface. A Vue 3 and Vuetify single-page application served by nginx on port `80`. It is -configured entirely at runtime, so one image serves every deployment. Published as -`ghcr.io/updatecli/udash-front`. - -| PostgreSQL -| Where the reports live. The server runs its own schema migrations at startup. -|=== - -The frontend never talks to the database, only to the API. Updatecli never talks to the database -either, it POSTs its report to the API like any other client. +== How it works -There is a fourth, optional piece: the link:/docs/udash/agent/[agent], which runs Updatecli on a -schedule inside a Kubernetes cluster rather than from each repository's CI. +Udash is a web interface backed by an API and a PostgreSQL database. Updatecli sends its reports to +the API over HTTP, from your laptop, from CI, or from a scheduled job. Nothing in your manifests has +to change. -== What is stored +Two things in a manifest decide how easily you find a report again: -The server splits each report into parts so the interface can search across runs: +* an `scm`, because the interface is organised per Git repository +* link:/docs/core/label/[labels], because the filters are built on them -* the pipeline report itself, with its name, its result, and its execution date -* the resource configurations, split into sources, conditions, and targets, each recorded with its - kind and its specification -* the scm, meaning the Git repository URL and the branch a pipeline acted on -* the labels attached to the pipeline, which most of the filtering is built on +== Getting an instance -This is why link:/docs/core/label/[labels] and an `scmid` matter more in Udash than they do in a -plain Updatecli run. A report with neither is stored correctly, but it is hard to find again, since -the interface is organised by Git repository and filtered by label. +To try Udash, the link:/docs/udash/quick-start/[quick start] runs it locally with Docker Compose. -== Trying it +For a shared instance, Udash is published as a Helm chart: -The fastest path is link:/docs/udash/quick-start/[the quick start], which brings the whole stack up -locally with Docker Compose and needs nothing besides Docker. +[source,shell] +---- +helm repo add updatecli https://updatecli.github.io/charts +helm install udash updatecli/udash +---- -For a real deployment, the link:/docs/udash/installation/[Helm chart] covers the database, both -workloads, and the ingress. +Deployment options, authentication, configuration, the in-cluster agent, and the API reference are +documented in the link:https://github.com/updatecli/udash[updatecli/udash] repository. == Go further * link:/docs/udash/quick-start/[Quick start] - run it locally in a few minutes. -* link:/docs/udash/sending-reports/[Sending reports] - the Updatecli side of the integration. +* link:/docs/udash/sending-reports/[Sending reports] - connect Updatecli to an instance. * link:/docs/udash/dashboards/[Dashboards] - what the interface offers once reports arrive. -* link:https://github.com/updatecli/udash[updatecli/udash] - the server source and issue tracker. +* link:https://github.com/updatecli/udash[updatecli/udash] - the source, the operator documentation, and the issue tracker. diff --git a/content/en/docs/udash/labels.adoc b/content/en/docs/udash/labels.adoc deleted file mode 100644 index 5b9f645f3..000000000 --- a/content/en/docs/udash/labels.adoc +++ /dev/null @@ -1,194 +0,0 @@ ---- -title: "Organising with labels" -description: "Use Updatecli pipeline labels to group pipelines from many repositories into one Udash view, and share that view as a link." -lead: "Grouping pipelines into dashboards" -date: 2026-09-23T10:00:00+02:00 -lastmod: 2026-09-23T10:00:00+02:00 -draft: false -images: [] -menu: - docs: - parent: "udash" -weight: 65 -toc: true ---- -// -:toc: -// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key -:toclevels: 4 - -== Description - -Udash does not store named dashboards or groups. A group of pipelines is whatever a label filter -selects, and the view it produces is a link you can bookmark or share. The labels come from your -Updatecli manifests, so deciding how pipelines are grouped in Udash means deciding which labels -they carry. - -This page covers how Udash uses labels and how to choose them, then how to set them on pipelines -you do not write by hand. The general rules for labels, and a suggested vocabulary, are on the -link:/docs/core/label/[Labels] page. - -== From manifest to dashboard - -A pipeline declares its labels at the top level of its manifest: - -.updatecli.yaml -[source,yaml] ----- -{{< include "assets/code_example/docs/udash/labels/updatecli.yaml" >}} ----- - -Updatecli copies them into the pipeline report, and Udash stores them with that report. Every -filter in the interface then offers the label keys it has seen, and for each key the values it has -seen, over the selected date range. - -Two views use them: - -[cols="1,3", options="header"] -|=== -| View | What labels do there - -| Git dashboard, `/scm/dashboard` -| Covers every Git repository at once. The label filter decides which pipelines count, so a label -filter here gives you a dashboard for one team or one ecosystem across the whole organisation. -Each branch shows its status and an activity chart for the matching pipelines only. - -| Reports, `/pipeline/reports` -| Works on one repository and branch at a time. Labels narrow the report list within it. -|=== - -The Git dashboard only shows pipelines that declare an `scm`. A pipeline with labels and no `scm` -is still stored and still matches the filter in the API, but it never appears on that dashboard. -Give an `scm` to every pipeline you want to see there. See -link:/docs/udash/sending-reports/#_making_reports_findable[Making reports findable]. - -== How the filter matches - -[cols="1,3", options="header"] -|=== -| Filter | Selects - -| A key and a value -| Pipelines whose label has exactly that value. The comparison is case-sensitive. - -| A key with no value -| Pipelines that carry the key, whatever its value. - -| Several rows -| Pipelines matching all of them. Rows are combined with AND. -|=== - -There is no OR. Each key takes one value, so "every `docker` or `helm` pipeline" cannot be one -view. When you need that grouping regularly, add a key whose value is shared by both, or open two -views. - -These rules are the same as the `--labels` flag of Updatecli, apart from the missing value: in the -Udash filter you leave the value empty, and on the command line you write `--labels="team:"`. - -== Choosing labels for your dashboards - -Start from the views you want, and give each question they answer its own key: - -[cols="2,2", options="header"] -|=== -| View you want | Label that produces it - -| Everything the platform team owns -| `team: platform` - -| Every container image, in every repository -| `ecosystem: docker` - -| The pipelines that must never fall behind -| `monitor: active` - -| The platform team's patch updates -| `team: platform` and `update.channel: patch` -|=== - -Because rows combine with AND, independent keys can be stacked into narrower views without -planning each combination in advance. That only works if a key answers one question: a single -`group: platform-docker` label cannot be split back into a team and an ecosystem. - -Keep the values few and stable. The filter lists every value it has seen for a key, so a label -holding a version number or a file path fills the list with entries nobody will pick, and it -groups nothing. - -The keys used above (`team`, `ecosystem`, `monitor`, and `update.channel`) come from the -link:/docs/core/label/#_suggested_vocabulary[suggested vocabulary]. It is a recommendation. -Updatecli and Udash accept any key and value and validate none of them. Its use is that pipelines -written by different people in different repositories end up under the same filter values. - -== Setting labels on pipelines you do not write - -=== Autodiscovery - -Labels on a manifest that declares `autodiscovery` are copied onto every pipeline the crawlers -generate. One manifest therefore labels all the updates discovered in a repository: - -.updatecli.d/autodiscovery.yaml -[source,yaml] ----- -{{< include "assets/code_example/docs/udash/labels/autodiscovery.yaml" >}} ----- - -Every generated pipeline carries the same labels. A label that has to differ between dependencies -needs one autodiscovery manifest per value, see -link:/docs/core/label/#_labels_and_autodiscovery[Labels and autodiscovery]. - -=== Published policies - -A policy's manifests are not yours to edit. Many published policies set their pipeline labels from -a `pipeline.labels` value instead, with defaults such as `ecosystem` and `policy`. Check the -`values.yaml` of the policy you use to see whether it reads that value. - -.updatecli-compose.yaml -[source,yaml] ----- -{{< include "assets/code_example/docs/udash/labels/updatecli-compose.yaml" >}} ----- - -.values.yaml -[source,yaml] ----- -{{< include "assets/code_example/docs/udash/labels/values.yaml" >}} ----- - -[IMPORTANT] -==== -Policies often have a second, top-level `labels` value. That one is the list of labels put on the -pull request, and it has no effect in Udash. Only `pipeline.labels` reaches the report. -==== - -The same values files work from the link:/docs/udash/agent/[agent chart], through `valuesFiles`, so -pipelines run inside the cluster can carry the same labels as the ones run from CI. - -== Sharing a grouped view - -Once the filter selects the group you want, copy the address from the browser. The whole filter, -labels included, is encoded in the URL, so the link opens the same view for anyone who can reach -the instance. A list of such links, one per team or per ecosystem, is the closest Udash has to a -set of saved dashboards. See link:/docs/udash/dashboards/#_sharing_a_filtered_view[Sharing a -filtered view]. - -== Pitfalls - -Udash rejects a report that contains a label with an empty value. Omit the label instead of -setting it to `""`. - -Changing a label's value splits the history. Reports are stored with the labels they had when they -were published, so after renaming `team: infra` to `team: platform`, the new value only matches -reports published since the change. The older reports stay under the old value until they fall out -of the date range, or until retention deletes them. - -Casing is part of the value. `Team: Platform` and `team: platform` are two different labels, and -the filter lists both. - -Pull request labels, set under `actions..spec.labels`, are applied on the Git provider and -never reach Udash. - -== Go further - -* link:/docs/core/label/[Labels] - the `labels` field, `--labels`, and the suggested vocabulary. -* link:/docs/udash/dashboards/[Dashboards] - the filter and the views it drives. -* link:/docs/udash/api/[API] - the same `labels` filter on the search and summary endpoints. diff --git a/content/en/docs/udash/quick-start.adoc b/content/en/docs/udash/quick-start.adoc index fbf596c50..a90d49dde 100644 --- a/content/en/docs/udash/quick-start.adoc +++ b/content/en/docs/udash/quick-start.adoc @@ -1,9 +1,9 @@ --- title: "Quick start" -description: "Run the whole Udash stack locally with Docker Compose, point Updatecli at it, and publish your first pipeline report." +description: "Run Udash locally with Docker Compose, point Updatecli at it, and publish your first pipeline report." lead: "Udash on your laptop in five minutes" date: 2026-08-07T10:00:00+02:00 -lastmod: 2026-09-23T10:00:00+02:00 +lastmod: 2026-09-24T10:00:00+02:00 draft: false images: [] menu: @@ -19,10 +19,8 @@ toc: true == Description -This page brings up PostgreSQL, the Udash API, the interface, and a Traefik in front of them, then -publishes a report into it from your own Updatecli runs. Everything runs locally and nothing is -authenticated. That is convenient for a first look, but do not deploy this setup anywhere other -people can reach it. +This page runs Udash locally, then publishes a report to it from your own Updatecli runs. Nothing is +authenticated, so do not expose this setup to anyone else. === Requirement @@ -49,9 +47,6 @@ Three files in one directory: {{< include "assets/code_example/docs/udash/quick-start/docker-compose.yaml" >}} ---- -Traefik publishes port `80` and routes `/api/` to the server and everything else to the frontend. -The database is not published at all, only the two services reach it. - .udash/config.yaml [source,yaml] ---- @@ -64,24 +59,20 @@ The database is not published at all, only the two services reach it. {{< include "assets/code_example/docs/udash/quick-start/config.json" >}} ---- -`config.json` is fetched by the browser before the application bundle loads, so the same image can -serve an open deployment like this one and an authenticated one. `AUTH_ENABLED` set to `false` is -what removes the login flow, and `API_BASE_URL` tells the browser where the API is. - -== 2. Start the stack +== 2. Start Udash [source,shell] ---- docker compose up -d ---- -Then open `http://localhost`. The API answers on `http://localhost/api`, and -`http://localhost/api/ping` is the quickest way to confirm it is up. +Open `http://localhost`. If the page does not load, `http://localhost/api/ping` tells you whether +the API is up. [NOTE] ==== -If the server exits immediately on the first start, PostgreSQL was not accepting connections yet. -Run `docker compose restart server`. +If the server exits on the first start, PostgreSQL was not ready yet. Run +`docker compose restart server`. ==== == 3. Point Updatecli at it @@ -91,22 +82,19 @@ Run `docker compose restart server`. updatecli udash login "http://localhost" --experimental ---- -The instance has no authentication, so the command does not ask for a token and only records the -endpoint in the Updatecli configuration file. `--api-url` defaults to the given URL with `/api` -appended, which is right here. - -`updatecli udash config` prints where that file was written. +The instance has no authentication, so the command does not ask for a token. It only records the +endpoint. == 4. Publish a report -Run Updatecli as you normally would, with `--experimental` added: +Run Updatecli as usual, with `--experimental` added: [source,shell] ---- updatecli diff --experimental ---- -The run ends with a section of its own, and one URL per pipeline: +The run ends with one link per pipeline: [source,text] ---- @@ -120,17 +108,14 @@ my pipeline: [WARNING] ==== -Without `--experimental` nothing is published, no warning is printed, and the run reports success. -Configuring the endpoint is not enough on its own. See the +Without `--experimental`, nothing is published and nothing warns you. See the link:/docs/help/experimental/["Experimental features" page]. ==== Refresh `http://localhost` and the report is there. -== Nothing to publish yet? - -If you have no manifest at hand, a published policy will do. Drop an `updatecli-compose.yaml` next -to a repository you care about: +If you have no manifest at hand, a published policy works too. Put this `updatecli-compose.yaml` +in a repository you care about: .updatecli-compose.yaml [source,yaml] @@ -138,22 +123,8 @@ to a repository you care about: {{< include "assets/code_example/docs/udash/quick-start/updatecli-compose.yaml" >}} ---- -then run it, still without changing anything: - -[source,shell] ----- -updatecli compose diff --experimental ----- - -Pulling a policy from the GitHub Container Registry may require `docker login ghcr.io` first. See -link:/docs/core/compose/[Compose] for what else the file can express. - -== A note on discoverability - -The interface is organised per Git repository, and its filters are built on -link:/docs/core/label/[labels]. A pipeline that declares neither an `scmid` nor any label is stored -correctly but is awkward to find again. Add an `scm` to the pipelines you intend to watch before -you load a lot of reports. +and run `updatecli compose diff --experimental`. Pulling a policy may require +`docker login ghcr.io` first. == Cleaning up @@ -164,6 +135,6 @@ docker compose down --volumes == Go further -* link:/docs/udash/installation/[Installation] - the same thing, deployed properly, with the Helm chart. -* link:/docs/udash/sending-reports/[Sending reports] - authentication, CI, and what a report carries. +* link:/docs/udash/sending-reports/[Sending reports] - connect Updatecli to a shared instance and to CI. * link:/docs/udash/dashboards/[Dashboards] - what to do with the reports once they are in. +* link:https://github.com/updatecli/udash[updatecli/udash] - deploying a shared instance. diff --git a/content/en/docs/udash/sending-reports.adoc b/content/en/docs/udash/sending-reports.adoc index 6ebf43628..c1135769e 100644 --- a/content/en/docs/udash/sending-reports.adoc +++ b/content/en/docs/udash/sending-reports.adoc @@ -1,15 +1,15 @@ --- title: "Sending reports" -description: "How Updatecli publishes pipeline reports to Udash: the experimental flag, udash login, the environment variables, and what makes a report findable." -lead: "The Updatecli side of the integration" +description: "Connect Updatecli to a Udash instance, from your laptop or from CI, and make your reports easy to find." +lead: "Publishing pipeline reports from Updatecli" date: 2026-08-07T10:00:00+02:00 -lastmod: 2026-09-23T10:00:00+02:00 +lastmod: 2026-09-24T10:00:00+02:00 draft: false images: [] menu: docs: parent: "udash" -weight: 60 +weight: 30 toc: true --- // @@ -19,94 +19,76 @@ toc: true == Description -Nothing about a manifest changes when you publish to Udash. Updatecli runs as it always did, and at -the end of the run it POSTs each pipeline's report to the API. What you have to give it is where to -publish, whether it needs a token, and the `--experimental` flag. - -== The experimental flag +Your manifests stay the same. At the end of a run, Updatecli sends each pipeline's report to Udash. +It needs three things from you: where to publish, a token if the instance requires one, and the +`--experimental` flag. [WARNING] ==== -Without `--experimental`, publishing is skipped *silently*. Updatecli prints no warning and no -error, and the run reports success. Configuring the endpoint, logging in, or setting every `UPDATECLI_UDASH_*` variable -changes nothing on its own. +Without `--experimental`, nothing is published. Updatecli prints no warning and the run reports +success, even when an endpoint and a token are configured. ==== -With the flag, the run gains a section of its own: +== From your laptop -[source,text] +[source,shell] ---- -UDASH - EXPERIMENTAL -===================== - -Publishing report to Udash -my pipeline: - => "https://udash.example.com/pipeline/reports/8f2b1c94-...." +updatecli udash login "https://udash.example.com" --experimental ---- -That URL is the stored report, and it is also attached to the report itself, so it can appear in -whatever consumes Updatecli's output. See the link:/docs/help/experimental/["Experimental features" -page] for everything else the flag turns on. +If the instance requires authentication, the command opens its token page in a browser and asks +you to paste an API token. Create one there, under *Profile* then *Tokens*. The command checks the +token before saving it, so a wrong token fails immediately. -== Telling Updatecli where to publish - -Two ways, and they can be mixed. The environment wins over the configuration file. - -=== With `udash login` +If the API is not at the given URL followed by `/api`, set it with `--api-url`: [source,shell] ---- -updatecli udash login "https://udash.example.com" --experimental +updatecli udash login --api-url "https://api.example.com/api" "https://udash.example.com" --experimental ---- -The command calls the API's `/whoami` endpoint first. An instance with authentication disabled -does not have that endpoint, so the command records the endpoint and asks for nothing. +`updatecli udash config` prints where the settings were saved, and `updatecli udash logout` removes +them. The link:/docs/commands/updatecli_udash_login/[`updatecli udash login`] reference lists every +flag. -`--api-url` sets the API endpoint and defaults to the given URL with `/api` appended, which is -correct for a default deployment. Point it elsewhere when the API lives on its own hostname: +Then run Updatecli with `--experimental`: [source,shell] ---- -updatecli udash login --api-url "https://api.example.com/api" "https://udash.example.com" --experimental +updatecli diff --experimental ---- -Against an authenticated instance, the command prompts for a Udash API token, or takes it from -`--token` or standard input, and validates it before saving it. See -link:/docs/udash/authentication/#_authenticating_updatecli[Authenticating Updatecli]. +Each pipeline ends with a link to its stored report: -`udash login` also reads the `UPDATECLI_UDASH_*` variables below, and a flag wins over the matching -variable. +[source,text] +---- +UDASH - EXPERIMENTAL +===================== -The token and endpoints land in the Updatecli configuration file. `updatecli udash config` prints -its path, and `updatecli udash logout` removes the entry. +Publishing report to Udash +my pipeline: + => "https://udash.example.com/pipeline/reports/8f2b1c94-...." +---- -=== With environment variables +== From CI -Better suited to CI, where there is no browser to redirect to. +Use environment variables instead of `udash login`: [cols="1,3", options="header"] |=== | Variable | Purpose | `UPDATECLI_UDASH_URL` -| The Udash instance URL, used to build the report links. +| The Udash URL, used to build the report links. | `UPDATECLI_UDASH_API_URL` -| The API URL. This is the one that decides whether anything is published at all. +| The API URL. Nothing is published when it is unset. | `UPDATECLI_UDASH_ACCESS_TOKEN` | The Udash API token, when the instance requires one. |=== -A variable that is set supersedes the corresponding value in the configuration file, and the choice -is logged at debug level. If `UPDATECLI_UDASH_API_URL` resolves to nothing, from either source, the -run logs `no Udash endpoint detected, skipping` and carries on. - -These are also listed on the link:/docs/help/environment/["Environment variables" page]. - -== In a CI pipeline - -Nothing beyond the two or three variables and the flag: +A variable wins over the value saved by `udash login`. [source,yaml] ---- @@ -118,26 +100,14 @@ Nothing beyond the two or three variables and the flag: run: updatecli apply --experimental ---- -See link:/docs/automate/github_action/[GitHub Actions] and link:/docs/automate/jenkins/[Jenkins] for +The token needs the `reports:write` scope, which new tokens get by default. See +link:/docs/automate/github_action/[GitHub Actions] and link:/docs/automate/jenkins/[Jenkins] for the surrounding job. -== Making reports findable - -A report that arrives with no context is stored correctly and is then hard to retrieve. Two things -decide how easily you will find it again. - -=== An scm - -The interface is organised per Git repository. A pipeline that declares an `scm` and references it -through `scmid` is filed under that repository, appears on the Git dashboard, and can be compared -with the other pipelines touching the same code. A pipeline with none of that is only reachable -through the report list. - -=== Labels +== Making reports easy to find -link:/docs/core/label/[Labels] are what the filters are built on, in Udash exactly as in -`--labels` locally. link:/docs/udash/labels/[Organising with labels] explains how to choose them so -they group pipelines into useful views. +The interface is organised per Git repository and filtered by label. A report with neither is +stored, but you can only reach it through the full report list. .updatecli.yaml [source,yaml] @@ -145,20 +115,36 @@ they group pipelines into useful views. {{< include "assets/code_example/docs/udash/sending-reports/updatecli.yaml" >}} ---- -[IMPORTANT] -==== -The label vocabularies suggested on the link:/docs/core/label/[Labels] page are recommendations. -Updatecli accepts any key and value, never validates or rewrites them, and never generates them for -you. Following the suggestions helps pipelines written by different people in different -repositories line up in the same filter. -==== +* An `scm` referenced through `scmid` files the pipeline under its Git repository and puts it on + the Git dashboard. +* link:/docs/core/label/[Labels] feed the filters. See + link:/docs/udash/dashboards/#_choosing_labels[Choosing labels]. + +Udash rejects a label with an empty value. Leave the label out instead of setting it to `""`. + +== When nothing arrives + +[cols="1,2", options="header"] +|=== +| Symptom | Fix + +| No `UDASH - EXPERIMENTAL` section, no warning +| Add `--experimental`. + +| `no Udash endpoint detected, skipping` +| Run `updatecli udash login`, or set `UPDATECLI_UDASH_API_URL`. Add `--debug` to see which +source Updatecli read. + +| `403` with `token is not allowed to perform this action` +| Your token cannot write reports. Create a token with the `reports:write` scope, or ask the +instance administrator for the `publisher` role. +|=== -One thing Udash does enforce: a label with an empty value is rejected when the report is stored. Omit -the label instead of giving it `""`. +Problems on the server side, such as sign-in or tokens that are always rejected, are covered in the +link:https://github.com/updatecli/udash[updatecli/udash] repository. == Go further -* link:/docs/udash/authentication/[Authentication] - publishing to an instance that requires a token. * link:/docs/udash/dashboards/[Dashboards] - the filters these labels feed. -* link:/docs/commands/updatecli_udash_login/[`updatecli udash login`] - the full flag reference. -* link:/docs/udash/troubleshooting/[Troubleshooting] - when nothing arrives. +* link:/docs/help/environment/["Environment variables"] - every variable Updatecli reads. +* link:/docs/help/experimental/["Experimental features"] - what else `--experimental` turns on. diff --git a/content/en/docs/udash/troubleshooting.adoc b/content/en/docs/udash/troubleshooting.adoc deleted file mode 100644 index be54cc068..000000000 --- a/content/en/docs/udash/troubleshooting.adoc +++ /dev/null @@ -1,190 +0,0 @@ ---- -title: "Troubleshooting" -description: "Symptoms you may hit running Udash, from reports that never arrive to tokens that are always rejected, with the cause and the fix for each." -lead: "When Udash does not behave" -date: 2026-08-07T10:00:00+02:00 -lastmod: 2026-09-23T10:00:00+02:00 -draft: false -images: [] -menu: - docs: - parent: "udash" - identifier: "udash-troubleshooting" -weight: 90 -toc: true ---- -// -:toc: -// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key -:toclevels: 4 - -== Description - -One symptom per section, with what causes it. For problems with Updatecli itself rather than with -Udash, see the link:/docs/help/troubleshooting/[Troubleshooting] page. - -== Nothing is published, and nothing is logged - -The run succeeds, the Udash section never appears, and there is no warning anywhere. - -`--experimental` is missing. Publishing requires it, whatever else is configured, and the skip is -silent. See link:/docs/help/experimental/["Experimental features"]. - -== `no Udash endpoint detected, skipping` - -Updatecli found no API URL, from either the configuration file or the environment. - -Check, in order: - -* `updatecli udash config` prints the configuration file path. Is there an entry, and does it carry - an `api` value? -* Is `UPDATECLI_UDASH_API_URL` set in this shell or job? - -Run with `--debug` to see which source was used. - -== Every token is rejected - -Requests that should be authorised come back rejected, and the logs say nothing useful about why. - -Almost always the issuer. It is compared to the token's `iss` claim verbatim, and the trailing slash -is part of the comparison: Auth0 issues one, Zitadel and Keycloak do not. Decode a real token from -your provider, read its `iss`, and copy that exact string into `auth.issuer`. - -Against Zitadel, also check the scope. Without the project audience scope -`urn:zitadel:iam:org:project:id::aud`, the provider issues a token that the API will -not accept. See link:/docs/udash/authentication/[Authentication]. - -== Signed in, but publishing is refused - -The API answers a report upload with `403` and `token is not allowed to perform this action`. - -The credential cannot write reports. Either the identity behind it is only a `viewer`, or it is an -API token without the `reports:write` scope. In `oidc` mode the first case applies to everyone when -`roles.claim` is empty, since no role is read at all. Set the claim and map a provider role to -`publisher`, or create a new token with the `reports:write` scope. - -Creating an API token on `/profile/tokens` also requires `publisher`, and a `viewer` gets -`insufficient permission` instead. See link:/docs/udash/authentication/#_permissions[Permissions]. - -== The server refuses to start after an upgrade - -`unknown authentication mode "oauth"` in the server log means the configuration still uses the -name the generic mode had before Udash v0.17.1. Rename `mode: oauth` to `mode: oidc`, and the -`oauth:` block holding `issuer` and `audience` to `oidc:`. The Helm chart fails with an explicit message for the same -reason. - -== Anyone can delete reports - -Expected, if no authentication mode is configured. Unset or `none` installs no middleware at all, -which leaves every endpoint open including the writes. - -Set `auth.enabled: true` with a mode, and pick a `visibility`. `public` keeps the dashboards -readable while requiring a token for anything that writes. - -== The server exits right after starting - -Usually PostgreSQL was not accepting connections yet when the server tried to connect. - -On Docker Compose, `docker compose restart server`. On Kubernetes the pod restarts on its own until -the database is ready, so a few restarts at install time are not a problem. - -If it persists, the URI is wrong or the database is unreachable. The connection string is logged at -debug level. - -== `failed calling webhook "mcluster.cnpg.io"` - -[source,text] ----- -failed calling webhook "mcluster.cnpg.io": no endpoints available for service "cnpg-webhook-service" ----- - -The CloudNative-PG operator is installed but its admission webhook is not serving yet, so the -`Cluster` the chart creates cannot be admitted. - -Install the operator with `--wait` before installing Udash, or simply retry the install once the -operator pod is ready. See link:/docs/udash/installation/[Installation]. - -== Migrations fail at startup - -The server runs its schema migrations before serving. A failure there stops it. - -If you migrate out of band, or you are rolling back to an older server against a newer schema, set -`database.migrationdisabled: true` to leave the schema alone. Be aware that a server running against -a schema it does not expect will fail in less obvious ways. - -== A blank page, or 404s on the assets - -Nearly always a subpath mismatch. Three things have to agree: - -. `ingress.paths.front`, where the ingress routes the frontend -. `front.appBasePath`, which the application uses to build its own URLs -. a strip-prefix, so nginx receives `/` rather than `/udash/` - -Miss the strip-prefix and the requests reach nginx with the prefix still attached. Miss -`appBasePath` and the application builds links from the root. See -link:/docs/udash/installation/#_subpath[Subpath routing]. - -== The interface loads but shows no data - -Open the browser console and look at where the API calls are going. - -* On split-domain routing, `front.apiBaseUrl` has to be an absolute URL. A relative `/api` sends the - calls to the frontend's own host, where nothing answers them. -* Check CORS, and that the API hostname resolves and has a valid certificate. -* `https:///api/ping` should return `pong`. - -== The API returns 400 - -The API bounds its own queries, and an integrator usually meets one of these first: - -[cols="2,3", options="header"] -|=== -| Message | Cause - -| `requested time range exceeds the maximum allowed span` -| More than 366 days between `start_time` and `end_time`. - -| `both start_time and end_time must be provided` -| Only one boundary was given. - -| `days and hours cannot be combined` -| Pick one window unit. - -| `requested time range and granularity produce too many buckets` -| More than 1000 buckets, for example an hourly granularity over a year. - -| `invalid pagination parameters` -| A page size above 1000, or a malformed `limit` or `page`. -|=== - -See link:/docs/udash/api/[API]. - -== Reports arrive but are hard to find - -The reports are stored, but nothing files them under a repository or a label. - -The interface is organised per Git repository and its filters are built on labels. A pipeline with -no `scmid` does not appear on the Git dashboard, and one with no labels matches no label filter. Add -an `scm` to the pipelines you intend to watch, and give them labels. See -link:/docs/udash/sending-reports/#_making_reports_findable[Making reports findable]. - -A label with an empty value is rejected outright when the report is stored. Omit the label rather -than giving it `""`. - -== Agent CronJobs run but nothing appears - -The jobs succeed, the database stays empty. - -* The agent publishes to the AgentRelay, not to the Udash server. Check `udash.json` in the agent's - ConfigMap points at `http://agentrelay/api`. -* The relay and the Udash server must share the same database. Compare - `secrets.database.stringdata.uri` in both releases. -* The agent's arguments must include `--experimental`, which the chart's defaults do carry. A custom - `images.agent.args` that drops it publishes nothing, silently. - -See link:/docs/udash/agent/[Agent]. - -== Go further - -* link:/docs/help/troubleshooting/[Updatecli troubleshooting] - for problems in the run itself. -* link:/support/[Support] - where to ask, and how to report a bug. From 9edaa612243c4cbcffaf19bcb7d8b3a0b10c50f5 Mon Sep 17 00:00:00 2001 From: Olblak Date: Thu, 24 Sep 2026 15:46:12 +0200 Subject: [PATCH 3/4] chore: mention updatecli.uda.sh Signed-off-by: Olblak --- content/en/docs/udash/_index.md | 3 ++ content/en/docs/udash/dashboards.adoc | 4 +- content/en/docs/udash/introduction.adoc | 9 ++++- updatecli/updatecli.d/udash.yaml | 52 +++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 updatecli/updatecli.d/udash.yaml diff --git a/content/en/docs/udash/_index.md b/content/en/docs/udash/_index.md index 886c5ddaf..e6dc6226b 100644 --- a/content/en/docs/udash/_index.md +++ b/content/en/docs/udash/_index.md @@ -13,6 +13,9 @@ sidebar: Updatecli tells you what changed on one run, in one repository. [Udash](https://github.com/updatecli/udash) keeps those runs, so you can see the same information across every repository and over time. +To see it in use, browse [updatecli.uda.sh](https://updatecli.uda.sh), the public instance the +Updatecli project publishes its own pipeline reports to. + {{< alert icon="⚠️" text="Udash is experimental, and so is the Updatecli side of it. Publishing reports requires the --experimental flag, and both the API and the interface can change without going through the usual deprecation cycle." >}} - [Introduction](/docs/udash/introduction/) - what Udash is and how Updatecli feeds it. diff --git a/content/en/docs/udash/dashboards.adoc b/content/en/docs/udash/dashboards.adoc index c65139e53..3299cbfe6 100644 --- a/content/en/docs/udash/dashboards.adoc +++ b/content/en/docs/udash/dashboards.adoc @@ -20,7 +20,9 @@ toc: true == Description Udash has three main screens: the reports list, a single report, and the Git dashboard. Labels -from your Updatecli manifests drive the filters on all of them. +from your Updatecli manifests drive the filters on all of them. You can try every screen described +here on link:https://updatecli.uda.sh[updatecli.uda.sh], the public instance the Updatecli project +publishes to. The home page also has a *Get Started* panel with the commands to connect Updatecli to this instance, filled in with its own URLs. Send that page to a colleague you are onboarding. diff --git a/content/en/docs/udash/introduction.adoc b/content/en/docs/udash/introduction.adoc index d8b10e53f..63e12e533 100644 --- a/content/en/docs/udash/introduction.adoc +++ b/content/en/docs/udash/introduction.adoc @@ -43,9 +43,16 @@ Two things in a manifest decide how easily you find a report again: * an `scm`, because the interface is organised per Git repository * link:/docs/core/label/[labels], because the filters are built on them +== See it running + +The Updatecli project publishes the reports of its own pipelines to a public instance, +link:https://updatecli.uda.sh[updatecli.uda.sh]. Anyone can browse it without signing in, which +makes it a quick way to see what Udash shows before running your own. Publishing to it is reserved +for the Updatecli project. + == Getting an instance -To try Udash, the link:/docs/udash/quick-start/[quick start] runs it locally with Docker Compose. +To try Udash on your own pipelines, the link:/docs/udash/quick-start/[quick start] runs it locally with Docker Compose. For a shared instance, Udash is published as a Helm chart: diff --git a/updatecli/updatecli.d/udash.yaml b/updatecli/updatecli.d/udash.yaml new file mode 100644 index 000000000..68399322e --- /dev/null +++ b/updatecli/updatecli.d/udash.yaml @@ -0,0 +1,52 @@ +name: "docs: update the Udash versions used by the documentation examples" +pipelineid: "updatecli_udash_docs" + +# The Udash quick start pins the server and frontend images in its Docker Compose example. +# Tags are read from the container registry rather than GitHub releases, so a version is +# only proposed once its image can actually be pulled. + +labels: + monitor: active + ecosystem: docker + kind: documentation + +actions: + default: + title: "docs: update the Udash versions used by the documentation examples" + kind: github/pullrequest + spec: + merge: + strategy: auto + mergemethod: squash + reviewers: + - updatecli/core + labels: + - chore + - documentation + scmid: default + +scms: + default: + kind: github + spec: + branch: "{{ .scm.branch }}" + email: "{{ .scm.email }}" + owner: "{{ .scm.owner }}" + repository: "{{ .scm.repository }}" + user: "{{ .scm.user }}" + force: true + disabled: false + +autodiscovery: + groupby: all + scmid: default + actionid: default + crawlers: + dockercompose: + rootdir: assets/code_example + only: + - images: + - ghcr.io/updatecli/udash + - ghcr.io/updatecli/udash-front + - updatecli/udash + - updatecli/udash-front From b9f337bbc97952f155da1496d285de158bfc35f9 Mon Sep 17 00:00:00 2001 From: Olblak Date: Thu, 24 Sep 2026 21:14:56 +0200 Subject: [PATCH 4/4] chore: update code example Signed-off-by: Olblak --- .../docs/udash/dashboards/updatecli-compose.yaml | 2 +- .../docs/udash/quick-start/docker-compose.yaml | 8 ++++---- .../docs/udash/quick-start/updatecli-compose.yaml | 2 +- content/en/docs/udash/quick-start.adoc | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/assets/code_example/docs/udash/dashboards/updatecli-compose.yaml b/assets/code_example/docs/udash/dashboards/updatecli-compose.yaml index 627d47307..3a6bae7fe 100644 --- a/assets/code_example/docs/udash/dashboards/updatecli-compose.yaml +++ b/assets/code_example/docs/udash/dashboards/updatecli-compose.yaml @@ -1,5 +1,5 @@ policies: - name: Rust dependencies - policy: ghcr.io/updatecli/policies/autodiscovery/cargo:latest + policy: ghcr.io/updatecli/policies/autodiscovery/cargo:0.9.1@sha256:237c49b0541ad1a5ce9bcdfef0a525dd47ff854436dee61985b56f2c981c828d values: - values.yaml diff --git a/assets/code_example/docs/udash/quick-start/docker-compose.yaml b/assets/code_example/docs/udash/quick-start/docker-compose.yaml index 9f4f773e2..d787c7dcf 100644 --- a/assets/code_example/docs/udash/quick-start/docker-compose.yaml +++ b/assets/code_example/docs/udash/quick-start/docker-compose.yaml @@ -1,6 +1,6 @@ services: db: - image: postgres:17 + image: postgres:18@sha256:5a5a84b19854a9ffaa54082c166ff4ec27473a361e496e5ea167f298f2da9722 restart: always environment: - POSTGRES_USER=udash @@ -10,7 +10,7 @@ services: - 5432 server: - image: ghcr.io/updatecli/udash:v0.17.1 + image: ghcr.io/updatecli/udash:v0.18.2@sha256:899c2e3a7bf8b4aea588ba4ca398ce4ca1626479ab96881aa69ad6a18964fae1 command: server start restart: always environment: @@ -26,7 +26,7 @@ services: - "traefik.http.services.server.loadbalancer.server.port=8080" front: - image: ghcr.io/updatecli/udash-front:v0.25.0 + image: ghcr.io/updatecli/udash-front:v0.26.0@sha256:21d5c66f6872907f5a83da1a07bde6fa1b46bd74d7e56898b52f0a6b5a8e7654 restart: always volumes: - "./udash-front/config.json:/usr/share/nginx/html/config.json" @@ -39,7 +39,7 @@ services: - "traefik.http.services.front.loadbalancer.server.port=80" traefik: - image: traefik:v3.0 + image: traefik:v3.7@sha256:24841fe2de7304c149343d877d2923b4c8800a38ba015dea9174c23b20e344a0 command: - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" diff --git a/assets/code_example/docs/udash/quick-start/updatecli-compose.yaml b/assets/code_example/docs/udash/quick-start/updatecli-compose.yaml index 86f47672b..b3caeae62 100644 --- a/assets/code_example/docs/udash/quick-start/updatecli-compose.yaml +++ b/assets/code_example/docs/udash/quick-start/updatecli-compose.yaml @@ -1,3 +1,3 @@ policies: - name: Discover what could be updated here - policy: ghcr.io/updatecli/policies/autodiscovery/all:latest + policy: ghcr.io/updatecli/policies/autodiscovery/all:0.7.0@sha256:09bd79160f4ecda4d8323b25815a733d39334c22544aaa6ac5859f0d3ef9fa79 diff --git a/content/en/docs/udash/quick-start.adoc b/content/en/docs/udash/quick-start.adoc index a90d49dde..8f52772d1 100644 --- a/content/en/docs/udash/quick-start.adoc +++ b/content/en/docs/udash/quick-start.adoc @@ -71,8 +71,8 @@ the API is up. [NOTE] ==== -If the server exits on the first start, PostgreSQL was not ready yet. Run -`docker compose restart server`. +The server may restart a few times on the first start, while PostgreSQL gets ready. It recovers +on its own within a few seconds. ==== == 3. Point Updatecli at it