Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/kosli/assertApproval.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func newAssertApprovalCmd(out io.Writer) *cobra.Command {
Short: assertApprovalShortDesc,
Long: assertApprovalLongDesc,
Example: assertApprovalExample,
Deprecated: "this command is deprecated and will be removed in a future release.",
Deprecated: deprecatedCommandMsg,
PreRunE: func(cmd *cobra.Command, args []string) error {
err := RequireGlobalFlags(global, []string{"Org", "ApiToken"})
if err != nil {
Expand Down
14 changes: 8 additions & 6 deletions cmd/kosli/attestDecision.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/url"
"os"

"github.com/kosli-dev/cli/internal/docgen"
"github.com/kosli-dev/cli/internal/requests"
"github.com/spf13/cobra"
)
Expand All @@ -27,7 +28,7 @@ type attestDecisionOptions struct {
payload DecisionAttestationPayload
}

const attestDecisionShortDesc = `[BETA] Record a compliance decision against a control in a Kosli trail. `
const attestDecisionShortDesc = `Record a compliance decision against a control in a Kosli trail. `

const attestDecisionLongDesc = attestDecisionShortDesc + `
Use this command to record the outcome of evaluating a control as part of your delivery
Expand Down Expand Up @@ -92,11 +93,12 @@ func newAttestDecisionCmd(out io.Writer) *cobra.Command {
},
}
cmd := &cobra.Command{
Use: "decision [IMAGE-NAME | FILE-PATH | DIR-PATH]",
Short: attestDecisionShortDesc,
Long: attestDecisionLongDesc,
Example: attestDecisionExample,
Hidden: true,
Use: "decision [IMAGE-NAME | FILE-PATH | DIR-PATH]",
Short: attestDecisionShortDesc,
Long: attestDecisionLongDesc,
Example: attestDecisionExample,
Hidden: true,
Annotations: map[string]string{docgen.DocHiddenAnnotation: "", betaCLIAnnotation: ""},
PreRunE: func(cmd *cobra.Command, args []string) error {
err := CustomMaximumNArgs(1, args)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion cmd/kosli/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ func (o *docsOptions) run() error {
Name: cmd.CommandPath(),
Beta: isBeta(cmd),
Deprecated: isDeprecated(cmd),
DeprecMsg: cmd.Deprecated,
DeprecMsg: deprecationHint(cmd),
Hidden: isDocHidden(cmd),
Summary: cmd.Short,
Long: cmd.Long,
UseLine: cmd.UseLine(),
Expand Down
9 changes: 5 additions & 4 deletions cmd/kosli/evaluate.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/spf13/cobra"
)

const evaluateShortDesc = `[BETA] Evaluate data against Rego policies.`
const evaluateShortDesc = `Evaluate data against Rego policies.`

// Backtick breaks (`"` + "`x`" + `"`) are needed to embed markdown
// inline code spans inside raw string literals.
Expand Down Expand Up @@ -36,9 +36,10 @@ logic reusable across environments with different tolerances.`

func newEvaluateCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "evaluate",
Short: evaluateShortDesc,
Long: evaluateLongDesc,
Use: "evaluate",
Short: evaluateShortDesc,
Long: evaluateLongDesc,
Annotations: map[string]string{betaCLIAnnotation: ""},
}

// Add subcommands
Expand Down
2 changes: 1 addition & 1 deletion cmd/kosli/evaluateInput.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type evaluateInputOptions struct {
inputFile string
}

const evaluateInputShortDesc = `[BETA] Evaluate a local JSON input against a Rego policy.`
const evaluateInputShortDesc = `Evaluate a local JSON input against a Rego policy.`

const evaluateInputLongDesc = evaluateInputShortDesc + `
Read JSON from a file or stdin and evaluate it against a Rego policy.
Expand Down
2 changes: 1 addition & 1 deletion cmd/kosli/evaluateTrail.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/spf13/cobra"
)

const evaluateTrailShortDesc = `[BETA] Evaluate a trail against a policy.`
const evaluateTrailShortDesc = `Evaluate a trail against a policy.`

const evaluateTrailLongDesc = evaluateTrailShortDesc + `
Fetch a single trail from Kosli and evaluate it against a Rego policy.
Expand Down
2 changes: 1 addition & 1 deletion cmd/kosli/evaluateTrails.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/spf13/cobra"
)

const evaluateTrailsShortDesc = `[BETA] Evaluate multiple trails against a policy.`
const evaluateTrailsShortDesc = `Evaluate multiple trails against a policy.`

