Skip to content

Commit bd3aaa3

Browse files
authored
Merge branch 'main' into revisit-cas-backend-permissions
Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev>
2 parents dbec713 + 92f96b6 commit bd3aaa3

39 files changed

Lines changed: 2454 additions & 918 deletions
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
name: custom-builtin-functions
3+
description: Create a custom builtin function to be used in the Rego policy engine
4+
---
5+
6+
### Policy Engine Extension
7+
8+
The OPA/Rego policy engine supports custom built-in functions written in Go.
9+
10+
**Adding Custom Built-ins**:
11+
12+
1. **Create Built-in Implementation** (e.g., `pkg/policies/engine/rego/builtins/myfeature.go`):
13+
```go
14+
package builtins
15+
16+
import (
17+
"github.com/open-policy-agent/opa/ast"
18+
"github.com/open-policy-agent/opa/topdown"
19+
"github.com/open-policy-agent/opa/types"
20+
)
21+
22+
const myFuncName = "chainloop.my_function"
23+
24+
func RegisterMyBuiltins() error {
25+
return Register(&ast.Builtin{
26+
Name: myFuncName,
27+
Description: "Description of what this function does",
28+
Decl: types.NewFunction(
29+
types.Args(types.Named("input", types.S).Description("this is the input")),
30+
types.Named("result", types.S).Description("this is the result"),
31+
),
32+
}, myFunctionImpl)
33+
}
34+
35+
func myFunctionImpl(bctx topdown.BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
36+
// Extract arguments
37+
input, ok := operands[0].Value.(ast.String)
38+
if !ok {
39+
return fmt.Errorf("input must be a string")
40+
}
41+
42+
// Implement logic
43+
result := processInput(string(input))
44+
45+
// Return result
46+
return iter(ast.StringTerm(result))
47+
}
48+
49+
// Autoregisters on package load
50+
func init() {
51+
if err := RegisterMyBuiltins(); err != nil {
52+
panic(fmt.Sprintf("failed to register built-ins: %v", err))
53+
}
54+
}
55+
```
56+
57+
2. **Use in Policies** (`*.rego`):
58+
```rego
59+
package example
60+
import rego.v1
61+
62+
result := {
63+
"violations": violations,
64+
"skipped": false
65+
}
66+
67+
violations contains msg if {
68+
output := chainloop.my_function(input.value)
69+
output != "expected"
70+
msg := "Function returned unexpected value"
71+
}
72+
```
73+
74+
**Guidelines**:
75+
- Use `chainloop.*` namespace for all custom built-ins
76+
- Functions that call third party services should be marked as non-restrictive by adding the `NonRestrictiveBuiltin` category to the builtin definition
77+
- Always implement proper error handling and return meaningful error messages
78+
- Use context from `BuiltinContext` for timeout/cancellation support
79+
- Document function signatures and behavior in the `Description` field and parameter definitions

.github/workflows/release.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ jobs:
2525
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
2626
- name: Install Chainloop
2727
run: |
28-
curl -sfL https://dl.chainloop.dev/cli/install.sh | bash -s
28+
# Need the ee CLI to have access to project management capabilities
29+
curl -sfL https://dl.chainloop.dev/cli/install.sh | bash -s -- --ee
2930
3031
- name: Initialize Attestation
3132
id: init_attestation
@@ -161,6 +162,12 @@ jobs:
161162
gh release download ${{ github.ref_name }} -A tar.gz -O /tmp/source-code.tar.gz
162163
chainloop attestation add --name source-code --value /tmp/source-code.tar.gz --kind ARTIFACT --attestation-id ${{ env.ATTESTATION_ID }}
163164
165+
- name: Promote Chainloop Project Version
166+
run: |
167+
current_version="$(cat .chainloop.yml | awk '/^projectVersion:/ {print $2}')"
168+
# Rename the existing pre-release into the actual release name
169+
chainloop project version update --project ${CHAINLOOP_PROJECT_NAME} --name $current_version --new-name ${{ github.ref_name }} || true
170+
164171
- name: Bump Chart and Dagger Version
165172
run: .github/workflows/utils/bump-chart-and-dagger-version.sh deployment/chainloop extras/dagger ${{ github.ref_name }}
166173
- name: Bump Project Version

CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,4 +269,5 @@ Code reviews are required for all submissions via GitHub pull requests.
269269
- when creating PR message, keep it high-level, what functionality was added, don't add info about testing, no icons, no info about how the message was generated.
270270
- app/controlplane/api/gen/frontend/google/protobuf/descriptor.ts is a special case that we don't want to upgrade, so if it upgrades, put it back to main
271271
- when creating a commit or PR message, NEVER add co-authored by or generated by Claude code
272-
- any call to authorization Enforce done from the biz or svc layer must be done using biz.AuthzUseCase
272+
- any call to authorization Enforce done from the biz or svc layer must be done using biz.AuthzUseCase
273+
- if you modify a schema, remember to run `make migration_sync`

app/cli/cmd/workflow_workflow_run_describe.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ func workflowRunDescribeTableOutput(run *action.WorkflowRunItemFull) error {
128128
if wr.Reason != "" {
129129
gt.AppendRow(table.Row{"Failure Reason", wr.Reason})
130130
}
131+
gt.AppendRow(table.Row{"Policy Status", wr.PolicyStatus})
131132
gt.AppendRow(table.Row{"Runner Link", wr.RunURL})
132133

133134
if run.WorkflowRun.FinishedAt == nil {

app/cli/cmd/workflow_workflow_run_list.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright 2024 The Chainloop Authors.
2+
// Copyright 2024-2025 The Chainloop Authors.
33
//
44
// Licensed under the Apache License, Version 2.0 (the "License");
55
// you may not use this file except in compliance with the License.
@@ -33,7 +33,7 @@ func newWorkflowWorkflowRunListCmd() *cobra.Command {
3333
DefaultLimit: 50,
3434
}
3535

36-
var workflowName, projectName, status string
36+
var workflowName, projectName, status, policyStatus string
3737

3838
cmd := &cobra.Command{
3939
Use: "list",
@@ -44,6 +44,10 @@ func newWorkflowWorkflowRunListCmd() *cobra.Command {
4444
return fmt.Errorf("invalid status %q, please chose one of: %v", status, listAvailableWorkflowStatusFlag())
4545
}
4646

47+
if policyStatus != "" && !slices.Contains([]string{"all", "failed", "passed"}, policyStatus) {
48+
return fmt.Errorf("invalid policy-status %q, please chose one of: all, failed, passed", policyStatus)
49+
}
50+
4751
return nil
4852
},
4953
RunE: func(cmd *cobra.Command, args []string) error {
@@ -55,7 +59,8 @@ func newWorkflowWorkflowRunListCmd() *cobra.Command {
5559
Limit: paginationOpts.Limit,
5660
NextCursor: paginationOpts.NextCursor,
5761
},
58-
Status: status,
62+
Status: status,
63+
PolicyStatus: policyStatus,
5964
},
6065
)
6166
if err != nil {
@@ -85,6 +90,7 @@ func newWorkflowWorkflowRunListCmd() *cobra.Command {
8590
cmd.Flags().StringVar(&projectName, "project", "", "project name")
8691
cmd.Flags().BoolVar(&full, "full", false, "full report")
8792
cmd.Flags().StringVar(&status, "status", "", fmt.Sprintf("filter by workflow run status: %v", listAvailableWorkflowStatusFlag()))
93+
cmd.Flags().StringVar(&policyStatus, "policy-status", "", "filter by policy violations status: all, failed, passed")
8894
// Add pagination flags
8995
paginationOpts.AddFlags(cmd)
9096

@@ -97,7 +103,7 @@ func workflowRunListTableOutput(runs []*action.WorkflowRunItem) error {
97103
return nil
98104
}
99105

100-
header := table.Row{"ID", "Workflow", "Version", "State", "Created At", "Runner"}
106+
header := table.Row{"ID", "Workflow", "Version", "State", "Policy Status", "Created At", "Runner"}
101107
if full {
102108
header = append(header, "Finished At", "Failure reason")
103109
}
@@ -107,7 +113,7 @@ func workflowRunListTableOutput(runs []*action.WorkflowRunItem) error {
107113

108114
for _, p := range runs {
109115
wf := p.Workflow
110-
r := table.Row{p.ID, wf.NamespacedName(), versionString(p.ProjectVersion), p.State, p.CreatedAt.Format(time.RFC822), p.RunnerType}
116+
r := table.Row{p.ID, wf.NamespacedName(), versionString(p.ProjectVersion), p.State, p.PolicyStatus, p.CreatedAt.Format(time.RFC822), p.RunnerType}
111117

112118
if full {
113119
var finishedAt string

app/cli/documentation/cli-reference.mdx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3890,13 +3890,14 @@ chainloop workflow workflow-run list [flags]
38903890
Options
38913891

38923892
```
3893-
--full full report
3894-
-h, --help help for list
3895-
--limit int number of items to show (default 50)
3896-
--next string cursor to load the next page
3897-
--project string project name
3898-
--status string filter by workflow run status: [CANCELLED EXPIRED FAILED INITIALIZED SUCCEEDED]
3899-
--workflow string workflow name
3893+
--full full report
3894+
-h, --help help for list
3895+
--limit int number of items to show (default 50)
3896+
--next string cursor to load the next page
3897+
--policy-status string filter by policy violations status: all, failed, passed
3898+
--project string project name
3899+
--status string filter by workflow run status: [CANCELLED EXPIRED FAILED INITIALIZED SUCCEEDED]
3900+
--workflow string workflow name
39003901
```
39013902

39023903
Options inherited from parent commands

app/cli/internal/policydevel/eval.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,6 @@ func verifyMaterial(pol *v1.Policies, material *v12.Attestation_Material, materi
165165
}
166166

167167
func craftMaterial(materialPath, materialKind string, logger *zerolog.Logger) (*v12.Attestation_Material, error) {
168-
if fileNotExists(materialPath) {
169-
return nil, fmt.Errorf("%s: does not exists", materialPath)
170-
}
171168
backend := &casclient.CASBackend{
172169
Name: "backend",
173170
MaxSize: 0,

app/cli/pkg/action/workflow_run_list.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright 2024 The Chainloop Authors.
2+
// Copyright 2024-2025 The Chainloop Authors.
33
//
44
// Licensed under the Apache License, Version 2.0 (the "License");
55
// you may not use this file except in compliance with the License.
@@ -52,6 +52,7 @@ type WorkflowRunItem struct {
5252
ContractRevisionUsed int `json:"contractRevisionUsed"`
5353
ContractRevisionLatest int `json:"contractRevisionLatest"`
5454
ProjectVersion *ProjectVersion `json:"projectVersion,omitempty"`
55+
PolicyStatus string `json:"policyStatus,omitempty"`
5556
}
5657

5758
type ProjectVersion struct {
@@ -74,6 +75,7 @@ type WorkflowRunListOpts struct {
7475
WorkflowName, ProjectName string
7576
Pagination *PaginationOpts
7677
Status string
78+
PolicyStatus string
7779
}
7880
type PaginationOpts struct {
7981
Limit int
@@ -95,6 +97,18 @@ func (action *WorkflowRunList) Run(opts *WorkflowRunListOpts) (*PaginatedWorkflo
9597
req.Status = v
9698
}
9799

100+
// Map policy status string to proto enum
101+
if opts.PolicyStatus != "" {
102+
switch opts.PolicyStatus {
103+
case "failed":
104+
req.PolicyViolations = pb.PolicyViolationsFilter_POLICY_VIOLATIONS_FILTER_WITH_VIOLATIONS
105+
case "passed":
106+
req.PolicyViolations = pb.PolicyViolationsFilter_POLICY_VIOLATIONS_FILTER_WITHOUT_VIOLATIONS
107+
case "all":
108+
req.PolicyViolations = pb.PolicyViolationsFilter_POLICY_VIOLATIONS_FILTER_UNSPECIFIED
109+
}
110+
}
111+
98112
resp, err := client.List(context.Background(), req)
99113
if err != nil {
100114
return nil, err
@@ -145,6 +159,7 @@ func pbWorkflowRunItemToAction(in *pb.WorkflowRunItem) *WorkflowRunItem {
145159
Version: in.GetVersion().GetVersion(),
146160
Prerelease: in.GetVersion().GetPrerelease(),
147161
},
162+
PolicyStatus: humanizedPolicyStatus(in.HasPolicyViolations),
148163
}
149164

150165
if in.GetContractVersion() != nil {
@@ -158,6 +173,16 @@ func pbWorkflowRunItemToAction(in *pb.WorkflowRunItem) *WorkflowRunItem {
158173
return item
159174
}
160175

176+
func humanizedPolicyStatus(hasPolicyViolations *bool) string {
177+
if hasPolicyViolations == nil {
178+
return "N/A"
179+
}
180+
if *hasPolicyViolations {
181+
return "failed"
182+
}
183+
return "passed"
184+
}
185+
161186
func humanizedRunnerType(in v1.CraftingSchema_Runner_RunnerType) string {
162187
mapping := map[v1.CraftingSchema_Runner_RunnerType]string{
163188
*v1.CraftingSchema_Runner_RUNNER_TYPE_UNSPECIFIED.Enum(): "Unspecified",

0 commit comments

Comments
 (0)