Skip to content

Commit a629161

Browse files
authored
Merge branch 'main' into frogbot-github.com/go-git/go-git/v5-19d549bb86210063c5144bccb5cbe196
2 parents 8115db1 + ddc16ad commit a629161

8 files changed

Lines changed: 412 additions & 116 deletions

File tree

.github/workflows/e2e-tests.yml

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,62 @@ on:
44
push:
55
branches: [ '**' ] # Run on all branches
66
pull_request:
7-
branches: [ '**' ] # Run on all PRs
7+
branches: [ '**' ] # Run on internal PRs (skipped for fork PRs - see job `if`)
8+
pull_request_target:
9+
# Used to run E2E on fork PRs after a maintainer applies the `safe to test` label.
10+
types: [ labeled, synchronize ]
811
workflow_dispatch: # Allow manual trigger
912

13+
permissions:
14+
contents: read
15+
pull-requests: write # only used by reset-trust-on-sync to remove the label
16+
1017
jobs:
18+
# When new commits arrive on a PR that already carries `safe to test`, drop the
19+
# label so a maintainer must re-review the diff before E2E runs again with secrets.
20+
reset-trust-on-sync:
21+
name: Reset trust label on new commits
22+
if: >
23+
github.event_name == 'pull_request_target' &&
24+
github.event.action == 'synchronize' &&
25+
contains(github.event.pull_request.labels.*.name, 'safe to test')
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Remove "safe to test" label
29+
env:
30+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31+
run: |
32+
gh api -X DELETE \
33+
"repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels/safe%20to%20test" \
34+
|| true
35+
1136
e2e-tests-saas:
1237
name: E2E Tests on SaaS Environment
1338
runs-on: ubuntu-latest
1439
timeout-minutes: 30
40+
# Run on:
41+
# - push to any branch
42+
# - manual workflow_dispatch
43+
# - pull_request from the same repo (internal PR)
44+
# - pull_request_target only when the `safe to test` label is being applied
45+
if: >
46+
github.event_name == 'push' ||
47+
github.event_name == 'workflow_dispatch' ||
48+
(github.event_name == 'pull_request' &&
49+
github.event.pull_request.head.repo.full_name == github.repository) ||
50+
(github.event_name == 'pull_request_target' &&
51+
github.event.action == 'labeled' &&
52+
github.event.label.name == 'safe to test')
1553
1654
steps:
1755
- name: Checkout code
1856
uses: actions/checkout@v5
57+
with:
58+
# For pull_request_target the default ref is the base branch; we must
59+
# explicitly check out the PR head SHA so we actually test the PR's code.
60+
# For all other events the default ref is correct.
61+
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.ref }}
62+
persist-credentials: false
1963

2064
- name: Set up Go
2165
uses: actions/setup-go@v6
@@ -41,4 +85,3 @@ jobs:
4185
--jfrog.projectKey=${{ secrets.EVIDENCE_PROJECT_KEY }}
4286
env:
4387
CI: true
44-

.github/workflows/pr-labels.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ jobs:
1111
- name: Check if PR has allowed labels
1212
uses: jfrog/.github/actions/pr-labels@main
1313
with:
14-
allowed-labels: 'bug,breaking change,new feature,feature,enhancement,improvement,security,vulnerability,performance,optimization,documentation,docs,test,testing,dependencies,deps,ci,build,maintenance,refactor,chore,style,question,wontfix,ignore for release,feature request'
14+
allowed-labels: 'bug,breaking change,new feature,feature,enhancement,improvement,security,vulnerability,performance,optimization,documentation,docs,test,testing,dependencies,deps,ci,build,maintenance,refactor,chore,style,question,wontfix,ignore for release,feature request,safe to test'

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
## General
99

