ProdgatorDocs
GatesRelease policies

Write a policy in Rego

Write release policies in Rego: the three policy styles, the result contract, tests, and how Prodgator runs your code.

Pas encore traduite. Cette page est affichée en anglais. Lire l’original en anglais.

Availability

Business plan and above. Anyone can view policies; creating and changing them needs the Edit release policies permission (Release managers and Org admins).

Writing policies in Rego

A policy can be written in three ways:

  • Form: form rules only.
  • Mixed: form rules plus custom rules written in Rego. Each custom rule is its own module in and defines .
  • Rego: one or more modules you write yourself, in , defining .

Prodgator compiles every policy with Open Policy Agent (OPA) when you save it. A policy that does not compile is not saved.

The result contract

In a Rego policy, is a list of objects:

FieldTypeMeaning
stringA short name for the rule. Optional in a custom rule's : without it, the result is labeled with the custom rule's package id.
string, , or .
booleanWhether a rejects the deployment.
stringShown on the deployment and in the GitHub comment (first 300 characters).
objectOptional details, stored with the evaluation.

A result with any other , or a value that is not a list, counts as .

package prodgator.policy

branch_status := "pass" if input.release.branch == "main"
else := "fail"

results := [{
	"rule": "main_only",
	"status": branch_status,
	"blocking": true,
	"reason": sprintf("deploying from %v", [input.release.branch]),
}]

Example: block self-approval

This policy fails a release when the only approval comes from the person who started the deployment. It compares with (the same object as ) by .

package prodgator.policy

other_approvals := [a |
	some a in input.approvals
	a.decision == "approved"
	a.prodgatorUserId != input.actor.prodgatorUserId
]

self_status := "pass" if count(other_approvals) > 0
else := "fail"

results := [{
	"rule": "no_self_approval",
	"status": self_status,
	"blocking": true,
	"reason": "needs an approval from someone other than the person who started the deployment",
}]

Example: a custom rule that reads attestations

In a mixed policy, a custom rule lives in its own module in and defines . This one passes when the Prodgator report action sent a trusted attestation named for the commit. Attestations come from ; see Release input document for every field. Send a custom attestation shows the CI side.

package prodgator.custom.change_ticket

approved := [a |
	some a in input.attestations
	a.kind == "custom"
	a.name == "change-ticket"
	a.trusted
	a.status == "pass"
]

result := {
	"status": "pass",
	"blocking": true,
	"reason": "change ticket approved",
} if count(approved) > 0

result := {
	"status": "fail",
	"blocking": true,
	"reason": "no approved change ticket attestation for this commit",
} if count(approved) == 0

Rule names

A custom rule in a mixed policy can have a display name. In the policy editor, fill in Display name next to the rule; the placeholder shows the default, which is the custom rule's id (from ). A name is 1 to 80 characters, trimmed, and cannot contain control, line-break, zero-width or bidirectional characters.

The display name comes only from the policy (the Display name field), never from Rego output. A non-empty string in a custom rule's sets the rule id shown; the package id is the fallback when is missing. The name appears in evaluations, on the deployment card, in the provenance tree, and in GitHub check run and approval comment output (escaped). Rule ids, Rego and policy keys do not change. Changing a name saves a new policy version. Names are left out of signed provenance statements, and AI features treat them as untrusted text.

A policy written only in Rego has no per-rule names. It shows rule types, or the string your Rego returns, if any.

Tests

A Rego policy can include test modules, with file names ending in . Prodgator runs them with on every save and refuses the save if a test fails.

package prodgator.policy_test

import data.prodgator.policy

test_main_passes if {
	policy.results[0].status == "pass" with input as {"release": {"branch": "main"}}
}

test_feature_branch_fails if {
	policy.results[0].status == "fail" with input as {"release": {"branch": "feature/x"}}
}

What Rego can use

Policies read only the input document described below. , the built-ins and are not available, and a policy that uses them does not compile. Size, time and memory limits are listed under Limits.

How Prodgator runs your Rego

  • Policies compile in a separate function that has no access to Prodgator's database and can only write compiled policies to storage. OPA runs there without the function's AWS credentials in its environment.
  • , the built-ins and are removed from OPA's capabilities, so a policy or test that calls them does not compile. Policies read only the input document.
  • The compiler can reach the internet (it does not run in a private network), but with network built-ins removed and no credentials, Rego has no way to use that access.
  • Compiled policies run as WebAssembly in a separate thread with a time and memory limit. A policy that exceeds a limit is stopped and its result is , which holds an enforce policy's deployment (wait) rather than approving it.

The input a policy reads is described in Release input document.

Sur cette page