Contributing¶
How a change gets from your editor to a tenant: author it, run the gate, open a pull request, merge, release. Every step is a command in tools/, and the gate tells you which one to run when it fails.
The flow¶
- Branch from
main, one concern per branch. A change that spans two packs is still one branch and one PR; per-pack commits are good hygiene, not a rule. - Author the change. Generated content is never edited by hand: a list emitted from a schema under
schemas/is changed by editing the schema and re-emitting; a page with a generated banner is changed by editing what it is generated from. - Gate with
python3 tools/check_contribution.pyfrom the repo root. It runs, per changed pack: contribution normalization, correlation-rule preflight, playbook condition lint,pack_prep(rule ids, JSON validity, dependency versions, manifest preflight,demisto-sdk validate),check_contracts, and the docs coverage check; then framework-wide:validate_shadow_mode --allandprep_docs --check; then it uploads the pack to the tenant in.envand verifies the content landed. A failure prints what to fix and the command to re-run. - Test on the tenant, happy and unhappy paths, before opening the PR. The gate proves the content is well-formed; only a run proves it works.
- Pull request against
main, with exactly one ofversion:patch,version:minor,version:majorwhen the change should cut a release, and[skip release]in the merge commit subject when it should not (the usual case while a release is being collected). No label means patch. - Merge by squash or rebase; merge commits are rejected. Merging to
mainwith a version label cuts an immutable GitHub Release per changed pack and rebuildspack_catalog.json; tenants install releases through the SOC Framework Pack Manager.
What is gated, and why¶
| Gate | Fails when | Why it exists |
|---|---|---|
normalize_contribution.py |
A tenant export carries _copy suffixes, UUID ids, a non-standard version |
Exports are not pack files until normalized |
correlation_rule_preflight.py |
A rule would be rejected by the platform with an uninformative error | The platform's 101704 says nothing; this says which field |
pack_prep.py → demisto-sdk validate --marketplace marketplacev2 |
Content breaks the SDK schema | The default marketplace silently skips XSIAM content |
check_contracts.py |
A Workflow playbook calls setIssue/setIncident, or writes another layer's namespace |
Layer contracts are what keep lifecycles vendor-generic |
check_readme_coverage.py |
A pack adds a script, list or list key that no page or README mentions | Capabilities nobody can find do not exist |
validate_shadow_mode.py --all |
A changing action ships live outside the documented policy | Shadow Mode is the safety contract |
prep_docs.py --check |
A generated page is stale | The site is built from the repo; drift is a lie on the site |
The CI PR gate repeats the scan for customer data, the version-label check, JSON integrity, shadow mode and SDK validation. It does not deploy anywhere: content reaches a tenant from your gate run, and production from a release.
Describing contract entries¶
The lifecycle contracts under schemas/soc-framework/ (SOCFrameworkPhaseContract_V3, the per-lifecycle normalize maps) render into the site's contract pages. Any entry may carry a description:; when present it appears as a column in the rendered table. Adding descriptions to the entries that lack them is the most useful small docs change there is, and needs no generator change: edit the YAML, re-emit, run prep_docs.py.
Pages in this section¶
- Tools reference: every script under
tools/, generated from its docstring. - Editing vendor contracts: the per-data-source YAML a vendor pack is generated from.
- CONTRIBUTING.md on GitHub: contributing content exported from a tenant through the GitHub web UI, with no terminal.