Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

## [0.2.1](https://github.com/gorules/cli/compare/cli-v0.2.0...cli-v0.2.1) (2026-03-20)


### Bug Fixes

* improve mcp context size ([#3](https://github.com/gorules/cli/issues/3)) ([5044971](https://github.com/gorules/cli/commit/5044971f8a7cce38b1acbfbb0e27ab433297c321))
- improve mcp context size ([#3](https://github.com/gorules/cli/issues/3)) ([5044971](https://github.com/gorules/cli/commit/5044971f8a7cce38b1acbfbb0e27ab433297c321))

## [0.2.0](https://github.com/gorules/cli/compare/cli-v0.1.0...cli-v0.2.0) (2026-03-01)

Expand Down
244 changes: 244 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,250 @@ Or run directly with npx (e.g. mcp start):
npx @gorules/cli mcp start
```

## Pulling rules into a pipeline

`gorules pull` resolves a target in BRMS and downloads the matching rules artifact. It is the
building block for shipping rules from BRMS into your own infrastructure: a CI job pulls the
artifact and uploads it wherever your runtime reads it from.

```bash
export GORULES_URL=https://acme.us1.gorules.io
export GORULES_TOKEN=... # project access token, read scope is enough

gorules pull --project pricing --target env:production --out ./dist
aws s3 cp ./dist/ s3://my-bucket/rules/live/ --recursive
```

### Targets

| Target | Resolves to |
| ------------------- | ------------------------------------------------- |
| `main` (default) | latest commit on the default branch |
| `branch:<branchId>` | latest commit on that branch |
| `commit:<commitId>` | that exact commit, pinned |
| `release:<version>` | that release, by semantic version or id |
| `env:<key>` | whichever release is deployed to that environment |

### Options

| Flag | Env | Description |
| --------------- | ----------------- | ------------------------------------------------------------------------------------------------- |
| `-p, --project` | `GORULES_PROJECT` | Project key or id |
| `-t, --target` | `GORULES_TARGET` | Target to resolve (default `main`) |
| `-o, --out` | | Output directory (default `.`) |
| `--unpack` | | Extract the archive instead of writing it |
| `--delete` | | With `--unpack`: delete files not in the artifact so the directory mirrors the target exactly |
| `--name` | | Output file name (zip) or sub-directory name (dir); defaults to the project key with no extension |
| `--current` | | Release or commit id you already hold; exits `3` when unchanged |
| `-u, --url` | `GORULES_URL` | BRMS URL |
| `--token` | `GORULES_TOKEN` | Access token |
| `--json` | | Print the result as JSON on stdout |

### Naming the output

The default writes `<project-key>` with **no** `.zip` suffix, because the agent's S3, GCS and Azure
Blob providers use the object name verbatim as the project key: upload `pricing.zip` and the agent
serves a project literally called `pricing.zip`.

The agent's local `zip` provider is the opposite -- it reads `<root>/<project>.zip` and strips the
suffix itself -- so that destination needs it back:

```bash
gorules pull --project pricing --name pricing.zip --out ./rules
```

With `--unpack`, `--name` is the sub-directory to extract into (default: the project key, which is
the layout the agent's `filesystem` provider expects). Pass `--name .` to extract straight into
`--out`, which is what you want when baking rules into a container image.

Extraction behaves like `aws s3 sync`: byte-identical files are left untouched, changed files are
written atomically (temp file + rename, so a concurrent reader never sees a partial write), and
files the artifact does not carry are preserved. Add `--delete` for `s3 sync --delete` semantics:
the directory mirrors the target exactly, so rules deleted in BRMS are deleted on disk too. As a
guard against wiping a directory it does not own, `--delete` refuses a non-empty destination that
has no `.config/project.json` from a previous pull, and deletions only run after every new file has
been written.

### Examples

Object storage that the agent watches -- one archive per project, no extension:

```bash
gorules pull --project pricing --target env:production --out ./dist
aws s3 cp ./dist/ s3://my-bucket/rules/live/ --recursive
```

A volume the agent reads with its `filesystem` provider -- unpacked, one directory per project:

```bash
gorules pull --project pricing --target env:production --out /srv/rules --unpack
# /srv/rules/pricing/...
```

Baked into a container image, pinned to an exact release so the build is reproducible:

```bash
gorules pull --project pricing --target release:1.4.2 --out ./rules --unpack --name .
# ./rules/*.json + ./rules/.config/project.json, ready for COPY
```

Scheduled job that does nothing when production has not moved:

```bash
gorules pull --project pricing --target env:production --current "$LAST_RELEASE_ID" --out ./dist
case $? in
0) aws s3 cp ./dist/ s3://my-bucket/rules/live/ --recursive ;;
3) echo "unchanged" ;;
*) exit 1 ;;
esac
```

### Exit codes

| Code | Meaning |
| ---- | ---------------------------------------------------- |
| `0` | Artifact downloaded |
| `1` | Error |
| `2` | Usage error (missing or invalid arguments) |
| `3` | Nothing to do (`--current` matched what is deployed) |
| `4` | No release is deployed to the target |

Pin the version in a pipeline rather than tracking `latest`:

```bash
npx @gorules/[email protected] pull --project pricing --target env:production
```

## GitHub Actions

Composite actions live under `actions/`, in this repository, so the tag you pin is the CLI version
you get.

```yaml
on:
workflow_dispatch:
inputs:
payload:
description: Set by BRMS when a webhook triggers the run; the action picks it up automatically
required: false
type: string

jobs:
rules:
runs-on: ubuntu-latest
steps:
- uses: gorules/cli/actions/[email protected]
id: rules
with:
url: https://acme.us1.gorules.io
token: ${{ secrets.GORULES_TOKEN }}
project: pricing
target: env:production
out: ./dist

- run: aws s3 cp ./dist/ s3://my-bucket/rules/live/ --recursive
if: steps.rules.outputs.changed == 'true'
```

| Input | Required | Description |
| ------------- | -------- | -------------------------------------------------------------------------------- |
| `url` | yes | BRMS URL |
| `token` | yes | Access token; pass a secret |
| `project` | yes\* | Project key or id; optional when `payload` is set |
| `target` | | Target to resolve (default `main`) |
| `out` | | Output directory (default `.`) |
| `name` | | Output file or sub-directory name |
| `unpack` | | `true` to extract the archive |
| `delete` | | With `unpack`, mirror the target exactly (delete stale files) |
| `current` | | Release or commit id already held |
| `payload` | | BRMS event payload; auto-detected from `workflow_dispatch`, set only to override |
| `cli-version` | | Version of `@gorules/cli` to run |

| Output | Description |
| -------------------------------- | --------------------------------------------------------------- |
| `changed` | `false` when `current` still matched, so nothing was downloaded |
| `release` / `version` / `commit` | What the target resolved to |
| `sha256` | Checksum of the downloaded artifact |
| `files` | JSON array of paths written |

The token is passed to the CLI as an environment variable rather than an argument, and masked in the
log. `changed` exists so a scheduled workflow can skip the upload when production has not moved.

## GitLab CI

`templates/gitlab-ci-pull.yml` defines a hidden job you extend:

```yaml
include:
- remote: 'https://raw.githubusercontent.com/gorules/cli/cli-v0.2.1/templates/gitlab-ci-pull.yml'

pull:rules:
extends: .gorules-pull
variables:
GORULES_PROJECT: pricing
GORULES_TARGET: env:production

publish:rules:
needs: ['pull:rules']
rules:
- if: $RULES_CHANGED == "true"
script:
- aws s3 cp dist/ s3://my-bucket/rules/live/ --recursive
```

`GORULES_URL` and `GORULES_TOKEN` are CI/CD variables; mask and protect the token. GitLab puts them
in the environment automatically, so nothing else is needed to wire them up. Optional job variables:
`GORULES_OUT` (default `dist`), `GORULES_NAME`, `GORULES_CURRENT`, `GORULES_UNPACK` and
`GORULES_DELETE` (both `'false'` by default), and `GORULES_CLI_VERSION`.

The job publishes `RULES_CHANGED`, `RULES_VERSION`, `RULES_RELEASE` and `RULES_SHA256` as a dotenv
report, so later jobs read them as ordinary variables.

## Azure Pipelines

`templates/azure-pipelines-pull.yml` is a job template you can reference directly:

```yaml
resources:
repositories:
- repository: gorules
type: github
name: gorules/cli
ref: refs/tags/cli-v0.2.1
endpoint: <your GitHub service connection>

jobs:
- template: templates/azure-pipelines-pull.yml@gorules
parameters:
url: https://acme.us1.gorules.io
project: pricing
target: env:production
azureSubscription: <your ARM service connection>
storageAccount: acmerules
container: rules
```

`GORULES_TOKEN` must exist as a secret pipeline variable or in a linked variable group. Azure
DevOps does not map secret variables into the environment automatically, which the template handles
by declaring it explicitly under `env:`.

The job sets `rulesChanged` and `rulesVersion` as pipeline variables for later stages to read.

## Triggered by BRMS

All three templates read `GRL_PAYLOAD` when it is present, which is what BRMS sends when a webhook
triggers the pipeline. The project and target then come from the event rather than from static
configuration, so one pipeline handles every project and environment:

| System | How the payload arrives |
| --------------- | --------------------------------------- |
| GitHub Actions | `inputs.payload` on `workflow_dispatch` |
| GitLab CI | `GRL_PAYLOAD` pipeline variable |
| Azure Pipelines | `GRL_PAYLOAD` run variable |

Without it, the configured `GORULES_PROJECT` and `GORULES_TARGET` are used, so the same file also
works for a manual or scheduled run.

## MCP Bridge

2
Expand Down
Loading
Loading