Skip to content

Commit 2fc1359

Browse files
committed
resolve merge conflicts
2 parents b905bce + 751a509 commit 2fc1359

172 files changed

Lines changed: 9334 additions & 4097 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.chainloop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# This can indicate the next version and by default it will be marked as pre-release
2-
projectVersion: v1.24.0
2+
projectVersion: v1.35.0
33

44
# Experimental feature used by Chainloop labs shared workflow https://github.com/chainloop-dev/labs
55
# It maps the material names with location in disk so they get automatically attested

app/cli/cmd/organization_apitoken_create.go

Lines changed: 7 additions & 6 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.
@@ -27,8 +27,8 @@ import (
2727

2828
func newAPITokenCreateCmd() *cobra.Command {
2929
var (
30-
description, name string
31-
expiresIn time.Duration
30+
description, name, projectName string
31+
expiresIn time.Duration
3232
)
3333

3434
cmd := &cobra.Command{
@@ -40,7 +40,7 @@ func newAPITokenCreateCmd() *cobra.Command {
4040
duration = &expiresIn
4141
}
4242

43-
res, err := action.NewAPITokenCreate(actionOpts).Run(context.Background(), name, description, duration)
43+
res, err := action.NewAPITokenCreate(actionOpts).Run(context.Background(), name, description, projectName, duration)
4444
if err != nil {
4545
return fmt.Errorf("creating API token: %w", err)
4646
}
@@ -61,6 +61,7 @@ func newAPITokenCreateCmd() *cobra.Command {
6161
cmd.InheritedFlags().StringVarP(&flagOutputFormat, "output", "o", "table", "output format, valid options are table, json, token")
6262
err := cmd.MarkFlagRequired("name")
6363
cobra.CheckErr(err)
64+
cmd.Flags().StringVar(&projectName, "project", "", "project name used to scope the token, if not set the token will be created at the organization level")
6465

6566
return cmd
6667
}
@@ -77,9 +78,9 @@ func apiTokenListTableOutput(tokens []*action.APITokenItem) error {
7778

7879
t := newTableWriter()
7980

80-
t.AppendHeader(table.Row{"Name", "Description", "Created At", "Expires At", "Revoked At", "Last used at"})
81+
t.AppendHeader(table.Row{"ID", "Name", "Scope", "Description", "Created At", "Expires At", "Revoked At", "Last used at"})
8182
for _, p := range tokens {
82-
r := table.Row{p.Name, p.Description, p.CreatedAt.Format(time.RFC822)}
83+
r := table.Row{p.ID, p.Name, p.ScopedEntity.String(), p.Description, p.CreatedAt.Format(time.RFC822)}
8384
if p.ExpiresAt != nil {
8485
r = append(r, p.ExpiresAt.Format(time.RFC822))
8586
} else {

app/cli/cmd/organization_apitoken_list.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright 2023 The Chainloop Authors.
2+
// Copyright 2023-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.
@@ -18,20 +18,37 @@ package cmd
1818
import (
1919
"context"
2020
"fmt"
21+
"slices"
2122

2223
"github.com/chainloop-dev/chainloop/app/cli/internal/action"
2324
"github.com/spf13/cobra"
2425
)
2526

2627
func newAPITokenListCmd() *cobra.Command {
27-
var includeRevoked bool
28+
var (
29+
includeRevoked bool
30+
project string
31+
scope string
32+
)
33+
34+
var availableScopes = []string{
35+
"project",
36+
"global",
37+
}
2838

2939
cmd := &cobra.Command{
3040
Use: "list",
3141
Aliases: []string{"ls"},
3242
Short: "List API tokens in this organization",
43+
PreRunE: func(_ *cobra.Command, _ []string) error {
44+
if scope != "" && !slices.Contains(availableScopes, scope) {
45+
return fmt.Errorf("invalid scope %q, please chose one of: %v", scope, availableScopes)
46+
}
47+
48+
return nil
49+
},
3350
RunE: func(cmd *cobra.Command, args []string) error {
34-
res, err := action.NewAPITokenList(actionOpts).Run(context.Background(), includeRevoked)
51+
res, err := action.NewAPITokenList(actionOpts).Run(context.Background(), includeRevoked, project, scope)
3552
if err != nil {
3653
return fmt.Errorf("listing API tokens: %w", err)
3754
}
@@ -41,5 +58,7 @@ func newAPITokenListCmd() *cobra.Command {
4158
}
4259

4360
cmd.Flags().BoolVarP(&includeRevoked, "all", "a", false, "show all API tokens including revoked ones")
61+
cmd.Flags().StringVarP(&project, "project", "p", "", "filter by project name")
62+
cmd.Flags().StringVarP(&scope, "scope", "s", "", fmt.Sprintf("filter by scope, available scopes: %v", availableScopes))
4463
return cmd
4564
}

app/cli/cmd/organization_apitoken_revoke.go

Lines changed: 5 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.
@@ -24,13 +24,13 @@ import (
2424
)
2525

2626
func newAPITokenRevokeCmd() *cobra.Command {
27-
var name string
27+
var id string
2828

2929
cmd := &cobra.Command{
3030
Use: "revoke",
3131
Short: "revoke API token",
3232
RunE: func(cmd *cobra.Command, args []string) error {
33-
if err := action.NewAPITokenRevoke(actionOpts).Run(context.Background(), name); err != nil {
33+
if err := action.NewAPITokenRevoke(actionOpts).Run(context.Background(), id); err != nil {
3434
return fmt.Errorf("revoking API token: %w", err)
3535
}
3636

@@ -39,8 +39,8 @@ func newAPITokenRevokeCmd() *cobra.Command {
3939
},
4040
}
4141

42-
cmd.Flags().StringVar(&name, "name", "", "API token name")
43-
err := cmd.MarkFlagRequired("name")
42+
cmd.Flags().StringVar(&id, "id", "", "API token ID")
43+
err := cmd.MarkFlagRequired("id")
4444
cobra.CheckErr(err)
4545

4646
return cmd

app/cli/cmd/organization_member_delete.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ import (
2525

2626
// Get the membership entry associated to the current user for the given organization
2727
func loadMembershipCurrentOrg(ctx context.Context, membershipID string) (*action.MembershipItem, error) {
28-
memberships, err := action.NewMembershipList(actionOpts).ListMembers(ctx)
28+
res, err := action.NewMembershipList(actionOpts).ListMembers(ctx, 1, 1, &action.ListMembersOpts{MembershipID: &membershipID})
2929
if err != nil {
3030
return nil, fmt.Errorf("listing memberships: %w", err)
3131
}
3232

33-
for _, m := range memberships {
33+
for _, m := range res.Memberships {
3434
if m.ID == membershipID {
3535
return m, nil
3636
}

app/cli/cmd/organization_member_list.go

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,40 +19,109 @@ import (
1919
"fmt"
2020
"time"
2121

22+
"github.com/chainloop-dev/chainloop/app/cli/cmd/options"
2223
"github.com/chainloop-dev/chainloop/app/cli/internal/action"
24+
2325
"github.com/jedib0t/go-pretty/v6/table"
2426
"github.com/spf13/cobra"
2527
)
2628

2729
func newOrganizationMemberList() *cobra.Command {
30+
var (
31+
paginationOpts = &options.OffsetPaginationOpts{}
32+
name string
33+
email string
34+
role string
35+
)
36+
2837
cmd := &cobra.Command{
2938
Use: "list",
3039
Aliases: []string{"ls"},
3140
Short: "List the members of the current organization",
41+
Example: ` # Let the default pagination apply
42+
chainloop organization member list
43+
44+
# Specify the page and page size
45+
chainloop organization member list --page 2 --limit 10
46+
47+
# Filter by name
48+
chainloop organization member list --name alice
49+
50+
# Filter by email
51+
chainloop organization member list --email alice@example.com
52+
53+
# Filter by role
54+
chainloop organization member list --role admin
55+
56+
# Combine filters and pagination
57+
chainloop organization member list --role admin --page 2 --limit 5
58+
`,
59+
PreRunE: func(_ *cobra.Command, _ []string) error {
60+
if paginationOpts.Page < 1 {
61+
return fmt.Errorf("--page must be greater or equal than 1")
62+
}
63+
if paginationOpts.Limit < 1 {
64+
return fmt.Errorf("--limit must be greater or equal than 1")
65+
}
66+
67+
return nil
68+
},
3269
RunE: func(cmd *cobra.Command, args []string) error {
33-
res, err := action.NewMembershipList(actionOpts).ListMembers(cmd.Context())
70+
opts := &action.ListMembersOpts{}
71+
72+
switch {
73+
case name != "":
74+
opts.Name = &name
75+
case email != "":
76+
opts.Email = &email
77+
case role != "":
78+
opts.Role = &role
79+
}
80+
81+
res, err := action.NewMembershipList(actionOpts).ListMembers(cmd.Context(), paginationOpts.Page, paginationOpts.Limit, opts)
3482
if err != nil {
3583
return err
3684
}
3785

38-
return encodeOutput(res, orgMembershipsTableOutput)
86+
if err := encodeOutput(res, orgMembershipsTableOutput); err != nil {
87+
return err
88+
}
89+
90+
pgResponse := res.PaginationMeta
91+
92+
if pgResponse.TotalPages >= paginationOpts.Page {
93+
inPage := min(paginationOpts.Limit, len(res.Memberships))
94+
lowerBound := (paginationOpts.Page - 1) * paginationOpts.Limit
95+
logger.Info().Msg(fmt.Sprintf("Showing [%d-%d] out of %d", lowerBound+1, lowerBound+inPage, pgResponse.TotalCount))
96+
}
97+
98+
if pgResponse.TotalCount > pgResponse.Page*pgResponse.PageSize {
99+
logger.Info().Msg(fmt.Sprintf("Next page available: %d", pgResponse.Page+1))
100+
}
101+
102+
return nil
39103
},
40104
}
41105

106+
cmd.Flags().StringVar(&name, "name", "", "Filter by member name or last name")
107+
cmd.Flags().StringVar(&email, "email", "", "Filter by member email")
108+
cmd.Flags().StringVar(&role, "role", "", fmt.Sprintf("Role of the user in the organization, available %s", action.AvailableRoles[:3]))
109+
paginationOpts.AddFlags(cmd)
110+
42111
return cmd
43112
}
44113

45-
func orgMembershipsTableOutput(items []*action.MembershipItem) error {
46-
if len(items) == 0 {
47-
fmt.Println(UserWithNoOrganizationMsg)
48-
return nil
49-
}
50-
114+
func orgMembershipsTableOutput(res *action.ListMembershipResult) error {
51115
t := newTableWriter()
52116
t.AppendHeader(table.Row{"ID", "Email", "Role", "Joined At"})
53117

54-
for _, i := range items {
55-
t.AppendRow(table.Row{i.ID, i.User.PrintUserProfileWithEmail(), i.Role, i.CreatedAt.Format(time.RFC822)})
118+
for _, m := range res.Memberships {
119+
t.AppendRow(table.Row{
120+
m.ID,
121+
m.User.PrintUserProfileWithEmail(),
122+
m.Role,
123+
m.CreatedAt.Format(time.RFC822),
124+
})
56125
t.AppendSeparator()
57126
}
58127

app/cli/cmd/organization_member_update.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func newOrganizationMemberUpdateCmd() *cobra.Command {
4646
return err
4747
}
4848

49-
return encodeOutput([]*action.MembershipItem{res}, orgMembershipsTableOutput)
49+
return encodeOutput(&action.ListMembershipResult{Memberships: []*action.MembershipItem{res}}, orgMembershipsTableOutput)
5050
},
5151
}
5252

app/cli/cmd/output.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ type tabulatedData interface {
5353
[]*action.OrgInvitationItem |
5454
*action.APITokenItem |
5555
[]*action.APITokenItem |
56-
*action.AttestationStatusMaterial
56+
*action.AttestationStatusMaterial |
57+
*action.ListMembershipResult
5758
}
5859

5960
var ErrOutputFormatNotImplemented = errors.New("format not implemented")

app/cli/cmd/plugins.go

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"fmt"
2121
"os"
2222
"strconv"
23+
"strings"
2324

2425
"github.com/chainloop-dev/chainloop/app/cli/internal/action"
2526
"github.com/chainloop-dev/chainloop/app/cli/pkg/plugins"
@@ -340,28 +341,19 @@ func pluginListTableOutput(plugins map[string]*plugins.LoadedPlugin) {
340341

341342
func pluginInfoTableOutput(plugin *plugins.LoadedPlugin) {
342343
t := newTableWriter()
343-
344-
t.AppendHeader(table.Row{"Name", "Version", "Description", "Commands"})
345-
t.AppendRow(table.Row{plugin.Metadata.Name, plugin.Metadata.Version, plugin.Metadata.Description, fmt.Sprintf("%d command(s)", len(plugin.Metadata.Commands))})
346-
344+
t.SetTitle(fmt.Sprintf("Plugin: %s", plugin.Metadata.Name))
345+
t.AppendSeparator()
346+
t.AppendRow(table.Row{"Version", plugin.Metadata.Version})
347+
t.AppendSeparator()
348+
t.AppendRow(table.Row{"Description", plugin.Metadata.Description})
349+
t.AppendSeparator()
350+
t.AppendRow(table.Row{"Commands", fmt.Sprintf("%d command(s)", len(plugin.Metadata.Commands))})
351+
t.AppendSeparator()
347352
t.Render()
348353

349-
pluginInfoCommandsTableOutput(plugin)
350354
pluginInfoFlagsTableOutput(plugin)
351355
}
352356

353-
func pluginInfoCommandsTableOutput(plugin *plugins.LoadedPlugin) {
354-
t := newTableWriter()
355-
356-
t.AppendHeader(table.Row{"Plugin", "Command", "Description", "Usage"})
357-
for _, cmd := range plugin.Metadata.Commands {
358-
t.AppendRow(table.Row{plugin.Metadata.Name, cmd.Name, cmd.Description, cmd.Usage})
359-
t.AppendSeparator()
360-
}
361-
362-
t.Render()
363-
}
364-
365357
func pluginInfoFlagsTableOutput(plugin *plugins.LoadedPlugin) {
366358
if len(plugin.Metadata.Commands) == 0 {
367359
return
@@ -378,15 +370,39 @@ func pluginInfoFlagsTableOutput(plugin *plugins.LoadedPlugin) {
378370
return
379371
}
380372

381-
t := newTableWriter()
382-
383-
t.AppendHeader(table.Row{"Plugin", "Command", "Flag", "Description", "Type", "Default", "Required"})
384373
for _, cmd := range plugin.Metadata.Commands {
374+
t := newTableWriter()
375+
t.SetTitle(fmt.Sprintf("Command: %s", cmd.Name))
376+
t.AppendSeparator()
377+
378+
flagDetails := "Flags:\n"
379+
380+
// Find the longest flag name to align descriptions properly
381+
maxFlagLen := 0
385382
for _, flag := range cmd.Flags {
386-
t.AppendRow(table.Row{plugin.Metadata.Name, cmd.Name, flag.Name, flag.Description, flag.Type, flag.Default, flag.Required})
387-
t.AppendSeparator()
383+
if len(flag.Name) > maxFlagLen {
384+
maxFlagLen = len(flag.Name)
385+
}
388386
}
389-
}
390387

391-
t.Render()
388+
maxFlagLen += 2 // For the "--" prefix
389+
for _, flag := range cmd.Flags {
390+
shorthand := " "
391+
if flag.Shorthand != "" {
392+
shorthand = fmt.Sprintf("-%s,", flag.Shorthand)
393+
}
394+
flagName := fmt.Sprintf(" %s--%s", shorthand, flag.Name)
395+
padding := strings.Repeat(" ", maxFlagLen-len(flag.Name)+2)
396+
397+
defaultValue := ""
398+
if flag.Default != nil {
399+
defaultValue = fmt.Sprintf(" (default: %v)", flag.Default)
400+
}
401+
402+
flagDetails += fmt.Sprintf("%s%s%s%s\n", flagName, padding, flag.Description, defaultValue)
403+
}
404+
405+
t.AppendRow(table.Row{flagDetails})
406+
t.Render()
407+
}
392408
}

0 commit comments

Comments
 (0)