Early alpha — first iteration. This code and documentation are in a barely alpha state. Expect breaking changes, missing features, and rough edges.
A compliance control tool that enforces every commit in a release references a valid Jira ticket in an approved state. It ingests commit history and Jira ticket data, attests the result to a Kosli trail, and evaluates the trail against an OPA/Rego policy.
The tool has two parts:
- Ingestor (
src/) — a TypeScript CLI that walks the git commit range between two tags, extracts Jira ticket IDs from commit messages and branch names, fetches ticket details from Jira, and writes a JSON attestation file. - Policy (
business-traceability.rego) — an OPA/Rego policy evaluated by Kosli that checks every commit has at least one Jira ticket, that tickets exist, and that they are in a permitted state and issue type.
gitGraph
commit id: "A"
commit id: "B" tag: "BASE_TAG"
branch feature
commit id: "C"
branch hotfix
commit id: "P"
commit id: "Q"
commit id: "R"
checkout main
commit id: "I"
merge hotfix id: "J"
checkout feature
commit id: "D"
commit id: "F"
checkout main
commit id: "K"
merge feature id: "G"
commit id: "H" tag: "RELEASE_TAG"
commit id: "S"
commit id: "T"
npm install
npm run buildPlace in the working directory (or pass --config <path>):
{
"jiraConfigType": "cloud",
"exemptions": {
"serviceAccounts": ["svc_.*", "dependabot\\[bot\\]"]
}
}| Field | Values | Description |
|---|---|---|
jiraConfigType |
standard, cloud, safe |
Selects the permitted issue types and states for the target Jira instance |
exemptions.serviceAccounts |
array of regex strings | Commits whose author name or email matches any pattern are exempt from ticket requirements |
| Config | Permitted issue types | Example permitted states |
|---|---|---|
standard |
Story, Bug, Technical Task, Change, Problem, Improvement, New Feature, Sub-task | IN PROGRESS, CODE REVIEW, DONE, CLOSED, … |
cloud |
Story, Bug, Task, Sub-task, Improvement | IN PROGRESS, READY FOR VERIFY, DONE, … |
safe |
Story, Enabler, Feature, Bug, Task, Sub-task | BUILD (IP), PO ACCEPTED, DONE, CLOSED, … |
See business-traceability.rego for the full list of permitted states per config type.
| Variable | Required | Description |
|---|---|---|
CURRENT_TAG |
Yes | Git tag or SHA of the release being attested |
BASE_TAG |
No | Git tag or SHA of the previous release. Auto-resolved from Kosli when omitted. |
GITHUB_REPOSITORY |
Yes | owner/repo — recorded in the attestation for reference |
JIRA_API_URL |
Yes | Base URL of your Jira instance, e.g. https://your-org.atlassian.net |
JIRA_CRED |
Yes | Base64-encoded email:api-token (the ingestor prepends Basic) |
PROXY_URL |
No | HTTP/HTTPS proxy for Jira API calls |
KOSLI_FLOW |
No | Kosli flow name, used for base-tag auto-resolution |
KOSLI_ATTESTATION_NAME |
No | Defaults to bt-data |
CURRENT_TAG=v1.2.0 \
BASE_TAG=v1.1.0 \
GITHUB_REPOSITORY=your-org/your-repo \
JIRA_API_URL=https://your-org.atlassian.net \
JIRA_CRED=$(printf 'you@example.com:YOUR_API_TOKEN' | base64 -w 0) \
node dist/index.js --repo /path/to/repo --config ./bt.config.jsonThis writes att_data_<CURRENT_TAG>.json in the current directory.
| Flag | Default | Description |
|---|---|---|
--repo <path> |
cwd |
Path to the git repository |
--config <path> |
./bt.config.json |
Path to the config file |
--env-file <path> |
— | Load variables from a .env file |
--flow <name> |
$KOSLI_FLOW |
Kosli flow name for base-tag auto-resolution |
--first-parent |
off | Walk only the mainline (first-parent) history; omit to consider all commits including those from merged branches |
When BASE_TAG is not set and a --flow name is provided, the ingestor queries Kosli to find the most recently attested commit in the first-parent history and uses that as the base. This means the first run of each release needs an explicit BASE_TAG; subsequent runs in the same flow resolve it automatically.
After running the ingestor, the typical Kosli workflow is:
# 1. Begin a trail for the release commit
kosli begin trail <commit-sha> \
--flow <flow-name> \
--commit <commit-sha> \
--repo-root /path/to/repo
# 2. Attest the ingestor output to the trail
kosli attest custom \
--type bt-data \
--name bt-data \
--attestation-data att_data_<tag>.json \
--trail <commit-sha> \
--flow <flow-name>
# 3. Evaluate the trail against the policy
kosli evaluate trail <commit-sha> \
--policy business-traceability.rego \
--flow <flow-name> \
--output json > eval_result.json
# 4. Attest the evaluation result
kosli attest generic \
--name bt-result \
--user-data eval_result.json \
--attachments business-traceability.rego \
--compliant=true \
--trail <commit-sha> \
--flow <flow-name>See simulate.sh for a complete end-to-end example.
The Rego policy (business-traceability.rego) enforces the following rules for every non-exempt commit:
| Rule | Violation message |
|---|---|
| Commit has at least one Jira ID | no Jira ticket ID found in commit message or branch name |
| Referenced ticket exists in Jira | was not found |
| Jira API was reachable | could not be fetched |
| Ticket issue type is permitted | non-permitted issue type |
| Ticket status is permitted | non-permitted state |
For sub-tasks, both the sub-task and its parent are evaluated against all rules.
npm run test:rego
# or directly:
opa test business-traceability.rego business-traceability_test.rego -vnpm testUnit tests cover the extractor (Jira ID parsing) and Jira fetcher (found, not found, fetch error, sub-tasks, deduplication).
Before the first attestation, register the bt-data custom type:
KOSLI_API_TOKEN=<token> KOSLI_ORG=<your-org> ./setup-kosli-attestation-type.shThis only needs to be done once per Kosli organisation.
simulate.sh replays a sequence of historical staging tags through the full ingest → attest → evaluate pipeline against a real Jira and Kosli environment:
SIMULATE_REPO=/path/to/local-clone-of-the-test-repo ./simulate.shThe script reads credentials from files in the repository root (these are gitignored):
| File | Contents |
|---|---|
kosli.key |
Kosli API token |
jira.key |
Jira API token |
Each iteration processes one tag-pair (previous → current):
- Begins a Kosli trail for the current commit
- Runs the ingestor (auto-resolving
BASE_TAGfrom Kosli on all but the first iteration) - Attests
att_data_<tag>.jsonto the trail - Evaluates the trail against the policy
- Attests the evaluation result (compliant or not) to the trail
Each run creates a uniquely named flow (business-traceability-<timestamp>) so runs do not interfere with each other.
.
├── src/
│ ├── index.ts # CLI entry point
│ ├── config.ts # Env var and config file loading
│ ├── git.ts # Git log parsing and SHA resolution
│ ├── extractor.ts # Jira ID extraction from text
│ ├── jira.ts # Jira REST API fetcher (two-phase)
│ ├── reporter.ts # Writes att_data_*.json
│ ├── kosli.ts # Kosli CLI wrapper for trail listing
│ ├── baseTagResolver.ts # Auto-resolves BASE_TAG from Kosli
│ └── types.ts # Shared TypeScript interfaces
├── tests/
│ ├── extractor.test.ts
│ └── jira.test.ts
├── business-traceability.rego # OPA policy
├── business-traceability_test.rego # OPA policy tests
├── bt.config.json # Jira config and exemptions
├── jsonschema.json # JSON Schema for att_data_*.json
├── setup-kosli-attestation-type.sh # One-time Kosli setup
├── simulate.sh # End-to-end simulation script
├── package.json
└── tsconfig.json