Read-only namespaces
Some namespaces in a model are not authored by hand: they are generated from an external schema — for example, a namespace produced by importing an XSD or a JSONJSON (JavaScript Object Notation). Text-based, language-independent format with key-value pairs (eg Name: Dave). schema. The generated Rune is a faithful representation of that schema, so editing those files manually would make the model drift away from the schema it is meant to mirror, silently breaking the correspondence between them.
To prevent that, such namespaces can be marked as read-only. They are listed in the
rune-config.yml, and a supplied GitHub workflow then fails any pull request that hand-edits a
read-only namespace — until someone deliberately decides to allow it. Nothing is locked forever;
the check is a safety net that turns an accidental manual edit into a conscious decision (and the
right way to update a read-only namespace is to re-run the import that generated it).
Not every generated namespace should be locked down. A namespace inferred from data samples rather than declared by a schema is meant to be refined by hand, and is marked as a generated namespace instead — the same workflow then reports changes to it rather than failing them.
Declaring the read-only namespaces
Read-only namespaces are declared in the namespaceConfig list of the rune-config.yml. Each entry
marks a namespace read-only with readOnly: true. The namespace is either an exact namespace or a
namespace followed by .*, which covers that namespace and all of its subnamespaces:
model:
name: My Model
namespaceConfig:
- namespace: com.rosetta.model.*
readOnly: true
- namespace: cdm.base.datetime
readOnly: true
In the example above, com.rosetta.model and every namespace under it are read-only, as is the
exact namespace cdm.base.datetime. Matching is segment-aware, so com.rosetta.model.* does not
match com.rosetta.modelling.
:::caution Patterns are not globs
Only these two forms are recognised (a bare *, matching everything, is the degenerate case of the
second). Anything else — com.rosetta*, a?c, a[bc] — is read as an exact namespace, which is to
say it matches nothing, and the check then fails on it as a stale pattern.
:::
A namespaceConfig entry ties together the aspects that apply to a namespace: besides readOnly, it
may also carry an external schemaConfig (populated when the namespace is generated by importing a
schema) and an origin marker recording which tool produced it.
The aspects are independent, so an entry can be read-only, schema-configured, marked as generated,
or any combination — and importing a schema can declare all of them in the same entry.
Adding the workflow
Add a workflow to the repository (for example .github/workflows/readonly-namespaces.yml) that
calls the reusable workflow on every pull request:
name: Read-only namespaces
on:
pull_request:
jobs:
readonly-namespaces:
uses: finos/rune-dsl/.github/workflows/verify-readonly-namespaces.yml@main
with:
config-path: rosetta-source/src/main/resources/rune-config.yml
root: rosetta-source/src/main/rosetta
The same workflow also runs the generated-namespace annotation as a further step of the same job, so a repository that uses generated namespaces needs no extra setup — and the check name a branch-protection rule pins to does not change.
config-path is the repository path of the rune-config.yml; the check fails if no file exists
there, so a mistyped path cannot silently disable the protection. root is optional and defaults to
the whole repository — pointing it at the folder holding the model's .rosetta sources keeps
samples, test fixtures and build output from being mistaken for model namespaces.
On each pull request the workflow maps every changed .rosetta file to its namespace and fails if a
change hand-edits a read-only namespace. The changes a schema import makes when it adds or removes a
read-only namespace together with its configuration are still allowed. It also fails if a configured
pattern matches no namespace at all, which catches stale or mistyped entries.
Making an intentional change
Sometimes a read-only namespace genuinely needs to change — for example, when a project admin rolls
out an updated import. There is no need to remove the workflow or change the namespace's readOnly
flag in namespaceConfig. Instead, the change can be accepted as a deliberate one-off by applying the
override-readonly-namespaces label (or whatever is configured via the bypass-label input) to
the pull request. The next run of the check sees the label, skips the namespace check, and records
a note explaining why. Because adding a label requires write access, the decision stays visible and
auditable in the pull request.
Once the change is merged, removing the label restores the check for future pull requests.
Parameters
The reusable workflow accepts the following inputs under with:.
| Input | Required | Default | Description |
|---|---|---|---|
config-path | Yes | — | Path to the rune-config.yml whose namespaceConfig entries marked readOnly: true define the read-only namespaces. The check fails if no file exists at this path. |
base-ref | No | The pull request base branch, otherwise the repository's default branch | The git ref that HEAD is compared against to determine which files changed. |
root | No | . | Directory scanned for the model's .rosetta files when verifying that every read-only pattern still matches a namespace. Changed files are checked repository-wide regardless. |
checker-ref | No | main | The ref of finos/rune-dsl from which the checker script is fetched. Pin this to a tag or commit to fix the version of the check. |
bypass-label | No | override-readonly-namespaces | The pull request label that, when present, skips the check to allow an intentional one-off change to a read-only namespace. |
generated-namespaces-docs-url | No | The generated namespaces page | Documentation link appended to every generated-namespace annotation. Applies to the annotation job only. |