const evaluateTrailsLongDesc = evaluateTrailsShortDesc + `
Fetch multiple trails from Kosli and evaluate them together against a Rego policy.
Expand Down
2 changes: 1 addition & 1 deletion cmd/kosli/getApproval.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func newGetApprovalCmd(out io.Writer) *cobra.Command {
Long: getApprovalLongDesc,
Example: getApprovalExample,
Args: cobra.ExactArgs(1),
Deprecated: "this command is deprecated and will be removed in a future release.",
Deprecated: deprecatedCommandMsg,
PreRunE: func(cmd *cobra.Command, args []string) error {
err := RequireGlobalFlags(global, []string{"Org", "ApiToken"})
if err != nil {
Expand Down
68 changes: 68 additions & 0 deletions cmd/kosli/lifecycle_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package main

import (
"io"
"strings"
"testing"

"github.com/kosli-dev/cli/internal/docgen"
"github.com/spf13/cobra"
)

func TestLifecycleEvaluateIsBeta(t *testing.T) {
cmd := newEvaluateCmd(io.Discard)
if !isBeta(cmd) {
t.Error("expected evaluate command to be beta")
}
// subcommands inherit beta via parent walk
for _, sub := range cmd.Commands() {
if !isBeta(sub) {
t.Errorf("expected subcommand %q to inherit beta", sub.Name())
}
}
}

func TestLifecycleAttestDecisionIsBetaAndDocHidden(t *testing.T) {
global = &GlobalOpts{}
cmd := newAttestDecisionCmd(io.Discard)
if !cmd.Hidden {
t.Error("expected attest decision to stay Hidden")
}
if !isBeta(cmd) {
t.Error("expected attest decision to be beta")
}
if _, ok := cmd.Annotations[docgen.DocHiddenAnnotation]; !ok {
t.Error("expected attest decision to carry the docHidden annotation")
}
if !isDocHidden(cmd) {
t.Error("expected isDocHidden to be true for attest decision")
}
}

func TestDeprecationHint(t *testing.T) {
generic := &cobra.Command{Use: "x", Deprecated: deprecatedCommandMsg}
if got := deprecationHint(generic); got != "" {
t.Errorf("expected generic deprecation message suppressed, got %q", got)
}
custom := &cobra.Command{Use: "y", Deprecated: "use 'kosli snapshot paths' instead"}
if got := deprecationHint(custom); got != "use 'kosli snapshot paths' instead" {
t.Errorf("expected custom hint preserved, got %q", got)
}
none := &cobra.Command{Use: "z"}
if got := deprecationHint(none); got != "" {
t.Errorf("expected empty for non-deprecated command, got %q", got)
}
}

func TestLifecycleNoBetaTextPrefix(t *testing.T) {
global = &GlobalOpts{}
cmds := []*cobra.Command{
newEvaluateCmd(io.Discard),
newAttestDecisionCmd(io.Discard),
}
for _, c := range cmds {
if strings.Contains(c.Short, "[BETA]") {
t.Errorf("command %q still has [BETA] text prefix in Short", c.Name())
}
}
}
2 changes: 1 addition & 1 deletion cmd/kosli/listApprovals.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func newListApprovalsCmd(out io.Writer) *cobra.Command {
Long: listApprovalsLongDesc,
Example: listApprovalsExample,
Args: cobra.NoArgs,
Deprecated: "this command is deprecated and will be removed in a future release.",
Deprecated: deprecatedCommandMsg,
PreRunE: func(cmd *cobra.Command, args []string) error {
err := RequireGlobalFlags(global, []string{"Org", "ApiToken"})
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/kosli/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func newReportCmd(out io.Writer) *cobra.Command {
Use: "report",
Short: reportDesc,
Long: reportDesc,
Deprecated: "this command is deprecated and will be removed in a future release.",
Deprecated: deprecatedCommandMsg,
}

// Add subcommands
Expand Down
2 changes: 1 addition & 1 deletion cmd/kosli/reportApproval.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func newReportApprovalCmd(out io.Writer) *cobra.Command {
Short: reportApprovalShortDesc,
Long: reportApprovalLongDesc,
Example: reportApprovalExample,
Deprecated: "this command is deprecated and will be removed in a future release.",
Deprecated: deprecatedCommandMsg,
PreRunE: func(cmd *cobra.Command, args []string) error {
err := RequireGlobalFlags(global, []string{"Org", "ApiToken"})
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/kosli/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func newRequestCmd(out io.Writer) *cobra.Command {
Use: "request",
Short: requestDesc,
Long: requestDesc,
Deprecated: "this command is deprecated and will be removed in a future release.",
Deprecated: deprecatedCommandMsg,
}

// Add subcommands
Expand Down
2 changes: 1 addition & 1 deletion cmd/kosli/requestApproval.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func newRequestApprovalCmd(out io.Writer) *cobra.Command {
Short: requestApprovalShortDesc,
Long: requestApprovalLongDesc,
Example: requestApprovalExample,
Deprecated: "this command is deprecated and will be removed in a future release.",
Deprecated: deprecatedCommandMsg,
PreRunE: func(cmd *cobra.Command, args []string) error {
err := RequireGlobalFlags(global, []string{"Org", "ApiToken"})
if err != nil {
Expand Down
24 changes: 22 additions & 2 deletions cmd/kosli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"path/filepath"
"strings"

"github.com/kosli-dev/cli/internal/docgen"
"github.com/kosli-dev/cli/internal/requests"
"github.com/kosli-dev/cli/internal/security"
"github.com/kosli-dev/cli/internal/version"
Expand All @@ -16,6 +17,10 @@ import (
"github.com/spf13/viper"
)

const betaCLIAnnotation = "betaCLI"

const deprecatedCommandMsg = "this command is deprecated and will be removed in a future release."

var globalUsage = `The Kosli CLI.

