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
53 changes: 30 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ esac
Pin the version in a pipeline rather than tracking `latest`:

```bash
npx @gorules/cli@0.2.1 pull --project pricing --target env:production
npx @gorules/cli@0.3.2 pull --project pricing --target env:production # x-release-please-version
```

## GitHub Actions
Expand All @@ -146,17 +146,19 @@ jobs:
rules:
runs-on: ubuntu-latest
steps:
- uses: gorules/cli/actions/pull@cli-v0.2.1
- uses: gorules/cli/actions/pull@cli-v0.3.2 # x-release-please-version
id: rules
with:
url: https://acme.us1.gorules.io
token: ${{ secrets.GORULES_TOKEN }}
project: pricing
target: env:production
# project and target normally arrive in the BRMS payload; set them
# only for runs that have none (manual without payload, schedules)
out: ./dist

- run: aws s3 cp ./dist/ s3://my-bucket/rules/live/ --recursive
if: steps.rules.outputs.changed == 'true'
- name: Deploy
env:
PROJECT: ${{ steps.rules.outputs.project }}
run: aws s3 cp "./dist/$PROJECT" "s3://my-bucket/rules/$PROJECT"
```

| Input | Required | Description |
Expand All @@ -175,6 +177,7 @@ jobs:

| Output | Description |
| -------------------------------- | --------------------------------------------------------------- |
| `project` / `target` | What was pulled, payload-aware |
| `changed` | `false` when `current` still matched, so nothing was downloaded |
| `release` / `version` / `commit` | What the target resolved to |
| `sha256` | Checksum of the downloaded artifact |
Expand All @@ -189,20 +192,19 @@ log. `changed` exists so a scheduled workflow can skip the upload when productio

```yaml
include:
- remote: 'https://raw.githubusercontent.com/gorules/cli/cli-v0.2.1/templates/gitlab-ci-pull.yml'
- remote: 'https://raw.githubusercontent.com/gorules/cli/cli-v0.3.2/templates/gitlab-ci-pull.yml' # x-release-please-version

pull:rules:
extends: .gorules-pull
variables:
GORULES_PROJECT: pricing
GORULES_TARGET: env:production
# project and target normally arrive in the BRMS payload (GRL_PAYLOAD);
# set GORULES_PROJECT / GORULES_TARGET only for runs that have none

publish:rules:
needs: ['pull:rules']
rules:
- if: $RULES_CHANGED == "true"
script:
- aws s3 cp dist/ s3://my-bucket/rules/live/ --recursive
# dotenv variables are not visible in rules: (evaluated before jobs run) -
# gate in script when using scheduled pulls with GORULES_CURRENT
- aws s3 cp "dist/$RULES_PROJECT" "s3://my-bucket/rules/$RULES_PROJECT"
```

`GORULES_URL` and `GORULES_TOKEN` are CI/CD variables; mask and protect the token. GitLab puts them
Expand All @@ -217,26 +219,31 @@ anything.

## Azure Pipelines

`templates/azure-pipelines-pull.yml` is a job template you can reference directly:
`templates/azure-pipelines-pull.yml` is a steps template: it pulls the artifact and sets result
variables (`rulesChanged`, `rulesProject`, `rulesTarget`, `rulesVersion`, `rulesRelease`,
`rulesSha256`), and you append your own publish step in the same job:

```yaml
resources:
repositories:
- repository: gorules
type: github
name: gorules/cli
ref: refs/tags/cli-v0.2.1
ref: refs/tags/cli-v0.3.2 # x-release-please-version
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
- job: deploy_rules
pool:
vmImage: ubuntu-latest
steps:
- template: templates/azure-pipelines-pull.yml@gorules
parameters:
url: https://acme.us1.gorules.io
# project and target normally arrive in the BRMS payload (GRL_PAYLOAD)

