blob: 97870134342236a963021c688dca6626e0405051 [file] [view]
# The ASFYamlFeature class
The base class for any .asf.yaml feature plugin is the `ASFYamlFeature` class.
For each new feature, you should generate a subclass of this, optionally specifying
the [YAML schema](#the-yaml-schema), the [environment](#feature-environments) name,
and/or [priority](#priority-scheduling) you wish to attach to this feature.
Features are located in the [feature/](../feature/) directory and needs to be manually
enabled in [feature/__init__.py](../feature/__init__.py) prior to using.
## Example feature class
~~~python3
from asfyaml import ASFYamlFeature
import strictyaml
class ASFTestFeature(ASFYamlFeature, name="test", env="production", priority=4):
schema = strictyaml.Map(
{
"foo": strictyaml.Str(),
strictyaml.Optional("enable", default=False): strictyaml.Bool(),
},
)
def run(self):
# Do some things with the 'foo' directive in our section...
if not do_things(self.yaml.foo):
# Oh no, something bad! We better relay this back to .asf.yaml
# This will notify the git client as well as send an error report to
# private@project.apache.org.
raise Exception("Something bad happened")
~~~
This would create a new feature called `test` in .asf.yaml that is accessible to all repositories,
with a required directive, `foo` which MUST be set, as well as an optional boolean value, `enable`,
which defaults to `False`. It would also run at [priority](#priority-scheduling) `4`, meaning
slightly before the default placement of features.
A valid .asf.yaml configuration section for this feature could look like this:
~~~yaml
test:
foo: pony
enable: true
~~~
## The YAML Schema
YAML schemas are validated using [StrictYAML](https://hitchdev.com/strictyaml/)
For detailed documentation on how to construct a schema, see the
[StrictYAML Users Guide](https://hitchdev.com/strictyaml/using/)
A feature can associate itself with a schema by setting the `schema` variable inside the
sub-class:
~~~python3
from asfyaml import ASFYamlFeature
import strictyaml
class ASFSchemaTestFeature(ASFYamlFeature, name="schematest"):
schema = strictyaml.Map(
{
"email_address": strictyaml.Str(),
"howmany": strictyaml.Int(),
strictyaml.Optional("enable_feature", default=False): strictyaml.Bool(),
},
)
def run(self): # Only ever runs if the YAML is valid
pass
~~~
The above schema example would require `email_address` to be set to a string value,
`howmany` to be set to an integer value, and optionally `enable_feature` can be set
to True or False, with False being the default if not set. If the YAML in a
repository's .asf.yaml section for this feature does not conform to the schema, an
error will be thrown and the .asf.yaml processing will be stopped.
All features with schemas are validated before any feature is actually run, so having a
schema properly defined can help spot typical configuration errors early on.
## Priority Scheduling
Features are, by default, scheduled with a priority level of `5`. This means they will be run in
order of appearance in the YAML file. If a feature needs to run before or after other features,
you can specify a priority, where 0 is the greatest priority and 10 is the least priority.
A feature with priority of 1 will be run before the default group, whereas a feature with a
priority of `9` would run after the default group.
TO-DO: Allow priority relative to other features.
## Feature Environments
A Feature can be enabled for specific environments, meaning only repositories set (and allowed)
to use that environment will be able to run it. This can be used for protected features, as
well as for feature previews that should only be enabled for one or more specific repositories,
either during the initial testing, or because the feature is tailored to a specific project.
The default environment is `production`, which is available to all repositories.
A feature environment branch is declared in the subclass definition:
~~~python3
class ASFTestFeature(ASFYamlFeature, name="test", env="beta"):
pass
~~~
If a feature of the same type exists in the `production` environment, this environment will
override it for any repository that has `beta` set as their [.asf.yaml environment](testing.md):
~~~yaml
meta: # The `meta` section manages things like which environment to use for .asf.yaml
environment: beta
test:
foo: bar
~~~
Environments can be set for any branch of a repository, although some features will only work on
certain branches (such as the default branch or if a specific `whoami` is set).
You can also add multiple environments in order to merge several features:
~~~yaml
meta: # The `meta` section manages things like which environment to use for .asf.yaml
environments: # Note, this is a list instead of a single string
- foobar
- otherfoo
- quietmode
~~~
## Accessing Other Features
Any feature can perform lookups on other enabled features in the .asf.yaml instance by
iterating through the parent `ASFYamlInstance` object:
~~~python3
def run(self):
# Make sure there is a valid target set for GitHub Discussions, through the notifications feature.
if not (
# Notifications must be configured
self.instance.features.notifications
and "discussions" in self.instance.features.notifications.valid_targets # GH Discussions must have a validated mailing list destination.
):
raise Exception("You need to set up a 'discussions' notification target before enabling GitHub Discussions")
pass
~~~