Skip to content

Repository scanning

Repository scanning is the capability that finds componecat.yaml descriptors in your repositories and turns them into catalog entities. It runs two ways that complement each other: a scheduled scan crawls your organizations looking for descriptors the catalog doesn’t know about yet, and a webhook re-ingests a descriptor the moment someone pushes a change to it.

Both are configured on a Git integration — see Integrations for connecting one. GitHub, GitLab, and Bitbucket all support repository scanning.

The connection’s credential is what reads your repositories, so it needs:

  • GitHub — read access to repository contents and code search across the organizations you want scanned. A personal access token or a GitHub App installation both work.
  • GitLab — the read_api scope (or api). A project access token covers a single project; use a personal access token to scan groups.
  • Bitbucket — an access token with read access to the workspaces and repositories you want scanned.

Private repositories that the credential can’t see are invisible to the scan.

On the integration’s page, open the Git Scanning card. Set the configuration first, then switch the capability on — the panel is editable while the capability is off, so the first run uses your settings.

Setting Meaning
Schedule (cron) How often the scan runs. Default 0 */6 * * * — every six hours.
Target Orgs/Groups Which organizations, groups, or workspaces to crawl. A comma-separated list, or * for everything the credential can see.
File Pattern Which files count as descriptors. Default componecat.yaml.

Use Run now on the card to trigger a scan immediately — handy right after you change the pattern or targets.

For each target, the scan asks the provider’s search API for repositories containing the descriptor filename, then lists each candidate repository’s files and matches them against your full pattern. Every match that the catalog doesn’t already track is queued for ingestion.

That last part is the important one: a scheduled scan ingests descriptors that are new to the catalog. Edits to a descriptor that already has an entity arrive through the webhook, or when someone selects Refresh on the entity’s Repository tab, which re-reads its descriptor on the spot.

The pattern is matched against each repository’s file listing with a small glob syntax: * matches within one path segment, ? matches a single character, and ** matches any number of segments.

Repository layout File Pattern Result
Descriptors anywhere in the repo (leave as the default) componecat.yaml matched at any depth.
One descriptor per top-level service directory services/*/componecat.yaml Matches services/api/componecat.yaml and siblings; a descriptor at the repo root or nested deeper is not discovered.
Services nested arbitrarily deep under one root services/**/componecat.yaml Matches at any depth under services/.
A different descriptor filename **/catalog-info.yaml Any depth, different filename.

A bare filename with no / matches at any depth. A pattern containing / is anchored to that directory structure.

Note that the pattern applies to scans only. Webhook ingestion always looks for files named componecat.yaml, at any depth, whatever pattern you set here — so a custom descriptor filename is discovered by scans but not by pushes.

The webhook is what makes the catalog update within seconds of a merge. Setting it up takes two values from Componecat and one webhook in your Git provider.

  1. Open the integration. The Webhook card at the top of its capabilities shows the endpoint URL to use, with a button to copy it. The URL ends in your integration’s id, so each connection has its own.
  2. In the same card, choose Rotate Webhook Secret and confirm. The new secret is shown once, so copy it before closing the dialog.
  3. Create the webhook in your Git provider with that URL and secret, as below. On GitLab you can use a signing token instead of the secret.

Rotating the secret invalidates the previous one immediately, so update the provider’s webhook configuration right away or pushes will be rejected.

Add a webhook on the repository or organization:

  • Payload URL — the endpoint from the integration page.
  • Content type — application/json.
  • Secret — the rotated secret.
  • Events — Pushes, and Pull requests if you use catalog intake.

Add a webhook under the project’s or group’s Webhooks settings:

  • URL — the endpoint from the integration page.
  • Trigger — Push events, and Merge request events if you use catalog intake.
  • Request authentication — a signing token (recommended) or a secret token, as below.

On GitLab 19.0 and later, use a signing token. GitLab signs every delivery with it and includes a timestamp, so Componecat can check that the push really came from your GitLab, wasn’t changed on the way, and isn’t an old delivery being replayed. GitLab generates the token and shows it only once:

  1. Under Configure request authentication, choose Generate signing token and copy the token. It starts with whsec_.
  2. In Componecat, open the integration’s Webhook card, choose Set Signing Token, paste the token, and save. The card then shows the signing token as Set. The token is stored encrypted and is never shown again; to change it, generate a new one in GitLab and choose Replace Signing Token.

On older GitLab versions, use a secret token instead: paste the rotated secret into Secret token. GitLab sends it with every delivery.

To move an existing webhook from a secret token to a signing token without missing a push, configure both for a while. Generate the signing token in GitLab and set it in Componecat while the secret token stays in place: signed deliveries are then checked against the signing token, and anything unsigned against the secret. Once deliveries succeed, remove the secret token from the GitLab webhook.

When a delivery is signed and the integration has a signing token, the signature alone decides: a delivery with a wrong or outdated signature is rejected even if it also carries the right secret token. Remove on the Webhook card deletes the stored signing token, after which deliveries are checked against the secret again.

Add a repository webhook:

  • URL — the endpoint from the integration page.
  • Secret — the rotated secret.
  • Triggers — Repository: Push, and if you use catalog intake, Pull Request: Created, Updated, Merged and Declined.

Bitbucket’s push events don’t list the files that changed, so a push re-reads the descriptor at the repository root. Descriptors in subdirectories of a Bitbucket repository are discovered by scheduled scans instead.

Only pushes to the repository’s default branch are processed; anything else is acknowledged and ignored, except a push to a branch a catalog intake opened, which shows that someone else committed to its merge request. Pull request and merge request events are read only for catalog intake, and other events are acknowledged and ignored. Componecat looks at the files the push touched, picks out every componecat.yaml among them (at any depth), and queues one ingestion per descriptor — so a monorepo push that changes three descriptors updates three entities independently. Deleting a descriptor is a change too, and is processed the same way.

If the integration also backs your organization’s wiki, the same webhook keeps the wiki up to date: a push to the wiki’s branch that changes its Markdown files starts a wiki sync. See Sync on push.

The Git Scanning card lists recent runs while the capability is enabled, with a status, timestamps, a discovered count — descriptors newly queued for ingestion in that run — and any errors. Errors are per repository: a repository the credential can’t read, or one whose file listing failed, is recorded and the scan carries on with the rest.

Runs in progress refresh automatically, so a Run now shows its result without a reload.

A scan only queues descriptors for ingestion, so a discovered descriptor can still fail. The ingestion log shows how each one ended.

The Ingestion Log on the Git Scanning card records every attempt to ingest a descriptor, whichever way it started: a push, a scan, or a Refresh. A file with several entities gets a row for each. Each row shows the repository and file, the entity when it could be identified, what started the attempt, the commit, and a status:

Status Meaning
Succeeded The descriptor was applied to the catalog.
Warnings The descriptor was applied, with problems listed on the row, such as a relationship target that doesn’t exist yet.
Failed The descriptor wasn’t applied. The row says why.
Removed The file was deleted, or no longer declares the entity. Its entities are marked missing.

A descriptor that fails before any entity exists has nowhere else to report, so this is the place to look when an entity you expected never appears. The log refreshes on its own, and keeps entries for 30 days. Pushes Componecat ignores, such as one to a non-default branch, never reach ingestion and aren’t listed; your Git provider’s webhook log has those.

Connecting a Git provider also enriches entities that link to a repository, even ones you created by hand. Componecat fetches the repository’s languages, README, default branch, and description, and tracks whether the repository is reachable, archived, or gone. Values you set manually aren’t overwritten. Without a matching integration this only works for public repositories — the integration’s credential is what makes private repositories readable.

When the integration has Git Scanning turned on, the same refresh also syncs the entity’s descriptor: the one it’s already synced from, or a componecat.yaml at the repository root. It runs when an entity is linked to a repository and when you select Refresh on its Repository tab. Entities managed by another integration, or released from their source, are left alone.

  • Pushes aren’t ingested. Check the delivery response in your Git provider’s webhook log. 401 means the signature didn’t match — usually a secret that was rotated on one side only. 404 means the URL is wrong, or the integration doesn’t have Git Scanning enabled.
  • Deliveries get 429. Componecat accepts up to 60 webhook deliveries a minute from each sending address, and pushes share that limit with pull request and merge request events, which an organization-level webhook sends many of. A refused push of a new componecat.yaml is found by the next scheduled scan. A refused push that changed a descriptor already in the catalog isn’t, because scans skip those: redeliver it from your Git provider’s webhook log, or select Refresh on the entity’s Repository tab. A refused pull request or merge request event needs nothing, because Componecat checks each open merge request itself as a backstop.
  • GitLab deliveries get 401 with a signing token. Make sure the token set in Componecat is the one currently on the GitLab webhook: generating a new signing token in GitLab replaces the old one, so set the new one in Componecat too. A webhook that has only a signing token needs it set in Componecat, or every delivery fails. A signed delivery is also rejected if its timestamp is more than five minutes away from Componecat’s clock, so on a self-managed GitLab, check that the server’s clock is right.
  • The push was accepted but nothing changed. The response body says why: a push to a non-default branch and a push that touched no componecat.yaml are both acknowledged and ignored.
  • A scan finds nothing. Confirm the target organizations are spelled as the provider knows them, that the credential can see those repositories, and that the file pattern matches where your descriptors actually live.
  • An existing descriptor’s changes never appear. Scheduled scans skip descriptors already tracked by the catalog; that path is the webhook’s job. Until the webhook is connected, Refresh on the entity’s Repository tab re-reads it.
  • A very large push. Webhook payloads above 512 KB are rejected. The scheduled scan still picks up anything new from such a push.
  • A scan keeps discovering the same descriptor. It’s failing to ingest, so the catalog still treats it as new. The ingestion log says why — see also When a descriptor fails to ingest.
  • A descriptor was found but the entity is wrong. That’s ingestion, not discovery — see Catalog as code for how descriptor and manual edits are reconciled.