- script: aws s3 cp "$(Build.ArtifactStagingDirectory)/rules/$(rulesProject)" "s3://my-bucket/rules/$(rulesProject)"
displayName: Deploy
```

`GORULES_TOKEN` must exist as a secret pipeline variable or in a linked variable group. Azure
Expand Down
11 changes: 11 additions & 0 deletions actions/pull/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ inputs:
default: 0.3.2 # x-release-please-version

outputs:
project:
description: Project that was pulled, payload-aware
value: ${{ steps.pull.outputs.project }}
target:
description: Target that was pulled, payload-aware
value: ${{ steps.pull.outputs.target }}
changed:
description: 'false when the target still matches the current input, true when an artifact was downloaded'
value: ${{ steps.pull.outputs.changed }}
Expand Down Expand Up @@ -116,6 +122,11 @@ runs:
[ "$INPUT_UNPACK" = "true" ] && args+=(--unpack)
[ "$INPUT_DELETE" = "true" ] && args+=(--delete)

# The effective values, payload-aware, so a workflow routes on what
# was actually pulled instead of re-parsing the payload.
echo "project=$GORULES_PROJECT" >> "$GITHUB_OUTPUT"
echo "target=${GORULES_TARGET:-main}" >> "$GITHUB_OUTPUT"

result="$RUNNER_TEMP/gorules-pull.json"

# Exit 3 means the target has not moved. That is a normal outcome for a
Expand Down
7 changes: 6 additions & 1 deletion release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
".": {
"package-name": "@gorules/cli",
"release-type": "node",
"extra-files": ["actions/pull/action.yml", "templates/gitlab-ci-pull.yml", "templates/azure-pipelines-pull.yml"]
"extra-files": [
"actions/pull/action.yml",
"templates/gitlab-ci-pull.yml",
"templates/azure-pipelines-pull.yml",
"README.md"
]
}
}
}
160 changes: 72 additions & 88 deletions templates/azure-pipelines-pull.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
# Azure Pipelines template: pull a rules artifact and publish it to Blob Storage.
# Azure Pipelines steps template: pull a rules artifact for your own publish step.
#
# resources:
# repositories:
# - repository: gorules
# type: github
# name: gorules/cli
# ref: refs/tags/cli-v0.2.1
# ref: refs/tags/cli-v0.3.2 # x-release-please-version
# 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
# container: rules
# storageAccount: acmerules
# azureSubscription: <your ARM service connection>
# - job: deploy_rules
# pool:
# vmImage: ubuntu-latest
# steps:
# - template: templates/azure-pipelines-pull.yml@gorules
# parameters:
# url: https://acme.us1.gorules.io
# - script: <publish $(Build.ArtifactStagingDirectory)/rules however you deploy>
#
# The template only pulls: it downloads the artifact and sets result variables
# (rulesChanged, rulesProject, rulesTarget, rulesVersion, rulesRelease,
# rulesSha256) for the steps you add after it. Publishing is yours.
#
# GORULES_TOKEN must exist as a secret variable, either in the pipeline or in a
# linked variable group.
Expand All @@ -30,11 +34,14 @@
parameters:
- name: url
type: string
# Project and target normally arrive in GRL_PAYLOAD; set them only for
# runs that have no payload (schedules), or pass them at queue time.
- name: project
type: string
default: ''
- name: target
type: string
default: main
default: ''
- name: out
type: string
default: $(Build.ArtifactStagingDirectory)/rules
Expand All @@ -48,91 +55,68 @@ parameters:
- name: delete
type: boolean
default: false
- name: cliVersion
type: string
default: 0.3.2 # x-release-please-version
- name: azureSubscription
# Release or commit id already held; when unchanged, rulesChanged is 'false'
- name: current
type: string
default: ''
- name: storageAccount
type: string
default: ''
- name: container
type: string
default: ''
- name: prefix
- name: cliVersion
type: string
default: rules/live
default: 0.3.2 # x-release-please-version

jobs:
- job: gorules_pull
displayName: Pull rules from GoRules
pool:
vmImage: ubuntu-latest
steps:
# Microsoft-hosted images already ship Node; self-hosted agents may not.
- task: NodeTool@0
displayName: Use Node 20
inputs:
versionSpec: '20.x'
steps:
# Microsoft-hosted images already ship Node; self-hosted agents may not.
- task: NodeTool@0
displayName: Use Node 24
inputs:
versionSpec: '24.x'

- script: |
set -euo pipefail
- script: |
set -euo pipefail

# BRMS passes GRL_PAYLOAD when it triggers the run, so the same
# template serves a manual run and a webhook-driven one. On a manual
# run the macro below does not resolve and Azure leaves the literal
# '$(GRL_PAYLOAD)' in place, so that exact string means "not set".
if [ -n "${GRL_PAYLOAD:-}" ] && [ "${GRL_PAYLOAD}" != '$(GRL_PAYLOAD)' ]; then
GORULES_PROJECT=$(node -e 'const p=JSON.parse(process.env.GRL_PAYLOAD);process.stdout.write(p.project?.key||p.projectId||"")')
GORULES_TARGET=$(node -e 'const p=JSON.parse(process.env.GRL_PAYLOAD);process.stdout.write(p.target||"main")')
export GORULES_PROJECT GORULES_TARGET
echo "Triggered by BRMS: $GORULES_PROJECT $GORULES_TARGET"
fi
# BRMS passes GRL_PAYLOAD when it triggers the run. On a manual run the
# macro below does not resolve and Azure leaves the literal
# '$(GRL_PAYLOAD)' in place, so that exact string means "not set".
if [ -n "${GRL_PAYLOAD:-}" ] && [ "${GRL_PAYLOAD}" != '$(GRL_PAYLOAD)' ]; then
GORULES_PROJECT=$(node -e 'const p=JSON.parse(process.env.GRL_PAYLOAD);process.stdout.write(p.project?.key||p.projectId||"")')
GORULES_TARGET=$(node -e 'const p=JSON.parse(process.env.GRL_PAYLOAD);process.stdout.write(p.target||"main")')
export GORULES_PROJECT GORULES_TARGET
echo "Triggered by BRMS: $GORULES_PROJECT $GORULES_TARGET"
fi

args=(pull --out "${{ parameters.out }}" --json)
if [ -n "${{ parameters.name }}" ]; then args+=(--name "${{ parameters.name }}"); fi
if [ "${{ lower(parameters.unpack) }}" = "true" ]; then args+=(--unpack); fi
if [ "${{ lower(parameters.delete) }}" = "true" ]; then args+=(--delete); fi
args=(pull --out "${{ parameters.out }}" --json)
if [ -n "${{ parameters.name }}" ]; then args+=(--name "${{ parameters.name }}"); fi
if [ -n "${{ parameters.current }}" ]; then args+=(--current "${{ parameters.current }}"); fi
if [ "${{ lower(parameters.unpack) }}" = "true" ]; then args+=(--unpack); fi
if [ "${{ lower(parameters.delete) }}" = "true" ]; then args+=(--delete); fi

# Exit 3 means the target has not moved: a normal outcome, not a failure
set +e
npx --yes "@gorules/cli@${{ parameters.cliVersion }}" "${args[@]}" > result.json
code=$?
set -e
# Exit 3 means the target has not moved: a normal outcome, not a failure
set +e
npx --yes "@gorules/cli@${{ parameters.cliVersion }}" "${args[@]}" > result.json
code=$?
set -e

if [ "$code" -eq 3 ]; then
echo "##vso[task.setvariable variable=rulesChanged]false"
echo "Target unchanged, nothing downloaded."
exit 0
fi
[ "$code" -ne 0 ] && exit "$code"
# The effective values, payload-aware, for the steps that follow
echo "##vso[task.setvariable variable=rulesProject]${GORULES_PROJECT:-}"
echo "##vso[task.setvariable variable=rulesTarget]${GORULES_TARGET:-main}"

echo "##vso[task.setvariable variable=rulesChanged]true"
echo "##vso[task.setvariable variable=rulesVersion]$(node -p "require('./result.json').version || ''")"
displayName: gorules pull
env:
GORULES_URL: ${{ parameters.url }}
GORULES_PROJECT: ${{ parameters.project }}
GORULES_TARGET: ${{ parameters.target }}
# Secret variables are NOT mapped into the environment automatically;
# without this line the token is simply absent.
GORULES_TOKEN: $(GORULES_TOKEN)
# Empty on a manual run; set by BRMS when it queues the pipeline
GRL_PAYLOAD: $(GRL_PAYLOAD)
if [ "$code" -eq 3 ]; then
echo "##vso[task.setvariable variable=rulesChanged]false"
echo "Target unchanged, nothing downloaded."
exit 0
fi
[ "$code" -ne 0 ] && exit "$code"

- task: AzureCLI@2
displayName: Upload to Blob Storage
condition: and(succeeded(), eq(variables['rulesChanged'], 'true'), ne('${{ parameters.container }}', ''))
inputs:
azureSubscription: ${{ parameters.azureSubscription }}
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
set -euo pipefail
az storage blob upload-batch \
--account-name "${{ parameters.storageAccount }}" \
--destination "${{ parameters.container }}/${{ parameters.prefix }}" \
--source "${{ parameters.out }}" \
--auth-mode login \
--overwrite
echo "##vso[task.setvariable variable=rulesChanged]true"
echo "##vso[task.setvariable variable=rulesVersion]$(node -p "require('./result.json').version || ''")"
echo "##vso[task.setvariable variable=rulesRelease]$(node -p "require('./result.json').release || ''")"
echo "##vso[task.setvariable variable=rulesSha256]$(node -p "require('./result.json').sha256 || ''")"
displayName: gorules pull
env:
GORULES_URL: ${{ parameters.url }}
GORULES_PROJECT: ${{ parameters.project }}
GORULES_TARGET: ${{ parameters.target }}
# Secret variables are NOT mapped into the environment automatically;
# without this line the token is simply absent.
GORULES_TOKEN: $(GORULES_TOKEN)
# Empty on a manual run; set by BRMS when it queues the pipeline
GRL_PAYLOAD: $(GRL_PAYLOAD)
10 changes: 5 additions & 5 deletions templates/gitlab-ci-pull.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# GitLab CI template: pull a rules artifact and publish it to your own storage.
#
# include:
# - remote: 'https://raw.githubusercontent.com/gorules/cli/cli-v0.2.1/templates/gitlab-ci-pull.yml'
# - remote: 'https://raw.githubusercontent.com/gorules/cli/cli-v0.3.2/templates/gitlab-ci-pull.yml' # x-release-please-version
#
# pull:rules:
# extends: .gorules-pull
# variables:
# GORULES_PROJECT: pricing
# GORULES_TARGET: env:production
# # project and target normally arrive in the BRMS webhook payload
# # (GRL_PAYLOAD); set GORULES_PROJECT / GORULES_TARGET only for runs
# # that have no payload, e.g. via the Run pipeline form or a schedule
#
# GORULES_URL and GORULES_TOKEN come from CI/CD variables; mask and protect the
# token. GitLab puts CI/CD variables in the environment automatically, so the
Expand All @@ -18,7 +18,7 @@
# and a webhook-driven one.

.gorules-pull:
image: node:20-alpine
image: node:24-alpine
variables:
GORULES_OUT: dist
GORULES_CLI_VERSION: '0.3.2' # x-release-please-version
Expand Down
Loading