Skip to content

Configuration as code

An organisation’s curation — a connection’s modules and field annotations, the organisation’s helper libraries and shared templates, and a few organisation settings — can also live in a repository and be pushed with the SDK. Discovery still runs on the server; the bundle is the curation layered on top. People, tokens, the connections themselves and billing are managed in Sloose, not pushed.

product-config/
org.json date format, expression context, AI data policy
modules.json which discovered modules are enabled, in what order, with match defaults
fields/<module>.json descriptions, formats, aliases, Exclude from AI flags, default mappings
libraries/<name>/ manifest.json and src/index.ts
templates/<name>.json reusable import recipes, shared with the whole org

A file in templates/ is either a bare TemplateConfig — in which case the file name is the template’s name — or a wrapper with name, description and config. Older configurations are migrated to the current shape before they are validated, so a file written by an earlier build still pushes. Pushing again updates the same template rather than creating a second copy, and sharing a template with the organisation is a plan feature: a bundle carrying templates/ is refused with FEATURE_SHARED_TEMPLATES on a plan without it.

A template may enable a module modules.json leaves out — one saved from an import that was set up for a module since switched off, say. validate passes such a template and names the module in a warning, since only the push can say what becomes of it, against the connection. When the connection has the module — switched off, or gone from the CRM, removed or not — the template is stored and push prints a warning, because an import started from it keeps that module’s setup aside until the module is chosen again. When the connection has never had it, the push is refused with UNKNOWN_MODULES, naming the template and the module. The key is what counts: a template still naming a module by a key modules.json renames is refused too, since no module keeps the old key once the push applies it.

@sloose/sdk is not on npm yet, so the two commands below find nothing today. Until it is published, send the bundle to the API yourself: POST /orgs/{org}/config/bundle applies one, and POST /orgs/{org}/config/bundle/dry-run checks it exactly as a push would and writes nothing (both in the API reference, with an API token). What follows describes the CLI as it will be run.

npx @sloose/sdk validate ./product-config
npx @sloose/sdk push ./product-config --org <org id> --connection <id> --env production

A push can be made against a version, so a change somebody made in Sloose since is not overwritten unseen. --version-file <path> keeps the configuration’s version between runs: diff writes the version it read, and push sends it and writes back the version it made. When the organisation’s configuration has changed since, the push is refused, says the version it is at now, and leaves everything as it was: run diff to see what changed, then push again. When something else changes it while the push is being applied, the push lands and says so: if that change landed between the push’s own steps, it can vouch for no version and leaves the file as it was; if it landed as the push finished, it keeps the push’s version, which the organisation is already past. Either way, run diff before the next push. The file also names the organisation, the connection and the API it was read from, since every connection of an organisation shares one version: using it for a push anywhere else is refused, and so is naming a file that holds no version, rather than pushing without one. Or pass a version yourself with --if-match <n>. Without either, a push is unconditional.

A bundle is one connection’s. Its modules and field annotations are the modules and fields of one CRM, and an organisation may hold several — production and a sandbox, or a client’s CRM each — so a push and a diff name the connection they are about with --connection (or SLOOSE_CONNECTION). One that names none is refused, and the CLI prints the organisation’s connections to choose from; a connection’s id is also the last part of its Overview’s address, under Connections in the app bar. The helper libraries, the organisation settings and the templates in a bundle are the organisation’s, and a pushed template names no connection: it is chosen when an import is started from it.

push and diff authenticate with an organisation API token. An administrator creates one in Sloose under Settings → API tokens (or with POST /orgs/:org/tokens), copies the secret once, and stores it as SLOOSE_TOKEN in the CI environment or passes --token. The token is scoped to that one organisation with the admin role — or its creator’s, if lower (see API tokens); revoke it from the same screen and every pipeline using it stops.

validate checks the bundle against the published schemas (and its module and field references against a discovery snapshot, if you give it one with --discovery). A push is checked again on the server, against the connection’s discovery, so a module or field the connection has never had fails at push time, not at run time. A field the CRM has stopped returning is not refused: its module’s page keeps it, and its annotation is written.

A key in a file that Sloose does not read — dateFromat for dateFormat, say — is not refused either: a push leaves it out and answers as usual. validate names each such key by its file, as a warning, and diff says the same and prints no line for it, since no push would write it.

A fields/<module>.json is the module’s whole set of annotations. On a push, every field the CRM still returns that the file does not list has its annotation cleared: its description, its aliases, and anything else set on its page in Sloose. A module with no fields file is left as it is, and so is a field the CRM no longer returns.

The Hidden mark is the exception. A push writes it only where a file says it: excluded: true hides the field, excluded: false offers it again, and a field the file leaves out, or names without excluded, keeps the mark it has. So a field somebody hid on its module’s page stays hidden through every deploy, and a module’s excludedFields hides a field whether or not its fields file mentions it.

A push of what is already there changes nothing, version included. The registry, each module’s field annotations and the libraries are each written only where they differ from what the connection holds, and only a part written moves the configuration’s version. So a pipeline that pushes the same repository on every deploy does not tell anybody with Sloose open that the configuration changed, nor refuse their next save as a conflict.

Helper libraries are compiled and loaded before a push writes anything, so a library that does not load refuses the push whole, saying which and why. Each one is published as a new version only when it differs from what the organisation already has — a push that sends the same libraries every time makes no new versions and leaves the configuration’s version where it was. A library someone has taken over in the browser is left as it is, and the push says so in its warnings — it is not compiled or checked against the plan either, so a copy in the repository that no longer loads does not stop the rest of the push. A library the push makes is the repository’s, read only on its page. Pushing libraries needs a plan with the helper libraries feature; see Helper libraries.

Report a problem with this page

sloose.com