Skip to content

Commit 94f0f23

Browse files
authored
Merge pull request #446 from depot/dep-3330-cli-v2
Add --repo flag for repo-scoped CI secrets and variables
2 parents 5403313 + 7cd4c48 commit 94f0f23

File tree

10 files changed

+4801
-107
lines changed

10 files changed

+4801
-107
lines changed

pkg/api/ci.go

Lines changed: 108 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"connectrpc.com/connect"
88
civ1 "github.com/depot/cli/pkg/proto/depot/ci/v1"
99
"github.com/depot/cli/pkg/proto/depot/ci/v1/civ1connect"
10+
civ2 "github.com/depot/cli/pkg/proto/depot/ci/v2"
11+
"github.com/depot/cli/pkg/proto/depot/ci/v2/civ2connect"
1012
)
1113

1214
var baseURLFunc = getBaseURL
@@ -101,115 +103,163 @@ func CIListRuns(ctx context.Context, token, orgID string, statuses []civ1.CIRunS
101103
return allRuns, nil
102104
}
103105

104-
func newCISecretServiceClient() civ1connect.SecretServiceClient {
106+
func newCISecretServiceV2Client() civ2connect.SecretServiceClient {
105107
baseURL := baseURLFunc()
106-
return civ1connect.NewSecretServiceClient(getHTTPClient(baseURL), baseURL, WithUserAgent())
108+
return civ2connect.NewSecretServiceClient(getHTTPClient(baseURL), baseURL, WithUserAgent())
107109
}
108110

109-
// CIAddSecret adds a single CI secret to an organization
111+
// CIAddSecret adds a single CI secret to an organization (org-wide).
110112
func CIAddSecret(ctx context.Context, token, orgID, name, value string) error {
111-
return CIAddSecretWithDescription(ctx, token, orgID, name, value, "")
113+
return CIAddSecretWithDescription(ctx, token, orgID, name, value, "", "")
112114
}
113115

114-
// CIAddSecretWithDescription adds a single CI secret to an organization, with an optional description.
115-
func CIAddSecretWithDescription(ctx context.Context, token, orgID, name, value, description string) error {
116-
client := newCISecretServiceClient()
117-
req := &civ1.AddSecretRequest{
118-
Name: name,
119-
Value: value,
116+
// CIAddSecretWithDescription adds a CI secret, optionally scoped to a repo.
117+
func CIAddSecretWithDescription(ctx context.Context, token, orgID, name, value, description, repo string) error {
118+
client := newCISecretServiceV2Client()
119+
if repo != "" {
120+
req := &civ2.AddRepoSecretRequest{Repo: repo, Name: name, Value: value}
121+
if description != "" {
122+
req.Description = &description
123+
}
124+
_, err := client.AddRepoSecret(ctx, WithAuthenticationAndOrg(connect.NewRequest(req), token, orgID))
125+
return err
120126
}
127+
req := &civ2.AddOrgSecretRequest{Name: name, Value: value}
121128
if description != "" {
122129
req.Description = &description
123130
}
124-
_, err := client.AddSecret(ctx, WithAuthenticationAndOrg(connect.NewRequest(req), token, orgID))
131+
_, err := client.AddOrgSecret(ctx, WithAuthenticationAndOrg(connect.NewRequest(req), token, orgID))
125132
return err
126133
}
127134

128-
// CISecret contains metadata about a CI secret
135+
// CISecret contains metadata about a CI secret.
129136
type CISecret struct {
130137
Name string `json:"name"`
131138
Description string `json:"description,omitempty"`
132139
CreatedAt string `json:"createdAt,omitempty"`
140+
Scope string `json:"scope"`
133141
}
134142

135-
// CIListSecrets lists all CI secrets for an organization
136-
func CIListSecrets(ctx context.Context, token, orgID string) ([]CISecret, error) {
137-
client := newCISecretServiceClient()
138-
resp, err := client.ListSecrets(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ1.ListSecretsRequest{}), token, orgID))
143+
func secretFromProto(s *civ2.Secret, scope string) CISecret {
144+
cs := CISecret{Name: s.Name, Scope: scope}
145+
if s.Description != nil {
146+
cs.Description = *s.Description
147+
}
148+
if s.LastModified != nil {
149+
cs.CreatedAt = s.LastModified.AsTime().Format(time.RFC3339)
150+
}
151+
return cs
152+
}
153+
154+
// CIListSecrets lists CI secrets. When repo is non-empty it returns both
155+
// org-wide and repo-specific secrets so the caller can see effective state.
156+
func CIListSecrets(ctx context.Context, token, orgID, repo string) ([]CISecret, error) {
157+
client := newCISecretServiceV2Client()
158+
159+
orgResp, err := client.ListOrgSecrets(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ2.ListOrgSecretsRequest{}), token, orgID))
139160
if err != nil {
140161
return nil, err
141162
}
142-
secrets := make([]CISecret, 0, len(resp.Msg.Secrets))
143-
for _, s := range resp.Msg.Secrets {
144-
cs := CISecret{
145-
Name: s.Name,
146-
}
147-
if s.Description != nil {
148-
cs.Description = *s.Description
163+
164+
secrets := make([]CISecret, 0, len(orgResp.Msg.Secrets))
165+
for _, s := range orgResp.Msg.Secrets {
166+
secrets = append(secrets, secretFromProto(s, "org"))
167+
}
168+
169+
if repo != "" {
170+
repoResp, err := client.ListRepoSecrets(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ2.ListRepoSecretsRequest{Repo: repo}), token, orgID))
171+
if err != nil {
172+
return nil, err
149173
}
150-
if s.LastModified != nil {
151-
cs.CreatedAt = s.LastModified.AsTime().Format(time.RFC3339)
174+
for _, s := range repoResp.Msg.Secrets {
175+
secrets = append(secrets, secretFromProto(s, repo))
152176
}
153-
secrets = append(secrets, cs)
154177
}
178+
155179
return secrets, nil
156180
}
157181

158-
// CIDeleteSecret deletes a CI secret from an organization
159-
func CIDeleteSecret(ctx context.Context, token, orgID, name string) error {
160-
client := newCISecretServiceClient()
161-
_, err := client.RemoveSecret(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ1.RemoveSecretRequest{Name: name}), token, orgID))
182+
// CIDeleteSecret deletes a CI secret, optionally scoped to a repo.
183+
func CIDeleteSecret(ctx context.Context, token, orgID, name, repo string) error {
184+
client := newCISecretServiceV2Client()
185+
if repo != "" {
186+
_, err := client.RemoveRepoSecret(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ2.RemoveRepoSecretRequest{Repo: repo, Name: name}), token, orgID))
187+
return err
188+
}
189+
_, err := client.RemoveOrgSecret(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ2.RemoveOrgSecretRequest{Name: name}), token, orgID))
162190
return err
163191
}
164192

