Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,20 @@ builds:
- darwin
ldflags:
- "-s -w -X github.com/isindir/git-get/version.Version=v{{.Version}} -X github.com/isindir/git-get/version.Commit={{.ShortCommit}} -X github.com/isindir/git-get/version.Time={{.CommitDate}}"
brews:
- name: git-get
repository:
homebrew_casks:
- repository:
owner: isindir
name: homebrew-git-get
branch: master
token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
commit_author:
name: Eriks Zelenka
email: isindir@users.sf.net
commit_msg_template: "Brew formula update for {{ .ProjectName }} version {{ .Tag }} - {{.ShortCommit}}"
directory: Formula
commit_msg_template: "Brew cask update for {{ .ProjectName }} version {{ .Tag }} - {{.ShortCommit}}"
directory: Casks
homepage: "https://github.com/isindir/git-get"
license: "MIT"
description: "Tool to clone/fetch project repositories an-mass using Gitfile configuration file. git-get can also generate Gitfile from github, gitlab or bitbucket, to have all repositories user has access to."
test: |
system "#{bin}/git-get", "version", "--long"
release:
prerelease: auto

Expand Down
30 changes: 30 additions & 0 deletions .mockery.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
packages:
github.com/isindir/git-get/exec:
config:
all: true
dir: "exec/mocks"
pkgname: "mocks"
filename: "mocks.go"
structname: "{{.InterfaceName}}"
github.com/isindir/git-get/gitlab:
config:
all: true
dir: "gitlab/mocks"
pkgname: "mocks"
filename: "mocks.go"
structname: "{{.InterfaceName}}"
github.com/isindir/git-get/github:
config:
all: true
dir: "github/mocks"
pkgname: "mocks"
filename: "mocks.go"
structname: "{{.InterfaceName}}"
github.com/isindir/git-get/bitbucket:
config:
all: true
dir: "bitbucket/mocks"
pkgname: "mocks"
filename: "mocks.go"
structname: "{{.InterfaceName}}"

10 changes: 5 additions & 5 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# UPDATE_HERE
# https://golang.org/dl/
golang 1.24.3
golang 1.25.5
# https://github.com/goreleaser/goreleaser/releases
goreleaser 2.9.0
goreleaser 2.13.2
# https://github.com/vektra/mockery/releases
mockery 2.50.0
mockery 3.6.1
# https://github.com/caarlos0/svu/releases
svu 3.2.3
svu 3.3.0
# https://github.com/golangci/golangci-lint/releases
golangci-lint 2.1.6
golangci-lint 2.8.0
7 changes: 3 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,12 @@ vet: ## Run go vet against code.

.PHONY: mockery
mockery: ## Regenerate mock files
for i in exec gitlab; do \
(cd $$i; rm -fr mocks; mockery --all) ;\
done
rm -fr exec/mocks gitlab/mocks github/mocks bitbucket/mocks
mockery

.PHONY: clean-mockery
clean-mockery: ## Clean mock files
for i in exec gitlab; do \
for i in exec gitlab github bitbucket; do \
(cd $$i; rm -fr mocks) ;\
done

Expand Down
74 changes: 58 additions & 16 deletions bitbucket/bitbucket.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright © 2021-2022 Eriks Zelenka <isindir@users.sourceforge.net>
Copyright © 2021-2026 Eriks Zelenka <isindir@users.sourceforge.net>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -33,20 +33,41 @@ import (
bitbucket "github.com/ktrysmt/go-bitbucket"
)

