Skip to main content

Keeping the catalog true

Every catalog you have used before went stale, and it went stale for the same reason: updating it was a separate job somebody had to remember, done by a different person on a different day from the one who changed the service.

Katado moves the entry into the repository, so the person changing the service and the person updating its description become the same person in the same commit. Your pipeline does the rest.

The file

Put katado.yaml at the root of a repository:

apiVersion: katado/v1
kind: Service
metadata:
name: payments-api
description: Takes card payments and reconciles them nightly.
tags: [go, pci, tier-1]
spec:
owner: Platform
type: api
version: "1.4.0"
dependsOn:
- ledger
- auth
consumers:
- checkout-web
resources:
- https://github.com/nimbitlabs/payments-api
- https://grafana.example.com/d/payments

A monorepo describes each service as its own document, separated by ---.

The fields that carry weight

metadata.name is the identity. Katado matches on it, so the same file synced a hundred times produces one entry rather than a hundred. Change the name and you get a new service, so treat it the way you would a primary key.

spec.owner is a team name, not an id — a repository should not have to know the catalog's internal keys to describe itself. If the team does not exist yet it is created, because otherwise adopting Katado would start with a chore of creating teams by hand before the first import is allowed to run.

spec.dependsOn names other services, also by name. They are resolved after every service in the file has been handled, so the order inside the file does not matter. Naming a service that has not been synced yet is a warning, not an error — the link is made once that service arrives. Without this, the order in which your teams adopted Katado would be something you had to coordinate.

Validate before you commit

Install the CLI and check the file on your own machine, where a mistake costs seconds rather than a failed pipeline:

katado validate            # defaults to ./katado.yaml
  payments-api                 owner Platform
ledger owner Platform
katado.yaml: 2 service(s), all valid

A bad file says exactly what is wrong and which document it is in:

error: katado.yaml: document 1: spec.owner is required — an unowned service
is the thing this catalog exists to prevent

Sync from your pipeline

The API takes the YAML file directly, so there is nothing to convert:

curl --fail-with-body -X PUT \
"https://api.katado.io/v1/catalog/sync?repo=$REPO_URL&dryRun=false" \
-H "Authorization: Bearer $KATADO_API_KEY" \
-H "Content-Type: application/yaml" \
--data-binary @katado.yaml

Or with the CLI, which validates first and prints a readable report:

katado sync --repo "$REPO_URL"

Two properties make this safe to run on every push:

  • It is idempotent. Running the same file twice creates one entry and reports the second run as unchanged. Reordering tags is not a change either, because a report that cries wolf on every commit is a report nobody reads.
  • It can plan without writing. Pass dryRun=true and you get the same report having changed nothing.

GitHub Actions

Plan on pull requests, apply on the default branch:

name: Catalog

on:
push:
branches: [main]
paths: ["katado.yaml"]
pull_request:
paths: ["katado.yaml"]

jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- env:
KATADO_API_KEY: ${{ secrets.KATADO_API_KEY }}
run: |
dry_run=false
[ "${{ github.event_name }}" = "pull_request" ] && dry_run=true
curl --fail-with-body -sS -X PUT \
"https://api.katado.io/v1/catalog/sync?repo=${{ github.repositoryUrl }}&dryRun=$dry_run" \
-H "Authorization: Bearer $KATADO_API_KEY" \
-H "Content-Type: application/yaml" \
--data-binary @katado.yaml

--fail-with-body matters: without it a rejected file fails the job with nothing to read, and the whole point is that the error names the line to fix.

GitLab CI

catalog:
image: curlimages/curl:latest
script:
- |
curl --fail-with-body -X PUT \
"https://api.katado.io/v1/catalog/sync?repo=$CI_PROJECT_URL&dryRun=false" \
-H "Authorization: Bearer $KATADO_API_KEY" \
-H "Content-Type: application/yaml" \
--data-binary @katado.yaml
rules:
- changes: [katado.yaml]

What the report tells you

  update     payments-api  (version, dependsOn)
unchanged ledger
create search
team Search (created)
warning search depends on "billing", which is not in the catalog yet —
the link will be made once it is
1 created, 1 updated, 1 unchanged

On a pull request this is the diff your reviewer reads: what merging would do to the catalog, before it does it.

Services created by hand

A service someone added through the interface and later describes in a repository is adopted, not duplicated. The report says so once, and from then on the file is the source of truth for that entry — the scorecard's "described in its repository" check starts passing, and the interface points editors at the repository rather than letting them change a field the next sync would overwrite.