taskcluster / taskgraph

Generates task dependency graphs for Taskcluster CI
Mozilla Public License 2.0
16 stars 42 forks source link

Create loader schemas for validating `kind.yml` files #148

Open ahal opened 2 years ago

ahal commented 2 years ago

The format of the kind.yml files is currently determined by loaders. We should provide schemas for what these loaders expect and validate kind.yml files against them. This would result in clearer expectations about what is allowed in these files.

This came from a request from :asuth who is looking into ways of integrating Taskgraph with Searchfox. He'd prefer the schema follow the JSON-schema spec. I'm not sure I'd want two separate schema validation methods being used (jsonschema + voluptuous), but maybe we could have a CI step to export the schema into the JSON-schema format or something.

ahal commented 1 year ago

Fixing this bug will involve creating a new voluptuous schema (like we have for transforms), here: https://github.com/taskcluster/taskgraph/blob/main/src/taskgraph/loader/transform.py

Then somewhere around here in generator.py, we'd evaluate the contents of the kind.yml against said schema. Possibly we could fetch the schema by looking for a common global variable in the loader module, or we could invent some more complex way of registering loaders..

Aakash017 commented 1 year ago

Hello @ahal I would like to work on this

Aakash017 commented 1 year ago

@ahal As per my understanding we have to create new schema using voluptuous and then evaluate all kind.yml files

ahal commented 1 year ago

Yes, that's correct! Though we'd only evaluate them as they are being loaded (we don't want to evaluate them all in row or anything like that, as they aren't always all needed).

Aakash017 commented 1 year ago

@ahal


from taskgraph.transforms.base import TransformSequence
from taskgraph.util.schema import Schema, optionally_keyed_by, resolve_keyed_by

_status_type = Any(
    "on-completed",
    "on-defined",
    "on-exception",
    "on-failed",
    "on-pending",
    "on-resolved",
    "on-running",
)

_recipients = [
    {
        Required("type"): "email",
        Required("address"): optionally_keyed_by("project", "level", str),
        Optional("status-type"): _status_type,
    },
    {
        Required("type"): "matrix-room",
        Required("room-id"): str,
        Optional("status-type"): _status_type,
    },
    {
        Required("type"): "pulse",
        Required("routing-key"): str,
        Optional("status-type"): _status_type,
    },
    {
        Required("type"): "slack-channel",
        Required("channel-id"): str,
        Optional("status-type"): _status_type,
    },
]

_route_keys = {
    "email": "address",
    "matrix-room": "room-id",
    "pulse": "routing-key",
    "slack-channel": "channel-id",
}
"""Map each type to its primary key that will be used in the route."""

NOTIFY_SCHEMA = Schema(
    {
        Exclusive("notify", "config"): {
            Required("recipients"): [Any(*_recipients)],
            Optional("content"): {
                Optional("email"): {
                    Optional("subject"): str,
                    Optional("content"): str,
                    Optional("link"): {
                        Required("text"): str,
                        Required("href"): str,
                    },
                },
                Optional("matrix"): {
                    Optional("body"): str,
                    Optional("formatted-body"): str,
                    Optional("format"): str,
                    Optional("msg-type"): str,
                },
                Optional("slack"): {
                    Optional("text"): str,
                    Optional("blocks"): list,
                    Optional("attachments"): list,
                },
            },
        },
        # Continue supporting the legacy schema for backwards compat.
        Exclusive("notifications", "config"): {
            Required("emails"): optionally_keyed_by("project", "level", [str]),
            Required("subject"): str,
            Optional("message"): str,
            Optional("status-types"): [_status_type],
        },
    },
    extra=ALLOW_EXTRA,
)

print(NOTIFY_SCHEMA({'classic': {'email': 'foo@example.com', 'password': 'bar'}}))

Is it something like this we have to evaluate or something else?