Provisioning: Use inline secrets for gitsync (#109908)

Co-authored-by: Clarity-89 <homes89@ukr.net>
Co-authored-by: Roberto Jimenez Sanchez <roberto.jimenez@grafana.com>
This commit is contained in:
Ryan McKinley
2025-08-22 18:38:28 +02:00
committed by GitHub
co-authored by Clarity-89 Roberto Jimenez Sanchez
parent 04f392d37b
commit ce65391067
69 changed files with 694 additions and 4255 deletions
@@ -6,6 +6,8 @@ import (
"github.com/google/go-github/v70/github"
"golang.org/x/oauth2"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
)
// Factory creates new GitHub clients.
@@ -20,16 +22,15 @@ func ProvideFactory() *Factory {
return &Factory{}
}
func (r *Factory) New(ctx context.Context, ghToken string) Client {
func (r *Factory) New(ctx context.Context, ghToken common.RawSecureValue) Client {
if r.Client != nil {
return NewClient(github.NewClient(r.Client))
}
tokenSrc := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: ghToken},
)
if len(ghToken) > 0 {
if !ghToken.IsZero() {
tokenSrc := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: string(ghToken)},
)
tokenClient := oauth2.NewClient(ctx, tokenSrc)
return NewClient(github.NewClient(tokenClient))
}
@@ -1,4 +1,4 @@
// Code generated by mockery v2.52.4. DO NOT EDIT.
// Code generated by mockery v2.53.4. DO NOT EDIT.
package github
@@ -503,168 +503,6 @@ func (_c *MockGithubRepository_Move_Call) RunAndReturn(run func(context.Context,
return _c
}
// OnCreate provides a mock function with given fields: ctx
func (_m *MockGithubRepository) OnCreate(ctx context.Context) ([]map[string]interface{}, error) {
ret := _m.Called(ctx)
if len(ret) == 0 {
panic("no return value specified for OnCreate")
}
var r0 []map[string]interface{}
var r1 error
if rf, ok := ret.Get(0).(func(context.Context) ([]map[string]interface{}, error)); ok {
return rf(ctx)
}
if rf, ok := ret.Get(0).(func(context.Context) []map[string]interface{}); ok {
r0 = rf(ctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]map[string]interface{})
}
}
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockGithubRepository_OnCreate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnCreate'
type MockGithubRepository_OnCreate_Call struct {
*mock.Call
}
// OnCreate is a helper method to define mock.On call
// - ctx context.Context
func (_e *MockGithubRepository_Expecter) OnCreate(ctx interface{}) *MockGithubRepository_OnCreate_Call {
return &MockGithubRepository_OnCreate_Call{Call: _e.mock.On("OnCreate", ctx)}
}
func (_c *MockGithubRepository_OnCreate_Call) Run(run func(ctx context.Context)) *MockGithubRepository_OnCreate_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context))
})
return _c
}
func (_c *MockGithubRepository_OnCreate_Call) Return(_a0 []map[string]interface{}, _a1 error) *MockGithubRepository_OnCreate_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockGithubRepository_OnCreate_Call) RunAndReturn(run func(context.Context) ([]map[string]interface{}, error)) *MockGithubRepository_OnCreate_Call {
_c.Call.Return(run)
return _c
}
// OnDelete provides a mock function with given fields: ctx
func (_m *MockGithubRepository) OnDelete(ctx context.Context) error {
ret := _m.Called(ctx)
if len(ret) == 0 {
panic("no return value specified for OnDelete")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
r0 = rf(ctx)
} else {
r0 = ret.Error(0)
}
return r0
}
// MockGithubRepository_OnDelete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnDelete'
type MockGithubRepository_OnDelete_Call struct {
*mock.Call
}
// OnDelete is a helper method to define mock.On call
// - ctx context.Context
func (_e *MockGithubRepository_Expecter) OnDelete(ctx interface{}) *MockGithubRepository_OnDelete_Call {
return &MockGithubRepository_OnDelete_Call{Call: _e.mock.On("OnDelete", ctx)}
}
func (_c *MockGithubRepository_OnDelete_Call) Run(run func(ctx context.Context)) *MockGithubRepository_OnDelete_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context))
})
return _c
}
func (_c *MockGithubRepository_OnDelete_Call) Return(_a0 error) *MockGithubRepository_OnDelete_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *MockGithubRepository_OnDelete_Call) RunAndReturn(run func(context.Context) error) *MockGithubRepository_OnDelete_Call {
_c.Call.Return(run)
return _c
}
// OnUpdate provides a mock function with given fields: ctx
func (_m *MockGithubRepository) OnUpdate(ctx context.Context) ([]map[string]interface{}, error) {
ret := _m.Called(ctx)
if len(ret) == 0 {
panic("no return value specified for OnUpdate")
}
var r0 []map[string]interface{}
var r1 error
if rf, ok := ret.Get(0).(func(context.Context) ([]map[string]interface{}, error)); ok {
return rf(ctx)
}
if rf, ok := ret.Get(0).(func(context.Context) []map[string]interface{}); ok {
r0 = rf(ctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]map[string]interface{})
}
}
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockGithubRepository_OnUpdate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnUpdate'
type MockGithubRepository_OnUpdate_Call struct {
*mock.Call
}
// OnUpdate is a helper method to define mock.On call
// - ctx context.Context
func (_e *MockGithubRepository_Expecter) OnUpdate(ctx interface{}) *MockGithubRepository_OnUpdate_Call {
return &MockGithubRepository_OnUpdate_Call{Call: _e.mock.On("OnUpdate", ctx)}
}
func (_c *MockGithubRepository_OnUpdate_Call) Run(run func(ctx context.Context)) *MockGithubRepository_OnUpdate_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context))
})
return _c
}
func (_c *MockGithubRepository_OnUpdate_Call) Return(_a0 []map[string]interface{}, _a1 error) *MockGithubRepository_OnUpdate_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockGithubRepository_OnUpdate_Call) RunAndReturn(run func(context.Context) ([]map[string]interface{}, error)) *MockGithubRepository_OnUpdate_Call {
_c.Call.Return(run)
return _c
}
// Owner provides a mock function with no fields
func (_m *MockGithubRepository) Owner() string {
ret := _m.Called()
@@ -1,4 +1,4 @@
// Code generated by mockery v2.52.4. DO NOT EDIT.
// Code generated by mockery v2.53.4. DO NOT EDIT.
package github
@@ -1,4 +1,4 @@
// Code generated by mockery v2.52.4. DO NOT EDIT.
// Code generated by mockery v2.53.4. DO NOT EDIT.
package github
@@ -8,10 +8,9 @@ import (
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/controller"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets"
)
func Mutator(secrets secrets.RepositorySecrets) controller.Mutator {
func Mutator() controller.Mutator {
return func(ctx context.Context, obj runtime.Object) error {
repo, ok := obj.(*provisioning.Repository)
if !ok {
@@ -31,16 +30,6 @@ func Mutator(secrets secrets.RepositorySecrets) controller.Mutator {
repo.Spec.GitHub.URL = url
}
if repo.Spec.GitHub.Token != "" {
secretName := repo.Name + githubTokenSecretSuffix
nameOrValue, err := secrets.Encrypt(ctx, repo, secretName, repo.Spec.GitHub.Token)
if err != nil {
return err
}
repo.Spec.GitHub.EncryptedToken = nameOrValue
repo.Spec.GitHub.Token = ""
}
return nil
}
}
@@ -2,25 +2,21 @@ package github
import (
"context"
"errors"
"testing"
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
)
func TestMutator(t *testing.T) {
tests := []struct {
name string
obj runtime.Object
token string
setupMocks func(*secrets.MockRepositorySecrets)
expectedToken string
expectedEncryptedToken string
expectedError string
name string
obj runtime.Object
token string
expectedError string
}{
{
name: "trims trailing .git and slash from GitHub URL",
@@ -35,9 +31,6 @@ func TestMutator(t *testing.T) {
},
},
},
setupMocks: func(mockSecrets *secrets.MockRepositorySecrets) {},
expectedToken: "",
expectedEncryptedToken: "",
},
{
name: "trims only trailing slash from GitHub URL",
@@ -52,9 +45,6 @@ func TestMutator(t *testing.T) {
},
},
},
setupMocks: func(mockSecrets *secrets.MockRepositorySecrets) {},
expectedToken: "",
expectedEncryptedToken: "",
},
{
name: "trims only trailing .git from GitHub URL",
@@ -69,9 +59,6 @@ func TestMutator(t *testing.T) {
},
},
},
setupMocks: func(mockSecrets *secrets.MockRepositorySecrets) {},
expectedToken: "",
expectedEncryptedToken: "",
},
{
name: "does not trim if no .git or slash",
@@ -86,76 +73,6 @@ func TestMutator(t *testing.T) {
},
},
},
setupMocks: func(mockSecrets *secrets.MockRepositorySecrets) {},
expectedToken: "",
expectedEncryptedToken: "",
},
{
name: "successful token encryption",
obj: &provisioning.Repository{
ObjectMeta: metav1.ObjectMeta{
Name: "test-repo",
Namespace: "default",
},
Spec: provisioning.RepositorySpec{
GitHub: &provisioning.GitHubRepositoryConfig{
Token: "secret-token",
},
},
},
setupMocks: func(mockSecrets *secrets.MockRepositorySecrets) {
mockSecrets.EXPECT().Encrypt(
context.Background(),
&provisioning.Repository{
ObjectMeta: metav1.ObjectMeta{
Name: "test-repo",
Namespace: "default",
},
Spec: provisioning.RepositorySpec{
GitHub: &provisioning.GitHubRepositoryConfig{
Token: "secret-token",
},
},
},
"test-repo"+githubTokenSecretSuffix,
"secret-token",
).Return([]byte("encrypted-token"), nil)
},
expectedToken: "",
expectedEncryptedToken: "encrypted-token",
},
{
name: "encryption error",
obj: &provisioning.Repository{
ObjectMeta: metav1.ObjectMeta{
Name: "test-repo",
Namespace: "default",
},
Spec: provisioning.RepositorySpec{
GitHub: &provisioning.GitHubRepositoryConfig{
Token: "secret-token",
},
},
},
setupMocks: func(mockSecrets *secrets.MockRepositorySecrets) {
mockSecrets.EXPECT().Encrypt(
context.Background(),
&provisioning.Repository{
ObjectMeta: metav1.ObjectMeta{
Name: "test-repo",
Namespace: "default",
},
Spec: provisioning.RepositorySpec{
GitHub: &provisioning.GitHubRepositoryConfig{
Token: "secret-token",
},
},
},
"test-repo"+githubTokenSecretSuffix,
"secret-token",
).Return(nil, errors.New("encryption failed"))
},
expectedError: "encryption failed",
},
{
name: "no github spec",
@@ -168,9 +85,6 @@ func TestMutator(t *testing.T) {
GitHub: nil,
},
},
setupMocks: func(_ *secrets.MockRepositorySecrets) {
// No expectations
},
},
{
name: "empty token",
@@ -180,30 +94,19 @@ func TestMutator(t *testing.T) {
Namespace: "default",
},
Spec: provisioning.RepositorySpec{
GitHub: &provisioning.GitHubRepositoryConfig{
Token: "",
},
GitHub: &provisioning.GitHubRepositoryConfig{},
},
},
setupMocks: func(_ *secrets.MockRepositorySecrets) {
// No expectations
},
},
{
name: "non-repository object",
obj: &runtime.Unknown{},
setupMocks: func(_ *secrets.MockRepositorySecrets) {
// No expectations
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockSecrets := secrets.NewMockRepositorySecrets(t)
tt.setupMocks(mockSecrets)
mutator := Mutator(mockSecrets)
mutator := Mutator()
err := mutator(context.Background(), tt.obj)
if tt.expectedError != "" {
@@ -211,16 +114,6 @@ func TestMutator(t *testing.T) {
assert.Contains(t, err.Error(), tt.expectedError)
} else {
assert.NoError(t, err)
// Check that token was cleared and encrypted token was set
if repo, ok := tt.obj.(*provisioning.Repository); ok && repo.Spec.GitHub != nil {
if tt.expectedEncryptedToken != "" {
// Token should be cleared after encryption
assert.Empty(t, repo.Spec.GitHub.Token, "Token should be cleared after encryption")
// EncryptedToken should be set to the expected value
assert.Equal(t, tt.expectedEncryptedToken, string(repo.Spec.GitHub.EncryptedToken), "EncryptedToken should match expected value")
}
}
}
})
}
@@ -7,25 +7,20 @@ import (
"net/url"
"strings"
"github.com/grafana/grafana-app-sdk/logging"
"k8s.io/apimachinery/pkg/util/validation/field"
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository/git"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets"
)
//nolint:gosec // This is a constant for a secret suffix
const githubTokenSecretSuffix = "-github-token"
// Make sure all public functions of this struct call the (*githubRepository).logger function, to ensure the GH repo details are included.
type githubRepository struct {
git.GitRepository
config *provisioning.Repository
gh Client // assumes github.com base URL
secrets secrets.RepositorySecrets
config *provisioning.Repository
gh Client // assumes github.com base URL
owner string
repo string
@@ -42,7 +37,6 @@ type GithubRepository interface {
repository.Reader
repository.RepositoryWithURLs
repository.StageableRepository
repository.Hooks
Owner() string
Repo() string
Client() Client
@@ -53,8 +47,7 @@ func NewGitHub(
config *provisioning.Repository,
gitRepo git.GitRepository,
factory *Factory,
token string,
secrets secrets.RepositorySecrets,
token common.RawSecureValue,
) (GithubRepository, error) {
owner, repo, err := ParseOwnerRepoGithub(config.Spec.GitHub.URL)
if err != nil {
@@ -67,7 +60,6 @@ func NewGitHub(
gh: factory.New(ctx, token), // TODO, baseURL from config
owner: owner,
repo: repo,
secrets: secrets,
}, nil
}
@@ -242,23 +234,3 @@ func (r *githubRepository) RefURLs(ctx context.Context, ref string) (*provisioni
return urls, nil
}
func (r *githubRepository) OnCreate(_ context.Context) ([]map[string]interface{}, error) {
return nil, nil
}
func (r *githubRepository) OnUpdate(_ context.Context) ([]map[string]interface{}, error) {
return nil, nil
}
func (r *githubRepository) OnDelete(ctx context.Context) error {
logger := logging.FromContext(ctx)
secretName := r.config.Name + githubTokenSecretSuffix
if err := r.secrets.Delete(ctx, r.config, secretName); err != nil {
return fmt.Errorf("delete github token secret: %w", err)
}
logger.Info("Deleted github token secret", "secretName", secretName)
return nil
}
@@ -15,9 +15,9 @@ import (
field "k8s.io/apimachinery/pkg/util/validation/field"
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository/git"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets"
)
func TestNewGitHub(t *testing.T) {
@@ -81,16 +81,13 @@ func TestNewGitHub(t *testing.T) {
gitRepo := git.NewMockGitRepository(t)
mockSecrets := secrets.NewMockRepositorySecrets(t)
// Call the function under test
repo, err := NewGitHub(
context.Background(),
tt.config,
gitRepo,
factory,
tt.token,
mockSecrets,
common.RawSecureValue(tt.token),
)
// Check results
@@ -179,10 +176,14 @@ func TestGitHubRepositoryValidate(t *testing.T) {
GitHub: &provisioning.GitHubRepositoryConfig{
URL: "https://github.com/grafana/grafana",
Branch: "main",
Token: "valid-token",
Path: "dashboards",
},
},
Secure: provisioning.SecureValues{
Token: common.InlineSecureValue{
Name: "with-name",
},
},
},
mockSetup: func(m *git.MockGitRepository) {
m.On("Config").Return(&provisioning.Repository{
@@ -190,10 +191,14 @@ func TestGitHubRepositoryValidate(t *testing.T) {
GitHub: &provisioning.GitHubRepositoryConfig{
URL: "https://github.com/grafana/grafana",
Branch: "main",
Token: "valid-token",
Path: "dashboards",
},
},
Secure: provisioning.SecureValues{
Token: common.InlineSecureValue{
Name: "with-name",
},
},
})
m.On("Validate").Return(field.ErrorList{})
},
@@ -223,7 +228,11 @@ func TestGitHubRepositoryValidate(t *testing.T) {
GitHub: &provisioning.GitHubRepositoryConfig{
URL: "",
Branch: "main",
Token: "valid-token",
},
},
Secure: provisioning.SecureValues{
Token: common.InlineSecureValue{
Name: "with-name",
},
},
},
@@ -233,7 +242,11 @@ func TestGitHubRepositoryValidate(t *testing.T) {
GitHub: &provisioning.GitHubRepositoryConfig{
URL: "",
Branch: "main",
Token: "valid-token",
},
},
Secure: provisioning.SecureValues{
Token: common.InlineSecureValue{
Name: "with-name",
},
},
})
@@ -248,7 +261,11 @@ func TestGitHubRepositoryValidate(t *testing.T) {
GitHub: &provisioning.GitHubRepositoryConfig{
URL: "invalid-url",
Branch: "main",
Token: "valid-token",
},
},
Secure: provisioning.SecureValues{
Token: common.InlineSecureValue{
Name: "with-name",
},
},
},
@@ -258,7 +275,11 @@ func TestGitHubRepositoryValidate(t *testing.T) {
GitHub: &provisioning.GitHubRepositoryConfig{
URL: "invalid-url",
Branch: "main",
Token: "valid-token",
},
},
Secure: provisioning.SecureValues{
Token: common.InlineSecureValue{
Name: "with-name",
},
},
})
@@ -273,7 +294,11 @@ func TestGitHubRepositoryValidate(t *testing.T) {
GitHub: &provisioning.GitHubRepositoryConfig{
URL: "https://gitlab.com/grafana/grafana",
Branch: "main",
Token: "valid-token",
},
},
Secure: provisioning.SecureValues{
Token: common.InlineSecureValue{
Name: "with-name",
},
},
},
@@ -283,7 +308,11 @@ func TestGitHubRepositoryValidate(t *testing.T) {
GitHub: &provisioning.GitHubRepositoryConfig{
URL: "https://gitlab.com/grafana/grafana",
Branch: "main",
Token: "valid-token",
},
},
Secure: provisioning.SecureValues{
Token: common.InlineSecureValue{
Name: "with-name",
},
},
})
@@ -339,7 +368,11 @@ func TestGitHubRepositoryTest(t *testing.T) {
GitHub: &provisioning.GitHubRepositoryConfig{
URL: "https://github.com/grafana/grafana",
Branch: "main",
Token: "valid-token",
},
},
Secure: provisioning.SecureValues{
Token: common.InlineSecureValue{
Name: "with-name",
},
},
},
@@ -361,7 +394,11 @@ func TestGitHubRepositoryTest(t *testing.T) {
GitHub: &provisioning.GitHubRepositoryConfig{
URL: "invalid-url",
Branch: "main",
Token: "valid-token",
},
},
Secure: provisioning.SecureValues{
Token: common.InlineSecureValue{
Name: "with-name",
},
},
},
@@ -878,7 +915,11 @@ func TestGitHubRepositoryDelegation(t *testing.T) {
GitHub: &provisioning.GitHubRepositoryConfig{
URL: "https://github.com/grafana/grafana",
Branch: "main",
Token: "test-token",
},
},
Secure: provisioning.SecureValues{
Token: common.InlineSecureValue{
Name: "with-name",
},
},
}
@@ -1095,7 +1136,6 @@ func TestGitHubRepositoryAccessors(t *testing.T) {
GitHub: &provisioning.GitHubRepositoryConfig{
URL: "https://github.com/grafana/grafana",
Branch: "main",
Token: "test-token",
},
},
}
@@ -1137,82 +1177,6 @@ func TestGitHubRepositoryAccessors(t *testing.T) {
})
}
func TestGitHubRepository_OnDelete(t *testing.T) {
tests := []struct {
name string
setupMock func(*secrets.MockRepositorySecrets)
config *provisioning.Repository
expectedError string
}{
{
name: "successful secret deletion",
setupMock: func(mockSecrets *secrets.MockRepositorySecrets) {
mockSecrets.EXPECT().Delete(
context.Background(),
&provisioning.Repository{
ObjectMeta: metav1.ObjectMeta{
Name: "test-repo",
Namespace: "default",
},
},
"test-repo"+githubTokenSecretSuffix,
).Return(nil)
},
config: &provisioning.Repository{
ObjectMeta: metav1.ObjectMeta{
Name: "test-repo",
Namespace: "default",
},
},
},
{
name: "secret deletion error",
setupMock: func(mockSecrets *secrets.MockRepositorySecrets) {
mockSecrets.EXPECT().Delete(
context.Background(),
&provisioning.Repository{
ObjectMeta: metav1.ObjectMeta{
Name: "test-repo",
Namespace: "default",
},
},
"test-repo"+githubTokenSecretSuffix,
).Return(errors.New("failed to delete secret"))
},
config: &provisioning.Repository{
ObjectMeta: metav1.ObjectMeta{
Name: "test-repo",
Namespace: "default",
},
},
expectedError: "delete github token secret: failed to delete secret",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockSecrets := secrets.NewMockRepositorySecrets(t)
tt.setupMock(mockSecrets)
githubRepo := &githubRepository{
config: tt.config,
secrets: mockSecrets,
}
err := githubRepo.OnDelete(context.Background())
if tt.expectedError != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.expectedError)
} else {
require.NoError(t, err)
}
mockSecrets.AssertExpectations(t)
})
}
}
func TestGithubRepository_Move(t *testing.T) {
tests := []struct {
name string
@@ -1262,7 +1226,6 @@ func TestGithubRepository_Move(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
// Create mock git repository
mockGitRepo := git.NewMockGitRepository(t)
mockSecrets := &secrets.MockRepositorySecrets{}
// Setup mock expectations
tt.setupMock(mockGitRepo)
@@ -1285,7 +1248,6 @@ func TestGithubRepository_Move(t *testing.T) {
GitRepository: mockGitRepo,
owner: "example",
repo: "repo",
secrets: mockSecrets,
}
// Execute move operation