10-
**jfrog-cli-evidence** is a Go module that encompasses some of the Evidence commands of [JFrog CLI](https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli). This module is an Embedded JFrog CLI Plugin and is referenced as a Go module within the [JFrog CLI codebase](https://github.com/jfrog/jfrog-cli).
10+
**jfrog-cli-evidence** is a Go module that encompasses some of the Evidence commands of [JFrog CLI](https://docs.jfrog.com/integrations/docs/jfrog-cli). This module is an Embedded JFrog CLI Plugin and is referenced as a Go module within the [JFrog CLI codebase](https://github.com/jfrog/jfrog-cli).
1111

1212
## 🫱🏻‍🫲🏼 Contributions
1313

evidence/cli/command/command_cli.go

Lines changed: 136 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
package command
22

33
import (
4+
"encoding/json"
45
"errors"
56
"fmt"
7+
"io"
68
"os"
79
"slices"
810
"strings"
911

12+
"github.com/jedib0t/go-pretty/v6/table"
13+
"github.com/jedib0t/go-pretty/v6/text"
1014
"github.com/jfrog/jfrog-cli-evidence/evidence/cli/command/application"
1115
"github.com/jfrog/jfrog-cli-evidence/evidence/cli/command/artifacts"
1216
"github.com/jfrog/jfrog-cli-evidence/evidence/cli/command/build"
1317
"github.com/jfrog/jfrog-cli-evidence/evidence/cli/command/flags"
1418
"github.com/jfrog/jfrog-cli-evidence/evidence/cli/command/github"
15-
"github.com/jfrog/jfrog-cli-evidence/evidence/cli/command/interface"
16-
"github.com/jfrog/jfrog-cli-evidence/evidence/cli/command/package"
19+
_interface "github.com/jfrog/jfrog-cli-evidence/evidence/cli/command/interface"
20+
_package "github.com/jfrog/jfrog-cli-evidence/evidence/cli/command/package"
1721
"github.com/jfrog/jfrog-cli-evidence/evidence/cli/command/releasebundle"
1822
commandUtils "github.com/jfrog/jfrog-cli-evidence/evidence/cli/command/utils"
1923
evdConfig "github.com/jfrog/jfrog-cli-evidence/evidence/config"
24+
"github.com/jfrog/jfrog-cli-evidence/evidence/model"
2025

2126
commonCliUtils "github.com/jfrog/jfrog-cli-core/v2/common/cliutils"
2227
"github.com/jfrog/jfrog-cli-core/v2/common/commands"
28+
"github.com/jfrog/jfrog-cli-core/v2/common/format"
2329
pluginsCommon "github.com/jfrog/jfrog-cli-core/v2/plugins/common"
2430
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
2531
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
@@ -37,15 +43,23 @@ import (
3743
generateCmd "github.com/jfrog/jfrog-cli-evidence/evidence/generate"
3844
)
3945

46+
// responseCollector is implemented by evidence create commands that accumulate
47+
// CreateResponse values during Run(). The interface is checked via type assertion
48+
// after command execution so that printCreateEvidenceResponse can format output.
49+
type responseCollector interface {
50+
CollectedResponses() []*model.CreateResponse
51+
}
52+
4053
func GetCommands() []components.Command {
4154
return []components.Command{
4255
{
43-
Name: "create-evidence",
44-
Aliases: []string{"create"},
45-
Flags: flags.GetCommandFlags(flags.CreateEvidence),
46-
Description: create.GetDescription(),
47-
Arguments: create.GetArguments(),
48-
Action: createEvidence,
56+
Name: "create-evidence",
57+
Aliases: []string{"create"},
58+
Flags: flags.GetCommandFlags(flags.CreateEvidence),
59+
Description: create.GetDescription(),
60+
Arguments: create.GetArguments(),
61+
Action: createEvidence,
62+
SupportedFormats: []format.OutputFormat{format.Json, format.Table},
4963
},
5064
{
5165
Name: "get-evidence",
@@ -74,8 +88,10 @@ func GetCommands() []components.Command {
7488
}
7589
}
7690

77-
var execFunc = commands.Exec
78-
var ErrUnsupportedSubject = errors.New("unsupported subject")
91+
var (
92+
execFunc = commands.Exec
93+
ErrUnsupportedSubject = errors.New("unsupported subject")
94+
)
7995

8096
func createEvidence(ctx *components.Context) error {
8197
if err := validateCreateEvidenceCommonContext(ctx); err != nil {
@@ -90,23 +106,50 @@ func createEvidence(ctx *components.Context) error {
90106
return err
91107
}
92108

93-
if slices.Contains(evidenceType, flags.TypeFlag) || (slices.Contains(evidenceType, flags.BuildName) && slices.Contains(evidenceType, flags.TypeFlag)) {
94-
return github.NewEvidenceGitHubCommand(ctx, execFunc).CreateEvidence(ctx, serverDetails)
109+
outputFormat, err := ctx.GetOutputFormat()
110+
if err != nil {
111+
return err
95112
}
96113

97-
evidenceCommands := map[string]func(*components.Context, commandUtils.ExecCommandFunc) _interface.EvidenceCommands{
98-
flags.SubjectRepoPath: artifacts.NewEvidenceCustomCommand,
99-
flags.ReleaseBundle: releasebundle.NewEvidenceReleaseBundleCommand,
100-
flags.BuildName: build.NewEvidenceBuildCommand,
101-
flags.PackageName: _package.NewEvidencePackageCommand,
102-
flags.ApplicationKey: application.NewEvidenceApplicationCommand,
114+
// Wrap execFunc to capture collected responses after each command runs.
115+
var collectedResponses []*model.CreateResponse
116+
collectingExec := func(cmd commands.Command) error {
117+
if runErr := execFunc(cmd); runErr != nil {
118+
return runErr
119+
}
120+
if rc, ok := cmd.(responseCollector); ok {
121+
collectedResponses = append(collectedResponses, rc.CollectedResponses()...)
122+
}
123+
return nil
103124
}
104125

105-
if commandFunc, exists := evidenceCommands[evidenceType[0]]; exists {
106-
return commandFunc(ctx, execFunc).CreateEvidence(ctx, serverDetails)
126+
var createErr error
127+
if slices.Contains(evidenceType, flags.TypeFlag) || (slices.Contains(evidenceType, flags.BuildName) && slices.Contains(evidenceType, flags.TypeFlag)) {
128+
createErr = github.NewEvidenceGitHubCommand(ctx, collectingExec).CreateEvidence(ctx, serverDetails)
129+
} else {
130+
evidenceCommands := map[string]func(*components.Context, commandUtils.ExecCommandFunc) _interface.EvidenceCommands{
131+
flags.SubjectRepoPath: artifacts.NewEvidenceCustomCommand,
132+
flags.ReleaseBundle: releasebundle.NewEvidenceReleaseBundleCommand,
133+
flags.BuildName: build.NewEvidenceBuildCommand,
134+
flags.PackageName: _package.NewEvidencePackageCommand,
135+
flags.ApplicationKey: application.NewEvidenceApplicationCommand,
136+
}
137+
138+
if commandFunc, exists := evidenceCommands[evidenceType[0]]; exists {
139+
createErr = commandFunc(ctx, collectingExec).CreateEvidence(ctx, serverDetails)
140+
} else {
141+
return ErrUnsupportedSubject
142+
}
107143
}
108144

109-
return ErrUnsupportedSubject
145+
if createErr != nil {
146+
return createErr
147+
}
148+
149+
if outputFormat != "" {
150+
return printCreateEvidenceResponse(os.Stdout, outputFormat, collectedResponses)
151+
}
152+
return nil
110153
}
111154

112155
func getEvidence(ctx *components.Context) error {
@@ -487,3 +530,75 @@ func generateKeyPair(ctx *components.Context) error {
487530
cmd := generateCmd.NewGenerateKeyPairCommand(serverDetails, uploadKey, alias, keyFilePath, fileName)
488531
return cmd.Run()
489532
}
533+
534+
// tableFieldOrder defines the display order for create-evidence table output.
535+
var tableFieldOrder = []struct {
536+
key string
537+
value func(*model.CreateResponse) string
538+
}{
539+
{"repository", func(r *model.CreateResponse) string { return r.Repository }},
540+
{"path", func(r *model.CreateResponse) string { return r.Path }},
541+
{"name", func(r *model.CreateResponse) string { return r.Name }},
542+
{"uri", func(r *model.CreateResponse) string { return r.Uri }},
543+
{"sha256", func(r *model.CreateResponse) string { return r.Sha256 }},
544+
{"predicate_type", func(r *model.CreateResponse) string { return r.PredicateType }},
545+
{"predicate_category", func(r *model.CreateResponse) string { return r.PredicateCategory }},
546+
{"predicate_slug", func(r *model.CreateResponse) string { return r.PredicateSlug }},
547+
{"created_at", func(r *model.CreateResponse) string { return r.CreatedAt }},
548+
{"created_by", func(r *model.CreateResponse) string { return r.CreatedBy }},
549+
{"verified", func(r *model.CreateResponse) string {
550+
if r.Verified {
551+
return "true"
552+
}
553+
return "false"
554+
}},
555+
{"provider_id", func(r *model.CreateResponse) string { return r.ProviderId }},
556+
}
557+
558+
// printCreateEvidenceResponse writes formatted output for the given responses.
559+
func printCreateEvidenceResponse(w io.Writer, outputFormat format.OutputFormat, responses []*model.CreateResponse) error {
560+
switch outputFormat {
561+
case format.Json:
562+
return printCreateEvidenceJSON(w, responses)
563+
case format.Table:
564+
return printCreateEvidenceTable(w, responses)
565+
default:
566+
return fmt.Errorf("unsupported format %q: accepted values are json, table", outputFormat)
567+
}
568+
}
569+
570+
func printCreateEvidenceJSON(w io.Writer, responses []*model.CreateResponse) error {
571+
enc := json.NewEncoder(w)
572+
enc.SetIndent("", " ")
573+
if len(responses) == 1 {
574+
return enc.Encode(responses[0])
575+
}
576+
return enc.Encode(responses)
577+
}
578+
579+
func printCreateEvidenceTable(w io.Writer, responses []*model.CreateResponse) error {
580+
for i, r := range responses {
581+
if i > 0 {
582+
if _, err := fmt.Fprintln(w); err != nil {
583+
return err
584+
}
585+
}
586+
t := table.NewWriter()
587+
t.SetOutputMirror(w)
588+
t.SetStyle(table.StyleLight)
589+
t.Style().Options.SeparateRows = false
590+
t.AppendHeader(table.Row{"FIELD", "VALUE"})
591+
t.SetColumnConfigs([]table.ColumnConfig{
592+
{Number: 1, Align: text.AlignLeft, AlignHeader: text.AlignLeft},
593+
{Number: 2, Align: text.AlignLeft, AlignHeader: text.AlignLeft},
594+
})
595+
for _, field := range tableFieldOrder {
596+
val := field.value(r)
597+
if val != "" {
598+
t.AppendRow(table.Row{field.key, val})
599+
}
600+
}
601+
t.Render()
602+
}
603+
return nil
604+
}

0 commit comments

Comments
 (0)