|
7 | 7 | "connectrpc.com/connect" |
8 | 8 | civ1 "github.com/depot/cli/pkg/proto/depot/ci/v1" |
9 | 9 | "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" |
10 | 12 | ) |
11 | 13 |
|
12 | 14 | var baseURLFunc = getBaseURL |
@@ -101,115 +103,163 @@ func CIListRuns(ctx context.Context, token, orgID string, statuses []civ1.CIRunS |
101 | 103 | return allRuns, nil |
102 | 104 | } |
103 | 105 |
|
104 | | -func newCISecretServiceClient() civ1connect.SecretServiceClient { |
| 106 | +func newCISecretServiceV2Client() civ2connect.SecretServiceClient { |
105 | 107 | baseURL := baseURLFunc() |
106 | | - return civ1connect.NewSecretServiceClient(getHTTPClient(baseURL), baseURL, WithUserAgent()) |
| 108 | + return civ2connect.NewSecretServiceClient(getHTTPClient(baseURL), baseURL, WithUserAgent()) |
107 | 109 | } |
108 | 110 |
|
109 | | -// CIAddSecret adds a single CI secret to an organization |
| 111 | +// CIAddSecret adds a single CI secret to an organization (org-wide). |
110 | 112 | 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, "", "") |
112 | 114 | } |
113 | 115 |
|
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 |
120 | 126 | } |
| 127 | + req := &civ2.AddOrgSecretRequest{Name: name, Value: value} |
121 | 128 | if description != "" { |
122 | 129 | req.Description = &description |
123 | 130 | } |
124 | | - _, err := client.AddSecret(ctx, WithAuthenticationAndOrg(connect.NewRequest(req), token, orgID)) |
| 131 | + _, err := client.AddOrgSecret(ctx, WithAuthenticationAndOrg(connect.NewRequest(req), token, orgID)) |
125 | 132 | return err |
126 | 133 | } |
127 | 134 |
|
128 | | -// CISecret contains metadata about a CI secret |
| 135 | +// CISecret contains metadata about a CI secret. |
129 | 136 | type CISecret struct { |
130 | 137 | Name string `json:"name"` |
131 | 138 | Description string `json:"description,omitempty"` |
132 | 139 | CreatedAt string `json:"createdAt,omitempty"` |
| 140 | + Scope string `json:"scope"` |
133 | 141 | } |
134 | 142 |
|
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)) |
139 | 160 | if err != nil { |
140 | 161 | return nil, err |
141 | 162 | } |
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 |
149 | 173 | } |
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)) |
152 | 176 | } |
153 | | - secrets = append(secrets, cs) |
154 | 177 | } |
| 178 | + |
155 | 179 | return secrets, nil |
156 | 180 | } |
157 | 181 |
|
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)) |
162 | 190 | return err |
163 | 191 | } |
164 | 192 |
|
165 | | -func newCIVariableServiceClient() civ1connect.VariableServiceClient { |
| 193 | +func newCIVariableServiceV2Client() civ2connect.VariableServiceClient { |
166 | 194 | baseURL := baseURLFunc() |
167 | | - return civ1connect.NewVariableServiceClient(getHTTPClient(baseURL), baseURL, WithUserAgent()) |
| 195 | + return civ2connect.NewVariableServiceClient(getHTTPClient(baseURL), baseURL, WithUserAgent()) |
168 | 196 | } |
169 | 197 |
|
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)) |
177 | 206 | return err |
178 | 207 | } |
179 | 208 |
|
180 | | -// CIVariable contains metadata about a CI variable |
| 209 | +// CIVariable contains metadata about a CI variable. |
181 | 210 | type CIVariable struct { |
182 | 211 | Name string `json:"name"` |
183 | 212 | Description string `json:"description,omitempty"` |
184 | 213 | CreatedAt string `json:"createdAt,omitempty"` |
| 214 | + Scope string `json:"scope"` |
185 | 215 | } |
186 | 216 |
|
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)) |
191 | 234 | if err != nil { |
192 | 235 | return nil, err |
193 | 236 | } |
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 |
201 | 247 | } |
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)) |
204 | 250 | } |
205 | | - variables = append(variables, cv) |
206 | 251 | } |
| 252 | + |
207 | 253 | return variables, nil |
208 | 254 | } |
209 | 255 |
|
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)) |
214 | 264 | return err |
215 | 265 | } |
0 commit comments