feat: scaffold deployment-policies monorepo
ci / verify (push) Failing after 10m57s
ci / publish (push) Skipped

OPA/Rego deployment gateway policies: 5 rules (valid-environment,
require-approvals, no-self-approval, block-weekends, freeze-window),
a deploy-gate combining them, JSON schemas, scenario-driven tests,
Gitea Actions CI (verify + publish to generic registry), changesets
versioning, Makefile and scripts.
This commit is contained in:
jdevega
2026-09-15 19:05:03 +02:00
commit 59cacfdfab
48 changed files with 1703 additions and 0 deletions
@@ -0,0 +1,6 @@
{
"name": "require-approvals",
"version": "0.0.0",
"description": "Denies deployments that do not have a sufficient number of active approvals.",
"private": true
}
@@ -0,0 +1,34 @@
# METADATA
# schemas:
# - input: schema["input"]
# - data: schema["data"]
package rules.require_approvals
import rego.v1
default allow := false
allow if {
count(violations) == 0
}
violations contains {"code": "insufficient_approvals", "message": sprintf("need at least %d approval(s), got %d", [min_approvals, count(active_approvals)])} if {
cfg := data.deploygate.config[input.deployment.environment]
cfg.min_approvals > 0
count(active_approvals) < cfg.min_approvals
}
min_approvals := data.deploygate.config[input.deployment.environment].min_approvals if {
input.deployment
}
active_approvals contains approval if {
some approval in input.approvals
approval.active
}
violations contains {"code": "not_configured", "message": sprintf("no min_approvals configured for environment %q", [input.deployment.environment])} if {
input.deployment
not data.deploygate.config[input.deployment.environment].min_approvals
}
@@ -0,0 +1,12 @@
package rules.require_approvals_test
import data.rules.require_approvals as rule
import rego.v1
test_scenarios[scenario_name] if {
some scenario_name, scenario in data.scenarios
actual := {code | code := rule.violations[_].code} with input as scenario.input with data.deploygate as scenario.data
expected := {code | code := scenario.expect.violations[_]}
actual == expected
rule.allow == scenario.expect.allow with input as scenario.input with data.deploygate as scenario.data
}
@@ -0,0 +1,96 @@
{
"scenarios": {
"enough_approvals": {
"input": {
"deployment": {
"environment": "production",
"service": "payments-api",
"version": "1.2.3",
"requested_by": "alice",
"created_at": "2026-09-15T10:00:00Z"
},
"approvals": [
{"by": "bob", "at": "2026-09-15T09:00:00Z", "active": true},
{"by": "carol", "at": "2026-09-15T09:30:00Z", "active": true}
]
},
"data": {
"config": {
"production": {"min_approvals": 2}
}
},
"expect": {
"allow": true,
"violations": []
}
},
"insufficient_approvals": {
"input": {
"deployment": {
"environment": "production",
"service": "payments-api",
"version": "1.2.3",
"requested_by": "alice",
"created_at": "2026-09-15T10:00:00Z"
},
"approvals": [
{"by": "bob", "at": "2026-09-15T09:00:00Z", "active": true}
]
},
"data": {
"config": {
"production": {"min_approvals": 2}
}
},
"expect": {
"allow": false,
"violations": ["insufficient_approvals"]
}
},
"revoked_approval_not_counted": {
"input": {
"deployment": {
"environment": "production",
"service": "payments-api",
"version": "1.2.3",
"requested_by": "alice",
"created_at": "2026-09-15T10:00:00Z"
},
"approvals": [
{"by": "bob", "at": "2026-09-15T09:00:00Z", "active": true},
{"by": "carol", "at": "2026-09-15T09:30:00Z", "active": false}
]
},
"data": {
"config": {
"production": {"min_approvals": 2}
}
},
"expect": {
"allow": false,
"violations": ["insufficient_approvals"]
}
},
"no_approval_required": {
"input": {
"deployment": {
"environment": "development",
"service": "payments-api",
"version": "1.2.3",
"requested_by": "alice",
"created_at": "2026-09-15T10:00:00Z"
},
"approvals": []
},
"data": {
"config": {
"development": {"min_approvals": 0}
}
},
"expect": {
"allow": true,
"violations": []
}
}
}
}
@@ -0,0 +1,27 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": true,
"properties": {
"deploygate": {
"type": "object",
"additionalProperties": true,
"properties": {
"config": {
"type": "object",
"additionalProperties": {
"type": "object",
"additionalProperties": true,
"properties": {
"min_approvals": {"type": "integer", "minimum": 0}
}
}
}
}
},
"rules": {
"type": "object",
"additionalProperties": true
}
}
}
@@ -0,0 +1,26 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": true,
"properties": {
"deployment": {
"type": "object",
"additionalProperties": true,
"required": ["environment"],
"properties": {
"environment": {"type": "string"}
}
},
"approvals": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true,
"required": ["active"],
"properties": {
"active": {"type": "boolean"}
}
}
}
}
}