func bitbucketAuth(repoSha string) *bitbucket.Client {
username, usernameFound := os.LookupEnv("BITBUCKET_USERNAME")
type GitGetBitbucket struct {
username string
token string
}

type GitGetBitbucketI interface {
Init() bool
RepositoryExists(repoSha, owner, repository string) bool
CreateRepository(repoSha, repository, mirrorVisibilityMode, sourceURL, projectName string) *bitbucket.Repository
FetchOwnerRepos(repoSha, owner, bitbucketRole string) []bitbucket.Repository
}

func (gitProvider *GitGetBitbucket) Init() bool {
var usernameFound, tokenFound bool
gitProvider.username, usernameFound = os.LookupEnv("BITBUCKET_USERNAME")
if !usernameFound {
log.Fatalf("%s: Error - environment variable BITBUCKET_TOKEN not found", repoSha)
log.Fatal("Error - environment variable BITBUCKET_USERNAME not found")
os.Exit(1)
}

token, tokenFound := os.LookupEnv("BITBUCKET_TOKEN")
gitProvider.token, tokenFound = os.LookupEnv("BITBUCKET_TOKEN")
if !tokenFound {
log.Fatalf("%s: Error - environment variable BITBUCKET_TOKEN not found", repoSha)
log.Fatal("Error - environment variable BITBUCKET_TOKEN not found")
os.Exit(1)
}

git := bitbucket.NewBasicAuth(username, token)
return usernameFound && tokenFound
}

func (gitProvider *GitGetBitbucket) auth(repoSha string) *bitbucket.Client {
git, err := bitbucket.NewBasicAuth(gitProvider.username, gitProvider.token)
if err != nil {
log.Fatalf("%s: Error - authentication failed", repoSha)
os.Exit(1)
}

return git
}
Expand All @@ -57,9 +78,9 @@ func GenerateProjectKey(projectName string) string {
return strings.ToUpper(re.ReplaceAllString(projectName, ""))
}

// RepositoryExists - checks if bitbucket repository exists
func RepositoryExists(repoSha, owner, repository string) bool {
git := bitbucketAuth(repoSha)
// RepositoryExists - checks if bitbucket repository exists (method)
func (gitProvider *GitGetBitbucket) RepositoryExists(repoSha, owner, repository string) bool {
git := gitProvider.auth(repoSha)

repoOptions := &bitbucket.RepositoryOptions{
Owner: owner,
Expand All @@ -75,6 +96,13 @@ func RepositoryExists(repoSha, owner, repository string) bool {
return true
}

// RepositoryExists - checks if bitbucket repository exists (package function for backward compatibility)
func RepositoryExists(repoSha, owner, repository string) bool {
gitProvider := &GitGetBitbucket{}
gitProvider.Init()
return gitProvider.RepositoryExists(repoSha, owner, repository)
}

// ProjectExists - checks if bitbucket project exists
func ProjectExists(git *bitbucket.Client, repoSha, workspace, project string) bool {
opt := &bitbucket.ProjectOptions{
Expand All @@ -93,9 +121,9 @@ func ProjectExists(git *bitbucket.Client, repoSha, workspace, project string) bo
return true
}

// CreateRepository - create bitbucket repository
func CreateRepository(repoSha, repository, mirrorVisibilityMode, sourceURL, projectName string) *bitbucket.Repository {
git := bitbucketAuth(repoSha)
// CreateRepository - create bitbucket repository (method)
func (gitProvider *GitGetBitbucket) CreateRepository(repoSha, repository, mirrorVisibilityMode, sourceURL, projectName string) *bitbucket.Repository {
git := gitProvider.auth(repoSha)

repoNameParts := strings.SplitN(repository, "/", 2)
owner, repoSlug := repoNameParts[0], repoNameParts[1]
Expand Down Expand Up @@ -132,12 +160,19 @@ func CreateRepository(repoSha, repository, mirrorVisibilityMode, sourceURL, proj
return resultingRepository
}

// FetchOwnerRepos - fetch owner repositories via API
func FetchOwnerRepos(repoSha, owner, bitbucketRole string) []bitbucket.Repository {
// CreateRepository - create bitbucket repository (package function for backward compatibility)
func CreateRepository(repoSha, repository, mirrorVisibilityMode, sourceURL, projectName string) *bitbucket.Repository {
gitProvider := &GitGetBitbucket{}
gitProvider.Init()
return gitProvider.CreateRepository(repoSha, repository, mirrorVisibilityMode, sourceURL, projectName)
}

// FetchOwnerRepos - fetch owner repositories via API (method)
func (gitProvider *GitGetBitbucket) FetchOwnerRepos(repoSha, owner, bitbucketRole string) []bitbucket.Repository {
log.Debugf("%s: Specified owner: '%s'", repoSha, owner)
var reposToReutrn []bitbucket.Repository

git := bitbucketAuth(repoSha)
git := gitProvider.auth(repoSha)

opts := &bitbucket.RepositoriesOptions{
Owner: owner,
Expand All @@ -161,3 +196,10 @@ func FetchOwnerRepos(repoSha, owner, bitbucketRole string) []bitbucket.Repositor

return reposToReutrn
}

// FetchOwnerRepos - fetch owner repositories via API (package function for backward compatibility)
func FetchOwnerRepos(repoSha, owner, bitbucketRole string) []bitbucket.Repository {
gitProvider := &GitGetBitbucket{}
gitProvider.Init()
return gitProvider.FetchOwnerRepos(repoSha, owner, bitbucketRole)
}
155 changes: 155 additions & 0 deletions bitbucket/bitbucket_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//go:build !integration
// +build !integration

package bitbucket

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
)

func TestGitGetBitbucket_Init_Success(t *testing.T) {
// Set up environment variables
originalUsername := os.Getenv("BITBUCKET_USERNAME")
originalToken := os.Getenv("BITBUCKET_TOKEN")
defer func() {
if originalUsername != "" {
os.Setenv("BITBUCKET_USERNAME", originalUsername)
} else {
os.Unsetenv("BITBUCKET_USERNAME")
}
if originalToken != "" {
os.Setenv("BITBUCKET_TOKEN", originalToken)
} else {
os.Unsetenv("BITBUCKET_TOKEN")
}
}()

os.Setenv("BITBUCKET_USERNAME", "test-user")
os.Setenv("BITBUCKET_TOKEN", "test-token-123")

gitProvider := &GitGetBitbucket{}
result := gitProvider.Init()

assert.True(t, result)
assert.Equal(t, "test-user", gitProvider.username)
assert.Equal(t, "test-token-123", gitProvider.token)
}

func TestGitGetBitbucket_Init_MissingUsername(t *testing.T) {
// This test would cause os.Exit(1), so we skip it in unit tests
t.Skip("Skipping test that calls os.Exit(1)")
}

func TestGitGetBitbucket_Init_MissingToken(t *testing.T) {
// This test would cause os.Exit(1), so we skip it in unit tests
t.Skip("Skipping test that calls os.Exit(1)")
}

func TestGenerateProjectKey(t *testing.T) {
testCases := []struct {
name string
input string
expected string
}{
{
name: "simple name",
input: "myproject",
expected: "MYPROJECT",
},
{
name: "name with spaces",
input: "my project",
expected: "MYPROJECT",
},
{
name: "name with hyphens",
input: "my-project-name",
expected: "MYPROJECTNAME",
},
{
name: "name with special chars",
input: "my@project#name!",
expected: "MYPROJECTNAME",
},
{
name: "name with underscores",
input: "my_project_name",
expected: "MY_PROJECT_NAME",
},
{
name: "mixed case with numbers",
input: "MyProject123",
expected: "MYPROJECT123",
},
{
name: "name with dots",
input: "my.project.name",
expected: "MYPROJECTNAME",
},
{
name: "complex name",
input: "My-Project_Name.123!@#",
expected: "MYPROJECT_NAME123",
},
{
name: "empty string",
input: "",
expected: "",
},
{
name: "only special chars",
input: "@#$%^&*()",
expected: "",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := GenerateProjectKey(tc.input)
assert.Equal(t, tc.expected, result)
})
}
}

func TestGitGetBitbucket_Auth(t *testing.T) {
// This test requires actual Bitbucket API or mocking at HTTP level
t.Skip("Requires Bitbucket API mocking or integration test")
}

func TestGitGetBitbucket_RepositoryExists(t *testing.T) {
// This test requires actual Bitbucket API or mocking at HTTP level
t.Skip("Requires Bitbucket API mocking or integration test")
}

func TestRepositoryExists_PackageFunction(t *testing.T) {
// This test requires actual Bitbucket API or mocking at HTTP level
t.Skip("Requires Bitbucket API mocking or integration test")
}

func TestProjectExists(t *testing.T) {
// This test requires actual Bitbucket API or mocking at HTTP level
t.Skip("Requires Bitbucket API mocking or integration test")
}

func TestGitGetBitbucket_CreateRepository(t *testing.T) {
// This test requires actual Bitbucket API or mocking at HTTP level
t.Skip("Requires Bitbucket API mocking or integration test")
}

func TestCreateRepository_PackageFunction(t *testing.T) {
// This test requires actual Bitbucket API or mocking at HTTP level
t.Skip("Requires Bitbucket API mocking or integration test")
}

func TestGitGetBitbucket_FetchOwnerRepos(t *testing.T) {
// This test requires actual Bitbucket API or mocking at HTTP level
t.Skip("Requires Bitbucket API mocking or integration test")
}

func TestFetchOwnerRepos_PackageFunction(t *testing.T) {
// This test requires actual Bitbucket API or mocking at HTTP level
t.Skip("Requires Bitbucket API mocking or integration test")
}
Loading