# Roadie Backstage Entity Validator

> Using the Roadie Backstage Validator

*Published: 2022-06-29T12:00:00.0Z*


## Introduction

Roadie provides many additional utilities to make your experience with Backstage smoother. One of these is the [Backstage Entity Validator](https://github.com/RoadieHQ/backstage-entity-validator). With this validator you can confirm that the entity descriptor files you are creating for Backstage conform to the schema and can be ingested by Backstage. The validator does not check the validity of entity references such as the "owner" field.

The Entity Validator is enabled automatically when you install the Roadie GitHub app to your GitHub organization. The validator automatically checks your pull requests and validates Backstage files included in them. The GitHub app check files with `.yml` or `.yaml` type and that have an `apiVersion` containing a string `backstage.io`, thus indicating that it is a Backstage descriptor file.

## Configuration

The validator reads a `.roadierc` configuration file (YAML format) to control its behaviour. It looks for this file in two places, in order:

1. **Org-level config**: `{your-org}/.github/.roadierc` — applies to all repositories in your GitHub organization. The Roadie GitHub app must be installed on the `.github` repo for this to work. If the app isn't installed there or the file doesn't exist, it is silently ignored.
2. **Repo-level config**: `.roadierc` in the root of the repository being validated — takes precedence over org-level config for repo-specific overrides.

### Available options

```yaml
validator:
  disabled: true
  exclude:
    - "catalog-*.yaml"
  validationSchemaUrl: "https://example.com/schemas/entity-validation.json"
```

| Option                          | Description                                                                                                                          |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `validator.disabled`            | Set to `true` to skip validation entirely for this repo (or org-wide if set in `.github`).                                           |
| `validator.exclude`             | Glob patterns for files to skip. Org and repo `exclude` lists are merged together.                                                   |
| `validator.validationSchemaUrl` | URL to a custom JSON Schema used to validate entity. Useful for enforcing naming conventions or custom annotation rules. |

### Precedence rules

When both org-level and repo-level configs exist, they are merged as follows:

| Field                 | Behaviour                                                                                                   |
| --------------------- | ----------------------------------------------------------------------------------------------------------- |
| `disabled`            | Repo overrides org. Setting `disabled: false` in a repo runs validation even if the org config disables it. |
| `exclude`             | Both lists are concatenated — org excludes and repo excludes both apply.                                    |
| `validationSchemaUrl` | Repo overrides org. If the repo specifies a different URL, it wins.                                         |

### Examples

Disable validation for all repositories in your org:

```yaml
# {your-org}/.github/.roadierc
validator:
  disabled: true
```

Use a custom annotation schema org-wide, but allow a specific repo to override it:

```yaml
# {your-org}/.github/.roadierc
validator:
  validationSchemaUrl: "https://raw.githubusercontent.com/my-org/.github/main/schemas/entity-validation.json"
```

```yaml
# my-special-repo/.roadierc
validator:
  validationSchemaUrl: "https://raw.githubusercontent.com/my-org/.github/main/schemas/strict-validation.json"
```

### Testing a custom schema before rolling it out org-wide

Because a repo-level `.roadierc` overrides the org-level one, you can safely trial a new schema in a single repository without affecting anyone else:

1. Add the schema file to your `.github` repo (or any public URL) and merge it to the default branch.
2. In a test repository, add a `.roadierc` that points to the new schema URL:

```yaml
# my-test-repo/.roadierc
validator:
  validationSchemaUrl: "https://raw.githubusercontent.com/my-org/.github/main/schemas/entity-validation-candidate.json"
```

3. Open a pull request in that repo with entity files that should pass, and others that should fail. Verify the check results match your expectations.
4. Once you're happy, move the `validationSchemaUrl` into `{your-org}/.github/.roadierc` to apply it across the org, and remove the repo-level `.roadierc`.

### Custom schema example

The `validationSchemaUrl` must point to a [JSON Schema (draft-07)](https://json-schema.org/draft-07/json-schema-validation) file. The schema is run against the **entire entity object**, so you can add extra constraints on any field — `metadata.name`, `metadata.labels`, `spec.owner`, `metadata.annotations`, etc.

> **Important**: the custom schema is purely additive. It runs on top of Backstage's built-in structural validation (required fields, valid `kind`, correct `spec` shape for each kind). It cannot relax or remove those built-in rules — it can only add stricter ones.

The following example enforces two org-specific rules on top of the standard Backstage validation:

1. `metadata.name` must be lowercase kebab-case with an optional team prefix separated by a dot (e.g. `payments.checkout-service`)
2. All annotation keys must be prefixed with either `backstage.io/` or `my-org.com/`

```json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://raw.githubusercontent.com/my-org/.github/main/schemas/entity-validation.json",
  "type": "object",
  "properties": {
    "metadata": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "pattern": "^([a-z0-9]+\\.)?[a-z0-9][a-z0-9-]*[a-z0-9]$",
          "description": "Entity names must be lowercase kebab-case, optionally prefixed with a team name and dot (e.g. payments.checkout-service)"
        },
        "annotations": {
          "type": "object",
          "patternProperties": {
            "^(backstage\\.io|my-org\\.com)/": {
              "type": "string"
            }
          },
          "additionalProperties": false
        }
      }
    }
  }
}
```

With this schema in place, a PR containing the following would fail validation because the name uses uppercase and the annotation key is not from an allowed domain:

```yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: MyCheckoutService
  annotations:
    internal/owner: payments-team
spec:
  type: service
  lifecycle: production
  owner: group:payments
```

## Schemas

The validator is based on the published [Backstage schemas](https://backstage.io/docs/features/software-catalog/descriptor-format) which can be referred to in the Backstage docs along with two additional Kinds - [Repository](/docs/catalog/repositories/) and Product. A list of Kinds available in Roadie can be [found here](/docs/catalog/modeling-entities/).

## Further reading

1. The [Backstage Entity Validator repository](https://github.com/RoadieHQ/backstage-entity-validator).
2. [Backstage Entity Schemas](https://backstage.io/docs/features/software-catalog/descriptor-format)