165-
func newCIVariableServiceClient() civ1connect.VariableServiceClient {
193+
func newCIVariableServiceV2Client() civ2connect.VariableServiceClient {
166194
baseURL := baseURLFunc()
167-
return civ1connect.NewVariableServiceClient(getHTTPClient(baseURL), baseURL, WithUserAgent())
195+
return civ2connect.NewVariableServiceClient(getHTTPClient(baseURL), baseURL, WithUserAgent())
168196
}
169197

170-
// CIAddVariable adds a single CI variable to an organization
171-
func CIAddVariable(ctx context.Context, token, orgID, name, value string) error {
172-
client := newCIVariableServiceClient()
173-
_, err := client.AddVariable(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ1.AddVariableRequest{
174-
Name: name,
175-
Value: value,
176-
}), token, orgID))
198+
// CIAddVariable adds a CI variable, optionally scoped to a repo.
199+
func CIAddVariable(ctx context.Context, token, orgID, name, value, repo string) error {
200+
client := newCIVariableServiceV2Client()
201+
if repo != "" {
202+
_, err := client.AddRepoVariable(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ2.AddRepoVariableRequest{Repo: repo, Name: name, Value: value}), token, orgID))
203+
return err
204+
}
205+
_, err := client.AddOrgVariable(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ2.AddOrgVariableRequest{Name: name, Value: value}), token, orgID))
177206
return err
178207
}
179208