Environment variables:
Expand Down Expand Up @@ -572,12 +577,12 @@ func bindFlags(cmd *cobra.Command, v *viper.Viper) {
}

func isBeta(cmd *cobra.Command) bool {
if _, ok := cmd.Annotations["betaCLI"]; ok {
if _, ok := cmd.Annotations[betaCLIAnnotation]; ok {
return true
}
var beta bool
cmd.VisitParents(func(cmd *cobra.Command) {
if _, ok := cmd.Annotations["betaCLI"]; ok {
if _, ok := cmd.Annotations[betaCLIAnnotation]; ok {
beta = true
}
})
Expand All @@ -588,6 +593,21 @@ func isDeprecated(cmd *cobra.Command) bool {
return cmd.Deprecated != ""
}

func isDocHidden(cmd *cobra.Command) bool {
_, ok := cmd.Annotations[docgen.DocHiddenAnnotation]
return ok
}

// deprecationHint returns the per-command migration hint to show on the docs
// page. The generic boilerplate is conveyed by the docs snippet, so it is
// suppressed here to avoid duplication; only custom hints are surfaced.
func deprecationHint(cmd *cobra.Command) string {
if cmd.Deprecated == deprecatedCommandMsg {
return ""
}
return cmd.Deprecated
}

const usageTemplate = `{{- if isBeta .}}Beta Feature:
{{.CommandPath}} is a beta feature.
Beta features provide early access to product functionality. These
Expand Down
12 changes: 7 additions & 5 deletions cmd/kosli/testdata/output/docs/mintlify/artifact.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
---
title: "artifact"
beta: false
deprecated: true
tag: "DEPRECATED"
description: "Report an artifact creation to a Kosli flow. "
---

<Warning>
**artifact** is deprecated. see kosli attest commands Deprecated commands will be removed in a future release.
</Warning>
import CliDeprecatedNotice from "/snippets/cli-deprecated-notice.mdx";

<CliDeprecatedNotice />

see kosli attest commands

## Synopsis

```shell
Expand Down
2 changes: 0 additions & 2 deletions cmd/kosli/testdata/output/docs/mintlify/snyk.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
---
title: "snyk"
beta: false
deprecated: false
description: "Report a snyk attestation to an artifact or a trail in a Kosli flow. "
---

Expand Down
7 changes: 7 additions & 0 deletions internal/docgen/formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ package docgen

import "github.com/spf13/cobra"

// DocHiddenAnnotation marks a hidden command that should still get a generated
// docs page (with hidden: true front matter), as opposed to internal commands
// that are never documented. cmd/kosli sets this annotation; the tree walker
// checks for its presence.
const DocHiddenAnnotation = "docHidden"

// CommandMeta holds metadata about a cobra command for doc generation.
type CommandMeta struct {
Name string
Beta bool
Deprecated bool
DeprecMsg string
Hidden bool
Summary string
Long string
UseLine string
Expand Down
10 changes: 6 additions & 4 deletions internal/docgen/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import (
// leaf command using the provided Formatter.
func GenMarkdownTree(cmd *cobra.Command, dir string, formatter Formatter, metaFn CommandMetaFunc) error {
for _, c := range cmd.Commands() {
// skip all unavailable commands except deprecated ones
if (!c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand()) && c.Deprecated == "" {
// skip unavailable commands, except deprecated ones and hidden-but-documented ones
_, docHidden := c.Annotations[DocHiddenAnnotation]
if (!c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand()) && c.Deprecated == "" && !docHidden {
continue
}
if err := GenMarkdownTree(c, dir, formatter, metaFn); err != nil {
Expand Down Expand Up @@ -60,8 +61,9 @@ func genMarkdownCustom(cmd *cobra.Command, w io.Writer, formatter Formatter, met
// Title
buf.WriteString(formatter.Title(name))

// Beta warning
if meta.Beta {
// Beta warning. Deprecated takes precedence (matching lifecycleTag), so a
// command that is somehow both renders only the deprecated notice.
if meta.Beta && !meta.Deprecated {
buf.WriteString(formatter.BetaWarning(name))
}

Expand Down
Loading
Loading