-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement support for action secrets (#1402)
Relates to: #1399.
- Loading branch information
1 parent
6923131
commit 7d28d53
Showing
5 changed files
with
285 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Copyright 2020 The go-github AUTHORS. All rights reserved. | ||
// | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package github | ||
|
||
// ActionsService handles communication with the actions related | ||
// methods of the GitHub API. | ||
// | ||
// GitHub API docs: https://developer.github.com/v3/actions/ | ||
type ActionsService service |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// Copyright 2020 The go-github AUTHORS. All rights reserved. | ||
// | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package github | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
) | ||
|
||
// PublicKey represents the public key that should be used to encrypt secrets. | ||
type PublicKey struct { | ||
KeyID *string `json:"key_id"` | ||
Key *string `json:"key"` | ||
} | ||
|
||
// GetPublicKey gets a public key that should be used for secret encryption. | ||
// | ||
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#get-your-public-key | ||
func (s *ActionsService) GetPublicKey(ctx context.Context, owner, repo string) (*PublicKey, *Response, error) { | ||
u := fmt.Sprintf("repos/%v/%v/actions/secrets/public-key", owner, repo) | ||
req, err := s.client.NewRequest("GET", u, nil) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
pubKey := new(PublicKey) | ||
resp, err := s.client.Do(ctx, req, pubKey) | ||
if err != nil { | ||
return nil, resp, err | ||
} | ||
|
||
return pubKey, resp, nil | ||
} | ||
|
||
// Secret represents a repository action secret. | ||
type Secret struct { | ||
Name string `json:"name"` | ||
CreatedAt Timestamp `json:"created_at"` | ||
UpdatedAt Timestamp `json:"updated_at"` | ||
} | ||
|
||
// Secrets represents one item from the ListSecrets response. | ||
type Secrets struct { | ||
TotalCount int `json:"total_count"` | ||
Secrets []*Secret `json:"secrets"` | ||
} | ||
|
||
// ListSecrets lists all secrets available in a repository | ||
// without revealing their encrypted values. | ||
// | ||
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#list-secrets-for-a-repository | ||
func (s *ActionsService) ListSecrets(ctx context.Context, owner, repo string, opt *ListOptions) (*Secrets, *Response, error) { | ||
u := fmt.Sprintf("repos/%s/%s/actions/secrets", owner, repo) | ||
u, err := addOptions(u, opt) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
req, err := s.client.NewRequest("GET", u, nil) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
secrets := new(Secrets) | ||
resp, err := s.client.Do(ctx, req, &secrets) | ||
if err != nil { | ||
return nil, resp, err | ||
} | ||
|
||
return secrets, resp, nil | ||
} | ||
|
||
// GetSecret gets a single secret without revealing its encrypted value. | ||
// | ||
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#get-a-secret | ||
func (s *ActionsService) GetSecret(ctx context.Context, owner, repo, name string) (*Secret, *Response, error) { | ||
u := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, name) | ||
req, err := s.client.NewRequest("GET", u, nil) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
secret := new(Secret) | ||
resp, err := s.client.Do(ctx, req, secret) | ||
if err != nil { | ||
return nil, resp, err | ||
} | ||
|
||
return secret, resp, nil | ||
} | ||
|
||
// EncryptedSecret represents a secret that is encrypted using a public key. | ||
// | ||
// The value of EncryptedValue must be your secret, encrypted with | ||
// LibSodium (see documentation here: https://libsodium.gitbook.io/doc/bindings_for_other_languages) | ||
// using the public key retrieved using the GetPublicKey method. | ||
type EncryptedSecret struct { | ||
Name string `json:"-"` | ||
KeyID string `json:"key_id"` | ||
EncryptedValue string `json:"encrypted_value"` | ||
} | ||
|
||
// CreateOrUpdateSecret creates or updates a secret with an encrypted value. | ||
// | ||
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#create-or-update-a-secret-for-a-repository | ||
func (s *ActionsService) CreateOrUpdateSecret(ctx context.Context, owner, repo string, eSecret *EncryptedSecret) (*Response, error) { | ||
u := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, eSecret.Name) | ||
|
||
req, err := s.client.NewRequest("PUT", u, eSecret) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return s.client.Do(ctx, req, nil) | ||
} | ||
|
||
// DeleteSecret deletes a secret in a repository using the secret name. | ||
// | ||
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#delete-a-secret-from-a-repository | ||
func (s *ActionsService) DeleteSecret(ctx context.Context, owner, repo, name string) (*Response, error) { | ||
u := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, name) | ||
|
||
req, err := s.client.NewRequest("DELETE", u, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return s.client.Do(ctx, req, nil) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Copyright 2020 The go-github AUTHORS. All rights reserved. | ||
// | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package github | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"reflect" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestActionsService_GetPublicKey(t *testing.T) { | ||
client, mux, _, teardown := setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/repos/o/r/actions/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { | ||
testMethod(t, r, "GET") | ||
fmt.Fprint(w, `{"key_id":"1234","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) | ||
}) | ||
|
||
key, _, err := client.Actions.GetPublicKey(context.Background(), "o", "r") | ||
if err != nil { | ||
t.Errorf("Actions.GetPublicKey returned error: %v", err) | ||
} | ||
|
||
want := &PublicKey{KeyID: String("1234"), Key: String("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} | ||
if !reflect.DeepEqual(key, want) { | ||
t.Errorf("Actions.GetPublicKey returned %+v, want %+v", key, want) | ||
} | ||
} | ||
|
||
func TestActionsService_ListSecrets(t *testing.T) { | ||
client, mux, _, teardown := setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/repos/o/r/actions/secrets", func(w http.ResponseWriter, r *http.Request) { | ||
testMethod(t, r, "GET") | ||
testFormValues(t, r, values{"per_page": "2", "page": "2"}) | ||
fmt.Fprint(w, `{"total_count":4,"secrets":[{"name":"A","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"},{"name":"B","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"}]}`) | ||
}) | ||
|
||
opt := &ListOptions{Page: 2, PerPage: 2} | ||
secrets, _, err := client.Actions.ListSecrets(context.Background(), "o", "r", opt) | ||
if err != nil { | ||
t.Errorf("Actions.ListSecrets returned error: %v", err) | ||
} | ||
|
||
want := &Secrets{ | ||
TotalCount: 4, | ||
Secrets: []*Secret{ | ||
{Name: "A", CreatedAt: Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, UpdatedAt: Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}}, | ||
{Name: "B", CreatedAt: Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, UpdatedAt: Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}}, | ||
}, | ||
} | ||
if !reflect.DeepEqual(secrets, want) { | ||
t.Errorf("Actions.ListSecrets returned %+v, want %+v", secrets, want) | ||
} | ||
} | ||
|
||
func TestActionsService_GetSecret(t *testing.T) { | ||
client, mux, _, teardown := setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/repos/o/r/actions/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { | ||
testMethod(t, r, "GET") | ||
fmt.Fprint(w, `{"name":"NAME","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"}`) | ||
}) | ||
|
||
secret, _, err := client.Actions.GetSecret(context.Background(), "o", "r", "NAME") | ||
if err != nil { | ||
t.Errorf("Actions.GetSecret returned error: %v", err) | ||
} | ||
|
||
want := &Secret{ | ||
Name: "NAME", | ||
CreatedAt: Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, | ||
UpdatedAt: Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}, | ||
} | ||
if !reflect.DeepEqual(secret, want) { | ||
t.Errorf("Actions.GetSecret returned %+v, want %+v", secret, want) | ||
} | ||
} | ||
|
||
func TestActionsService_CreateOrUpdateSecret(t *testing.T) { | ||
client, mux, _, teardown := setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/repos/o/r/actions/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { | ||
testMethod(t, r, "PUT") | ||
testHeader(t, r, "Content-Type", "application/json") | ||
testBody(t, r, `{"key_id":"1234","encrypted_value":"QIv="}`+"\n") | ||
w.WriteHeader(http.StatusCreated) | ||
}) | ||
|
||
input := &EncryptedSecret{ | ||
Name: "NAME", | ||
EncryptedValue: "QIv=", | ||
KeyID: "1234", | ||
} | ||
_, err := client.Actions.CreateOrUpdateSecret(context.Background(), "o", "r", input) | ||
if err != nil { | ||
t.Errorf("Actions.CreateOrUpdateSecret returned error: %v", err) | ||
} | ||
} | ||
|
||
func TestActionsService_DeleteSecret(t *testing.T) { | ||
client, mux, _, teardown := setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/repos/o/r/actions/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { | ||
testMethod(t, r, "DELETE") | ||
}) | ||
|
||
_, err := client.Actions.DeleteSecret(context.Background(), "o", "r", "NAME") | ||
if err != nil { | ||
t.Errorf("Actions.DeleteSecret returned error: %v", err) | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters