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.

Availability

Business plan and above. Anyone can view policies; creating and changing them needs the role.

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.
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

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.

On this page