180-
// CIVariable contains metadata about a CI variable
209+
// CIVariable contains metadata about a CI variable.
181210
type CIVariable struct {
182211
Name string `json:"name"`
183212
Description string `json:"description,omitempty"`
184213
CreatedAt string `json:"createdAt,omitempty"`
214+
Scope string `json:"scope"`
185215
}
186216

187-
// CIListVariables lists all CI variables for an organization
188-
func CIListVariables(ctx context.Context, token, orgID string) ([]CIVariable, error) {
189-
client := newCIVariableServiceClient()
190-
resp, err := client.ListVariables(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ1.ListVariablesRequest{}), token, orgID))
217+
func variableFromProto(v *civ2.Variable, scope string) CIVariable {
218+
cv := CIVariable{Name: v.Name, Scope: scope}
219+
if v.Description != nil {
220+
cv.Description = *v.Description
221+
}
222+
if v.LastModified != nil {
223+
cv.CreatedAt = v.LastModified.AsTime().Format(time.RFC3339)
224+
}
225+
return cv
226+
}
227+
228+
// CIListVariables lists CI variables. When repo is non-empty it returns both
229+
// org-wide and repo-specific variables.
230+
func CIListVariables(ctx context.Context, token, orgID, repo string) ([]CIVariable, error) {
231+
client := newCIVariableServiceV2Client()
232+
233+
orgResp, err := client.ListOrgVariables(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ2.ListOrgVariablesRequest{}), token, orgID))
191234
if err != nil {
192235
return nil, err
193236
}
194-
variables := make([]CIVariable, 0, len(resp.Msg.Variables))
195-
for _, v := range resp.Msg.Variables {
196-
cv := CIVariable{
197-
Name: v.Name,
198-
}
199-
if v.Description != nil {
200-
cv.Description = *v.Description
237+
238+
variables := make([]CIVariable, 0, len(orgResp.Msg.Variables))
239+
for _, v := range orgResp.Msg.Variables {
240+
variables = append(variables, variableFromProto(v, "org"))
241+
}
242+
243+
if repo != "" {
244+
repoResp, err := client.ListRepoVariables(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ2.ListRepoVariablesRequest{Repo: repo}), token, orgID))
245+
if err != nil {
246+
return nil, err
201247
}
202-
if v.LastModified != nil {
203-
cv.CreatedAt = v.LastModified.AsTime().Format(time.RFC3339)
248+
for _, v := range repoResp.Msg.Variables {
249+
variables = append(variables, variableFromProto(v, repo))
204250
}
205-
variables = append(variables, cv)
206251
}
252+
207253
return variables, nil
208254
}
209255

210-
// CIDeleteVariable deletes a CI variable from an organization
211-
func CIDeleteVariable(ctx context.Context, token, orgID, name string) error {
212-
client := newCIVariableServiceClient()
213-
_, err := client.RemoveVariable(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ1.RemoveVariableRequest{Name: name}), token, orgID))
256+
// CIDeleteVariable deletes a CI variable, optionally scoped to a repo.
257+
func CIDeleteVariable(ctx context.Context, token, orgID, name, repo string) error {
258+
client := newCIVariableServiceV2Client()
259+
if repo != "" {
260+
_, err := client.RemoveRepoVariable(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ2.RemoveRepoVariableRequest{Repo: repo, Name: name}), token, orgID))
261+
return err
262+
}
263+
_, err := client.RemoveOrgVariable(ctx, WithAuthenticationAndOrg(connect.NewRequest(&civ2.RemoveOrgVariableRequest{Name: name}), token, orgID))
214264
return err
215265
}

pkg/cmd/ci/migrate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ func runMigrate(ctx context.Context, opts migrateOptions) error {
320320
return err
321321
}
322322

323-
if err := api.CIAddVariable(ctx, token, orgID, name, value); err != nil {
323+
if err := api.CIAddVariable(ctx, token, orgID, name, value, ""); err != nil {
324324
return fmt.Errorf("failed to configure variable %s: %w", name, err)
325325
}
326326
configuredVariables = append(configuredVariables, name)

0 commit comments

Comments
 (0)