Skip to content

Commit 04ddb1b

Browse files
authored
feat(policy): add policy develop init (#2229)
Signed-off-by: Sylwester Piskozub <[email protected]>
1 parent 560ff2e commit 04ddb1b

File tree

10 files changed

+876
-1
lines changed

10 files changed

+876
-1
lines changed

app/cli/cmd/policy.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//
2+
// Copyright 2025 The Chainloop Authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package cmd
17+
18+
import (
19+
"github.com/spf13/cobra"
20+
)
21+
22+
func newPolicyCmd() *cobra.Command {
23+
cmd := &cobra.Command{
24+
Use: "policy",
25+
Short: "Craft chainloop policies",
26+
}
27+
28+
cmd.AddCommand(newPolicyDevelopCmd())
29+
return cmd
30+
}

app/cli/cmd/policy_develop.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// Copyright 2025 The Chainloop Authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package cmd
17+
18+
import (
19+
"github.com/spf13/cobra"
20+
)
21+
22+
func newPolicyDevelopCmd() *cobra.Command {
23+
cmd := &cobra.Command{
24+
Use: "develop",
25+
Aliases: []string{"devel"},
26+
Short: `Tools for policy development
27+
Refer to https://docs.chainloop.dev/guides/custom-policies
28+
`,
29+
}
30+
31+
cmd.AddCommand(newPolicyDevelopInitCmd())
32+
return cmd
33+
}

app/cli/cmd/policy_develop_init.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// Copyright 2025 The Chainloop Authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package cmd
17+
18+
import (
19+
"fmt"
20+
21+
"github.com/chainloop-dev/chainloop/app/cli/internal/action"
22+
"github.com/spf13/cobra"
23+
)
24+
25+
func newPolicyDevelopInitCmd() *cobra.Command {
26+
var (
27+
force bool
28+
embedded bool
29+
name string
30+
description string
31+
directory string
32+
)
33+
34+
cmd := &cobra.Command{
35+
Use: "init",
36+
Short: "Initialize a new policy",
37+
Long: `Initialize a new policy by creating template policy files in the specified directory.
38+
By default, it creates chainloop-policy.yaml and chainloop-policy.rego files.`,
39+
Example: `
40+
# Initialize in current directory with separate files
41+
chainloop policy develop init
42+
43+
# Initialize in specific directory with embedded format and policy name
44+
chainloop policy develop init --directory ./policies --embedded --name mypolicy`,
45+
RunE: func(_ *cobra.Command, _ []string) error {
46+
if directory == "" {
47+
directory = "."
48+
}
49+
opts := &action.PolicyInitOpts{
50+
Force: force,
51+
Embedded: embedded,
52+
Name: name,
53+
Description: description,
54+
Directory: directory,
55+
}
56+
57+
policyInit, err := action.NewPolicyInit(opts, actionOpts)
58+
if err != nil {
59+
return fmt.Errorf("failed to initialize policy: %w", err)
60+
}
61+
62+
if err := policyInit.Run(); err != nil {
63+
return newGracefulError(err)
64+
}
65+
66+
logger.Info().Msg("Initialized policy files")
67+
68+
return nil
69+
},
70+
}
71+
72+
cmd.Flags().BoolVarP(&force, "force", "f", false, "overwrite existing files")
73+
cmd.Flags().BoolVar(&embedded, "embedded", false, "initialize an embedded policy (single YAML file)")
74+
cmd.Flags().StringVar(&name, "name", "", "name of the policy")
75+
cmd.Flags().StringVar(&description, "description", "", "description of the policy")
76+
cmd.Flags().StringVar(&directory, "directory", "", "directory for policy")
77+
return cmd
78+
}

app/cli/cmd/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ func NewRootCmd(l zerolog.Logger) *cobra.Command {
242242
rootCmd.AddCommand(newWorkflowCmd(), newAuthCmd(), NewVersionCmd(),
243243
newAttestationCmd(), newArtifactCmd(), newConfigCmd(),
244244
newIntegrationCmd(), newOrganizationCmd(), newCASBackendCmd(),
245-
newReferrerDiscoverCmd(),
245+
newReferrerDiscoverCmd(), newPolicyCmd(),
246246
)
247247

248248
// Load plugins if we are not running a subcommand

app/cli/documentation/cli-reference.mdx

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2703,6 +2703,180 @@ Options inherited from parent commands
27032703
-y, --yes Skip confirmation
27042704
```
27052705

2706+
## chainloop policy
2707+
2708+
Craft chainloop policies
2709+
2710+
Options
2711+
2712+
```
2713+
-h, --help help for policy
2714+
```
2715+
2716+
Options inherited from parent commands
2717+
2718+
```
2719+
--artifact-cas string URL for the Artifacts Content Addressable Storage API ($CHAINLOOP_ARTIFACT_CAS_API) (default "api.cas.chainloop.dev:443")
2720+
--artifact-cas-ca string CUSTOM CA file for the Artifacts CAS API (optional) ($CHAINLOOP_ARTIFACT_CAS_API_CA)
2721+
-c, --config string Path to an existing config file (default is $HOME/.config/chainloop/config.toml)
2722+
--control-plane string URL for the Control Plane API ($CHAINLOOP_CONTROL_PLANE_API) (default "api.cp.chainloop.dev:443")
2723+
--control-plane-ca string CUSTOM CA file for the Control Plane API (optional) ($CHAINLOOP_CONTROL_PLANE_API_CA)
2724+
--debug Enable debug/verbose logging mode
2725+
-i, --insecure Skip TLS transport during connection to the control plane ($CHAINLOOP_API_INSECURE)
2726+
-n, --org string organization name
2727+
-o, --output string Output format, valid options are json and table (default "table")
2728+
-t, --token string API token. NOTE: Alternatively use the env variable CHAINLOOP_TOKEN
2729+
-y, --yes Skip confirmation
2730+
```
2731+
2732+
### chainloop policy develop
2733+
2734+
Tools for policy development
2735+
Refer to https://docs.chainloop.dev/guides/custom-policies
2736+
2737+
Options
2738+
2739+
```
2740+
-h, --help help for develop
2741+
```
2742+
2743+
Options inherited from parent commands
2744+
2745+
```
2746+
--artifact-cas string URL for the Artifacts Content Addressable Storage API ($CHAINLOOP_ARTIFACT_CAS_API) (default "api.cas.chainloop.dev:443")
2747+
--artifact-cas-ca string CUSTOM CA file for the Artifacts CAS API (optional) ($CHAINLOOP_ARTIFACT_CAS_API_CA)
2748+
-c, --config string Path to an existing config file (default is $HOME/.config/chainloop/config.toml)
2749+
--control-plane string URL for the Control Plane API ($CHAINLOOP_CONTROL_PLANE_API) (default "api.cp.chainloop.dev:443")
2750+
--control-plane-ca string CUSTOM CA file for the Control Plane API (optional) ($CHAINLOOP_CONTROL_PLANE_API_CA)
2751+
--debug Enable debug/verbose logging mode
2752+
-i, --insecure Skip TLS transport during connection to the control plane ($CHAINLOOP_API_INSECURE)
2753+
-n, --org string organization name
2754+
-o, --output string Output format, valid options are json and table (default "table")
2755+
-t, --token string API token. NOTE: Alternatively use the env variable CHAINLOOP_TOKEN
2756+
-y, --yes Skip confirmation
2757+
```
2758+
2759+
#### chainloop policy develop help
2760+
2761+
Help about any command
2762+
2763+
Synopsis
2764+
2765+
Help provides help for any command in the application.
2766+
Simply type develop help [path to command] for full details.
2767+
2768+
```
2769+
chainloop policy develop help [command] [flags]
2770+
```
2771+
2772+
Options
2773+
2774+
```
2775+
-h, --help help for help
2776+
```
2777+
2778+
Options inherited from parent commands
2779+
2780+
```
2781+
--artifact-cas string URL for the Artifacts Content Addressable Storage API ($CHAINLOOP_ARTIFACT_CAS_API) (default "api.cas.chainloop.dev:443")
2782+
--artifact-cas-ca string CUSTOM CA file for the Artifacts CAS API (optional) ($CHAINLOOP_ARTIFACT_CAS_API_CA)
2783+
-c, --config string Path to an existing config file (default is $HOME/.config/chainloop/config.toml)
2784+
--control-plane string URL for the Control Plane API ($CHAINLOOP_CONTROL_PLANE_API) (default "api.cp.chainloop.dev:443")
2785+
--control-plane-ca string CUSTOM CA file for the Control Plane API (optional) ($CHAINLOOP_CONTROL_PLANE_API_CA)
2786+
--debug Enable debug/verbose logging mode
2787+
-i, --insecure Skip TLS transport during connection to the control plane ($CHAINLOOP_API_INSECURE)
2788+
-n, --org string organization name
2789+
-o, --output string Output format, valid options are json and table (default "table")
2790+
-t, --token string API token. NOTE: Alternatively use the env variable CHAINLOOP_TOKEN
2791+
-y, --yes Skip confirmation
2792+
```
2793+
2794+
#### chainloop policy develop init
2795+
2796+
Initialize a new policy
2797+
2798+
Synopsis
2799+
2800+
Initialize a new policy by creating template policy files in the specified directory.
2801+
By default, it creates chainloop-policy.yaml and chainloop-policy.rego files.
2802+
2803+
```
2804+
chainloop policy develop init [flags]
2805+
```
2806+
2807+
Examples
2808+
2809+
```
2810+
2811+
Initialize in current directory with separate files
2812+
chainloop policy develop init
2813+
2814+
Initialize in specific directory with embedded format and policy name
2815+
chainloop policy develop init --directory ./policies --embedded --name mypolicy
2816+
```
2817+
2818+
Options
2819+
2820+
```
2821+
--description string description of the policy
2822+
--directory string directory for policy
2823+
--embedded initialize an embedded policy (single YAML file)
2824+
-f, --force overwrite existing files
2825+
-h, --help help for init
2826+
--name string name of the policy
2827+
```
2828+
2829+
Options inherited from parent commands
2830+
2831+
```
2832+
--artifact-cas string URL for the Artifacts Content Addressable Storage API ($CHAINLOOP_ARTIFACT_CAS_API) (default "api.cas.chainloop.dev:443")
2833+
--artifact-cas-ca string CUSTOM CA file for the Artifacts CAS API (optional) ($CHAINLOOP_ARTIFACT_CAS_API_CA)
2834+
-c, --config string Path to an existing config file (default is $HOME/.config/chainloop/config.toml)
2835+
--control-plane string URL for the Control Plane API ($CHAINLOOP_CONTROL_PLANE_API) (default "api.cp.chainloop.dev:443")
2836+
--control-plane-ca string CUSTOM CA file for the Control Plane API (optional) ($CHAINLOOP_CONTROL_PLANE_API_CA)
2837+
--debug Enable debug/verbose logging mode
2838+
-i, --insecure Skip TLS transport during connection to the control plane ($CHAINLOOP_API_INSECURE)
2839+
-n, --org string organization name
2840+
-o, --output string Output format, valid options are json and table (default "table")
2841+
-t, --token string API token. NOTE: Alternatively use the env variable CHAINLOOP_TOKEN
2842+
-y, --yes Skip confirmation
2843+
```
2844+
2845+
### chainloop policy help
2846+
2847+
Help about any command
2848+
2849+
Synopsis
2850+
2851+
Help provides help for any command in the application.
2852+
Simply type policy help [path to command] for full details.
2853+
2854+
```
2855+
chainloop policy help [command] [flags]
2856+
```
2857+
2858+
Options
2859+
2860+
```
2861+
-h, --help help for help
2862+
```
2863+
2864+
Options inherited from parent commands
2865+
2866+
```
2867+
--artifact-cas string URL for the Artifacts Content Addressable Storage API ($CHAINLOOP_ARTIFACT_CAS_API) (default "api.cas.chainloop.dev:443")
2868+
--artifact-cas-ca string CUSTOM CA file for the Artifacts CAS API (optional) ($CHAINLOOP_ARTIFACT_CAS_API_CA)
2869+
-c, --config string Path to an existing config file (default is $HOME/.config/chainloop/config.toml)
2870+
--control-plane string URL for the Control Plane API ($CHAINLOOP_CONTROL_PLANE_API) (default "api.cp.chainloop.dev:443")
2871+
--control-plane-ca string CUSTOM CA file for the Control Plane API (optional) ($CHAINLOOP_CONTROL_PLANE_API_CA)
2872+
--debug Enable debug/verbose logging mode
2873+
-i, --insecure Skip TLS transport during connection to the control plane ($CHAINLOOP_API_INSECURE)
2874+
-n, --org string organization name
2875+
-o, --output string Output format, valid options are json and table (default "table")
2876+
-t, --token string API token. NOTE: Alternatively use the env variable CHAINLOOP_TOKEN
2877+
-y, --yes Skip confirmation
2878+
```
2879+
27062880
## chainloop version
27072881

27082882
Command line version
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//
2+
// Copyright 2025 The Chainloop Authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package action
17+
18+
import (
19+
"fmt"
20+
21+
"github.com/chainloop-dev/chainloop/app/cli/internal/policydevel"
22+
)
23+
24+
type PolicyInitOpts struct {
25+
Force bool
26+
Embedded bool
27+
Name string
28+
Description string
29+
Directory string
30+
}
31+
32+
type PolicyInit struct {
33+
*ActionsOpts
34+
opts *PolicyInitOpts
35+
}
36+
37+
func NewPolicyInit(opts *PolicyInitOpts, actionOpts *ActionsOpts) (*PolicyInit, error) {
38+
return &PolicyInit{
39+
ActionsOpts: actionOpts,
40+
opts: opts,
41+
}, nil
42+
}
43+
44+
func (action *PolicyInit) Run() error {
45+
initOpts := &policydevel.InitOptions{
46+
Directory: action.opts.Directory,
47+
Embedded: action.opts.Embedded,
48+
Force: action.opts.Force,
49+
Name: action.opts.Name,
50+
Description: action.opts.Description,
51+
}
52+
53+
if err := policydevel.Initialize(initOpts); err != nil {
54+
return fmt.Errorf("initializing policy: %w", err)
55+
}
56+
57+
return nil
58+
}

0 commit comments

Comments
 (0)