Compare commits
2 Commits
84753ee4b9
...
1f04ffbf80
Author | SHA1 | Date |
---|---|---|
Robert Kaussow | 1f04ffbf80 | |
Robert Kaussow | 47bfb740d7 |
|
@ -0,0 +1,73 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFetchSource(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "fetch from origin with branch",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
Branch: "main",
|
||||
},
|
||||
want: []string{gitBin, "fetch", "origin", "+main:"},
|
||||
},
|
||||
{
|
||||
name: "fetch from origin with different branch",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
Branch: "develop",
|
||||
},
|
||||
want: []string{gitBin, "fetch", "origin", "+develop:"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := FetchSource(tt.repo)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckoutHead(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "checkout head with branch",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
Branch: "main",
|
||||
},
|
||||
want: []string{gitBin, "checkout", "-qf", "main"},
|
||||
},
|
||||
{
|
||||
name: "checkout head with different branch",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
Branch: "develop",
|
||||
},
|
||||
want: []string{gitBin, "checkout", "-qf", "develop"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := CheckoutHead(tt.repo)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@ func Add(repo Repository) *types.Cmd {
|
|||
}
|
||||
|
||||
// TestCleanTree returns non-zero if diff between index and local repository.
|
||||
func TestCleanTree(repo Repository) *types.Cmd {
|
||||
func IsCleanTree(repo Repository) *types.Cmd {
|
||||
cmd := execabs.Command(
|
||||
gitBin,
|
||||
"diff-index",
|
||||
|
@ -64,10 +64,7 @@ func EmptyCommit(repo Repository) *types.Cmd {
|
|||
repo.CommitMsg,
|
||||
}
|
||||
|
||||
cmd := execabs.Command(
|
||||
gitBin,
|
||||
args...,
|
||||
)
|
||||
cmd := execabs.Command(gitBin, args...)
|
||||
cmd.Dir = repo.WorkDir
|
||||
|
||||
if repo.NoVerify {
|
||||
|
@ -86,10 +83,7 @@ func Commit(repo Repository) *types.Cmd {
|
|||
repo.CommitMsg,
|
||||
}
|
||||
|
||||
cmd := execabs.Command(
|
||||
gitBin,
|
||||
args...,
|
||||
)
|
||||
cmd := execabs.Command(gitBin, args...)
|
||||
cmd.Dir = repo.WorkDir
|
||||
|
||||
if repo.NoVerify {
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "add all files",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
Add: "",
|
||||
},
|
||||
want: []string{gitBin, "add", "--all"},
|
||||
},
|
||||
{
|
||||
name: "add specific file",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
Add: "file.go",
|
||||
},
|
||||
want: []string{gitBin, "add", "file.go"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := Add(tt.repo)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsCleanTree(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "clean working tree",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
},
|
||||
want: []string{gitBin, "diff-index", "--quiet", "HEAD", "--ignore-submodules"},
|
||||
},
|
||||
{
|
||||
name: "unclean working tree",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/unclean/repo",
|
||||
},
|
||||
want: []string{gitBin, "diff-index", "--quiet", "HEAD", "--ignore-submodules"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := IsCleanTree(tt.repo)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyCommit(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "empty commit with default options",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
CommitMsg: "Empty commit",
|
||||
},
|
||||
want: []string{gitBin, "commit", "--allow-empty", "-m", "Empty commit"},
|
||||
},
|
||||
{
|
||||
name: "empty commit with no-verify option",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
CommitMsg: "Empty commit",
|
||||
NoVerify: true,
|
||||
},
|
||||
want: []string{gitBin, "commit", "--allow-empty", "-m", "Empty commit", "--no-verify"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := EmptyCommit(tt.repo)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -7,7 +7,8 @@ import (
|
|||
"golang.org/x/sys/execabs"
|
||||
)
|
||||
|
||||
// repoUserEmail sets the global git author email.
|
||||
// ConfigAutocorrect sets the local git autocorrect configuration for the given repository.
|
||||
// The autocorrect setting determines how git handles minor typos in commands.
|
||||
func ConfigAutocorrect(repo Repository) *types.Cmd {
|
||||
args := []string{
|
||||
"config",
|
||||
|
@ -24,7 +25,7 @@ func ConfigAutocorrect(repo Repository) *types.Cmd {
|
|||
}
|
||||
}
|
||||
|
||||
// repoUserEmail sets the global git author email.
|
||||
// ConfigUserEmail sets the global git author email.
|
||||
func ConfigUserEmail(repo Repository) *types.Cmd {
|
||||
args := []string{
|
||||
"config",
|
||||
|
@ -41,7 +42,7 @@ func ConfigUserEmail(repo Repository) *types.Cmd {
|
|||
}
|
||||
}
|
||||
|
||||
// repoUserName sets the global git author name.
|
||||
// ConfigUserName configures the user.name git config setting for the given repository.
|
||||
func ConfigUserName(repo Repository) *types.Cmd {
|
||||
args := []string{
|
||||
"config",
|
||||
|
@ -58,7 +59,7 @@ func ConfigUserName(repo Repository) *types.Cmd {
|
|||
}
|
||||
}
|
||||
|
||||
// ConfigSSLVerify disables globally the git ssl verification.
|
||||
// ConfigSSLVerify configures the http.sslVerify git config setting for the given repository.
|
||||
func ConfigSSLVerify(repo Repository, skipVerify bool) *types.Cmd {
|
||||
args := []string{
|
||||
"config",
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConfigAutocorrect(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "enable autocorrect",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
Autocorrect: "1",
|
||||
},
|
||||
want: []string{gitBin, "config", "--local", "help.autocorrect", "1"},
|
||||
},
|
||||
{
|
||||
name: "disable autocorrect",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
Autocorrect: "0",
|
||||
},
|
||||
want: []string{gitBin, "config", "--local", "help.autocorrect", "0"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := ConfigAutocorrect(tt.repo)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigUserEmail(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "set user email",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
Author: Author{
|
||||
Email: "user@example.com",
|
||||
},
|
||||
},
|
||||
want: []string{gitBin, "config", "--local", "user.email", "user@example.com"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := ConfigUserEmail(tt.repo)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigUserName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "set user name",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
Author: Author{
|
||||
Name: "John Doe",
|
||||
},
|
||||
},
|
||||
want: []string{gitBin, "config", "--local", "user.name", "John Doe"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := ConfigUserName(tt.repo)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigSSLVerify(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
skipVerify bool
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "enable SSL verification",
|
||||
repo: Repository{WorkDir: "/path/to/repo"},
|
||||
skipVerify: false,
|
||||
want: []string{gitBin, "config", "--local", "http.sslVerify", "true"},
|
||||
},
|
||||
{
|
||||
name: "disable SSL verification",
|
||||
repo: Repository{WorkDir: "/path/to/repo"},
|
||||
skipVerify: true,
|
||||
want: []string{gitBin, "config", "--local", "http.sslVerify", "false"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := ConfigSSLVerify(tt.repo, tt.skipVerify)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import (
|
|||
"golang.org/x/sys/execabs"
|
||||
)
|
||||
|
||||
// Init creates a new Git repository in the given Repository's WorkDir.
|
||||
// Init creates a new Git repository in the specified directory.
|
||||
func Init(repo Repository) *types.Cmd {
|
||||
cmd := execabs.Command(
|
||||
gitBin,
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
repo := Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
}
|
||||
|
||||
cmd := Init(repo)
|
||||
require.Equal(t, []string{gitBin, "init"}, cmd.Cmd.Args)
|
||||
require.Equal(t, repo.WorkDir, cmd.Cmd.Dir)
|
||||
|
||||
// Test with an empty work directory
|
||||
repo.WorkDir = ""
|
||||
cmd = Init(repo)
|
||||
require.Equal(t, []string{gitBin, "init"}, cmd.Cmd.Args)
|
||||
require.Empty(t, cmd.Cmd.Dir)
|
||||
}
|
|
@ -15,10 +15,7 @@ func RemoteRemove(repo Repository) *types.Cmd {
|
|||
repo.RemoteName,
|
||||
}
|
||||
|
||||
cmd := execabs.Command(
|
||||
gitBin,
|
||||
args...,
|
||||
)
|
||||
cmd := execabs.Command(gitBin, args...)
|
||||
cmd.Dir = repo.WorkDir
|
||||
|
||||
return &types.Cmd{
|
||||
|
@ -35,10 +32,7 @@ func RemoteAdd(repo Repository) *types.Cmd {
|
|||
repo.RemoteURL,
|
||||
}
|
||||
|
||||
cmd := execabs.Command(
|
||||
gitBin,
|
||||
args...,
|
||||
)
|
||||
cmd := execabs.Command(gitBin, args...)
|
||||
cmd.Dir = repo.WorkDir
|
||||
|
||||
return &types.Cmd{
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRemoteRemove(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "remove remote",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
RemoteName: "origin",
|
||||
},
|
||||
want: []string{gitBin, "remote", "rm", "origin"},
|
||||
},
|
||||
{
|
||||
name: "remove custom remote name",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
RemoteName: "upstream",
|
||||
},
|
||||
want: []string{gitBin, "remote", "rm", "upstream"},
|
||||
},
|
||||
{
|
||||
name: "remove remote with empty work dir",
|
||||
repo: Repository{
|
||||
RemoteName: "origin",
|
||||
},
|
||||
want: []string{gitBin, "remote", "rm", "origin"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := RemoteRemove(tt.repo)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteAdd(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "add remote with valid inputs",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
RemoteName: "origin",
|
||||
RemoteURL: "https://example.com/repo.git",
|
||||
},
|
||||
want: []string{gitBin, "remote", "add", "origin", "https://example.com/repo.git"},
|
||||
},
|
||||
{
|
||||
name: "add remote with empty work dir",
|
||||
repo: Repository{
|
||||
RemoteName: "origin",
|
||||
RemoteURL: "https://example.com/repo.git",
|
||||
},
|
||||
want: []string{gitBin, "remote", "add", "origin", "https://example.com/repo.git"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := RemoteAdd(tt.repo)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemotePush(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want []string
|
||||
forcePush bool
|
||||
followTags bool
|
||||
}{
|
||||
{
|
||||
name: "push with default options",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
RemoteName: "origin",
|
||||
Branch: "main",
|
||||
},
|
||||
want: []string{gitBin, "push", "origin", "HEAD:main"},
|
||||
},
|
||||
{
|
||||
name: "push with force option",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
RemoteName: "origin",
|
||||
Branch: "main",
|
||||
ForcePush: true,
|
||||
},
|
||||
want: []string{gitBin, "push", "origin", "HEAD:main", "--force"},
|
||||
forcePush: true,
|
||||
},
|
||||
{
|
||||
name: "push with follow tags option",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
RemoteName: "origin",
|
||||
Branch: "main",
|
||||
PushFollowTags: true,
|
||||
},
|
||||
want: []string{gitBin, "push", "origin", "HEAD:main", "--follow-tags"},
|
||||
followTags: true,
|
||||
},
|
||||
{
|
||||
name: "push with empty work dir",
|
||||
repo: Repository{
|
||||
RemoteName: "origin",
|
||||
Branch: "main",
|
||||
},
|
||||
want: []string{gitBin, "push", "origin", "HEAD:main"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := RemotePush(tt.repo)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,14 +1,12 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/thegeeklab/wp-plugin-go/v2/types"
|
||||
"golang.org/x/sys/execabs"
|
||||
)
|
||||
|
||||
// Status returns a command that runs `git status --porcelain` in the given repository's working directory.
|
||||
// Status returns a command that runs `git status --porcelain` for the given repository.
|
||||
func Status(repo Repository) *types.Cmd {
|
||||
cmd := execabs.Command(
|
||||
gitBin,
|
||||
|
@ -16,7 +14,6 @@ func Status(repo Repository) *types.Cmd {
|
|||
"--porcelain",
|
||||
)
|
||||
cmd.Dir = repo.WorkDir
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
return &types.Cmd{
|
||||
Cmd: cmd,
|
||||
|
@ -24,8 +21,9 @@ func Status(repo Repository) *types.Cmd {
|
|||
}
|
||||
|
||||
// IsDirty checks if the given repository has any uncommitted changes.
|
||||
// It runs the `git status --porcelain` command and returns true if the output is non-empty,
|
||||
// It runs `git status --porcelain` and returns true if the output is non-empty,
|
||||
// indicating that there are uncommitted changes in the repository.
|
||||
// If there is an error running the git command, it returns false.
|
||||
func IsDirty(repo Repository) bool {
|
||||
cmd := Status(repo)
|
||||
cmd.Dir = repo.WorkDir
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStatus(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "with work dir",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
},
|
||||
want: []string{gitBin, "status", "--porcelain"},
|
||||
},
|
||||
{
|
||||
name: "without work dir",
|
||||
repo: Repository{},
|
||||
want: []string{gitBin, "status", "--porcelain"},
|
||||
},
|
||||
{
|
||||
name: "with custom stderr",
|
||||
repo: Repository{
|
||||
WorkDir: "/path/to/repo",
|
||||
},
|
||||
want: []string{gitBin, "status", "--porcelain"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := Status(tt.repo)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.repo.WorkDir, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsDirty(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo Repository
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "dirty repo",
|
||||
repo: Repository{
|
||||
WorkDir: t.TempDir(),
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "clean repo",
|
||||
repo: Repository{
|
||||
WorkDir: t.TempDir(),
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := Init(tt.repo).Run(); err != nil {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if tt.want {
|
||||
_, err := os.Create(filepath.Join(tt.repo.WorkDir, "dummy"))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
isDirty := IsDirty(tt.repo)
|
||||
require.Equal(t, tt.want, isDirty)
|
||||
})
|
||||
}
|
||||
}
|
70
git/util.go
70
git/util.go
|
@ -3,18 +3,15 @@ package git
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
netrcFile = `
|
||||
machine %s
|
||||
netrcFile = `machine %s
|
||||
login %s
|
||||
password %s
|
||||
`
|
||||
configFile = `
|
||||
Host *
|
||||
configFile = `Host *
|
||||
StrictHostKeyChecking no
|
||||
UserKnownHostsFile=/dev/null
|
||||
`
|
||||
|
@ -22,65 +19,38 @@ UserKnownHostsFile=/dev/null
|
|||
|
||||
const (
|
||||
strictFilePerm = 0o600
|
||||
strictDirPerm = 0o600
|
||||
strictDirPerm = 0o700
|
||||
)
|
||||
|
||||
// WriteKey writes the SSH private key.
|
||||
func WriteSSHKey(privateKey string) error {
|
||||
home := "/root"
|
||||
func WriteSSHKey(path, key string) error {
|
||||
sshPath := filepath.Join(path, ".ssh")
|
||||
confPath := filepath.Join(sshPath, "config")
|
||||
keyPath := filepath.Join(sshPath, "id_rsa")
|
||||
|
||||
if currentUser, err := user.Current(); err == nil {
|
||||
home = currentUser.HomeDir
|
||||
if err := os.MkdirAll(sshPath, strictDirPerm); err != nil {
|
||||
return fmt.Errorf("failed to create .ssh directory: %w", err)
|
||||
}
|
||||
|
||||
sshpath := filepath.Join(home, ".ssh")
|
||||
|
||||
if err := os.MkdirAll(sshpath, strictDirPerm); err != nil {
|
||||
return err
|
||||
if err := os.WriteFile(confPath, []byte(configFile), strictFilePerm); err != nil {
|
||||
return fmt.Errorf("failed to create .ssh/config file: %w", err)
|
||||
}
|
||||
|
||||
confpath := filepath.Join(sshpath, "config")
|
||||
|
||||
if err := os.WriteFile(
|
||||
confpath,
|
||||
[]byte(configFile),
|
||||
strictFilePerm,
|
||||
); err != nil {
|
||||
return err
|
||||
if err := os.WriteFile(keyPath, []byte(key), strictFilePerm); err != nil {
|
||||
return fmt.Errorf("failed to create .ssh/id_rsa file: %w", err)
|
||||
}
|
||||
|
||||
privpath := filepath.Join(sshpath, "id_rsa")
|
||||
|
||||
return os.WriteFile(
|
||||
privpath,
|
||||
[]byte(privateKey),
|
||||
strictFilePerm,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteNetrc writes the netrc file.
|
||||
func WriteNetrc(machine, login, password string) error {
|
||||
netrcContent := fmt.Sprintf(
|
||||
netrcFile,
|
||||
machine,
|
||||
login,
|
||||
password,
|
||||
)
|
||||
func WriteNetrc(path, machine, login, password string) error {
|
||||
netrcPath := filepath.Join(path, ".netrc")
|
||||
netrcContent := fmt.Sprintf(netrcFile, machine, login, password)
|
||||
|
||||
home := "/root"
|
||||
|
||||
if currentUser, err := user.Current(); err == nil {
|
||||
home = currentUser.HomeDir
|
||||
if err := os.WriteFile(netrcPath, []byte(netrcContent), strictFilePerm); err != nil {
|
||||
return fmt.Errorf("failed to create .netrc file: %w", err)
|
||||
}
|
||||
|
||||
netpath := filepath.Join(
|
||||
home,
|
||||
".netrc",
|
||||
)
|
||||
|
||||
return os.WriteFile(
|
||||
netpath,
|
||||
[]byte(netrcContent),
|
||||
strictFilePerm,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWriteSSHKey(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
privateKey string
|
||||
dir string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid private key",
|
||||
privateKey: "valid_private_key",
|
||||
dir: t.TempDir(),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty private key",
|
||||
privateKey: "",
|
||||
dir: t.TempDir(),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := WriteSSHKey(tt.dir, tt.privateKey)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
privateKeyPath := filepath.Join(tt.dir, ".ssh", "id_rsa")
|
||||
_, err = os.Stat(privateKeyPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
configPath := filepath.Join(tt.dir, ".ssh", "config")
|
||||
_, err = os.Stat(configPath)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteNetrc(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
machine string
|
||||
login string
|
||||
password string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid input",
|
||||
path: t.TempDir(),
|
||||
machine: "example.com",
|
||||
login: "user",
|
||||
password: "pass",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := WriteNetrc(tt.path, tt.machine, tt.login, tt.password)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
netrcPath := filepath.Join(tt.path, ".netrc")
|
||||
_, err = os.Stat(netrcPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
content, err := os.ReadFile(netrcPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := fmt.Sprintf("machine %s\nlogin %s\npassword %s\n", tt.machine, tt.login, tt.password)
|
||||
require.Equal(t, expected, string(content))
|
||||
})
|
||||
}
|
||||
}
|
4
go.mod
4
go.mod
|
@ -4,6 +4,7 @@ go 1.22
|
|||
|
||||
require (
|
||||
github.com/rs/zerolog v1.32.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/thegeeklab/wp-plugin-go/v2 v2.2.0
|
||||
github.com/urfave/cli/v2 v2.27.2
|
||||
golang.org/x/sys v0.20.0
|
||||
|
@ -14,6 +15,7 @@ require (
|
|||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/uuid v1.1.1 // indirect
|
||||
github.com/huandu/xstrings v1.3.3 // indirect
|
||||
github.com/imdario/mergo v0.3.11 // indirect
|
||||
|
@ -22,10 +24,12 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mitchellh/copystructure v1.0.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
1
go.sum
1
go.sum
|
@ -89,6 +89,7 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
|||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
|
|
|
@ -22,6 +22,13 @@ var (
|
|||
ErrGitCloneDestintionNotValid = errors.New("destination not valid")
|
||||
)
|
||||
|
||||
const (
|
||||
ActionClone Action = "clone"
|
||||
ActionCommit Action = "commit"
|
||||
ActionPush Action = "push"
|
||||
ActionPages Action = "pages"
|
||||
)
|
||||
|
||||
//nolint:revive
|
||||
func (p *Plugin) run(ctx context.Context) error {
|
||||
if err := p.Validate(); err != nil {
|
||||
|
@ -48,20 +55,21 @@ func (p *Plugin) Validate() error {
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to get working directory: %w", err)
|
||||
}
|
||||
|
||||
for _, action := range p.Settings.Action.Value() {
|
||||
for _, actionStr := range p.Settings.Action.Value() {
|
||||
action := Action(actionStr)
|
||||
switch action {
|
||||
case "clone":
|
||||
case ActionClone:
|
||||
continue
|
||||
case "commit":
|
||||
case ActionCommit:
|
||||
continue
|
||||
case "push":
|
||||
case ActionPush:
|
||||
if p.Settings.SSHKey == "" && p.Settings.Netrc.Password == "" {
|
||||
return ErrAuthSourceNotSet
|
||||
}
|
||||
case "pages":
|
||||
case ActionPages:
|
||||
p.Settings.Pages.Directory = filepath.Join(p.Settings.Repo.WorkDir, p.Settings.Pages.Directory)
|
||||
p.Settings.Repo.WorkDir = filepath.Join(p.Settings.Repo.WorkDir, ".tmp")
|
||||
|
||||
|
@ -85,7 +93,7 @@ func (p *Plugin) Validate() error {
|
|||
return ErrPagesActionNotExclusive
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%w: %s", ErrActionUnknown, action)
|
||||
return fmt.Errorf("%w: %s", ErrActionUnknown, actionStr)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,10 +102,8 @@ func (p *Plugin) Validate() error {
|
|||
|
||||
// Execute provides the implementation of the plugin.
|
||||
func (p *Plugin) Execute() error {
|
||||
var err error
|
||||
|
||||
homeDir := getUserHomeDir()
|
||||
batchCmd := make([]*types.Cmd, 0)
|
||||
|
||||
gitEnv := []string{
|
||||
"GIT_AUTHOR_NAME",
|
||||
"GIT_AUTHOR_EMAIL",
|
||||
|
@ -109,38 +115,41 @@ func (p *Plugin) Execute() error {
|
|||
|
||||
for _, env := range gitEnv {
|
||||
if err := os.Unsetenv(env); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to unset git env vars '%s': %w", env, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Setenv("GIT_TERMINAL_PROMPT", "0"); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to git env var': %w", err)
|
||||
}
|
||||
|
||||
// Write SSH key and netrc file.
|
||||
if p.Settings.SSHKey != "" {
|
||||
if err := git.WriteSSHKey(p.Settings.SSHKey); err != nil {
|
||||
if err := git.WriteSSHKey(homeDir, p.Settings.SSHKey); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := git.WriteNetrc(p.Settings.Netrc.Machine, p.Settings.Netrc.Login, p.Settings.Netrc.Password); err != nil {
|
||||
netrc := p.Settings.Netrc
|
||||
if err := git.WriteNetrc(homeDir, netrc.Machine, netrc.Login, netrc.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle repo initialization.
|
||||
if err := os.MkdirAll(p.Settings.Repo.WorkDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to create working directory: %w", err)
|
||||
}
|
||||
|
||||
p.Settings.Repo.IsEmpty, err = file.IsDirEmpty(p.Settings.Repo.WorkDir)
|
||||
isEmpty, err := file.IsDirEmpty(p.Settings.Repo.WorkDir)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to check working directory: %w", err)
|
||||
}
|
||||
|
||||
p.Settings.Repo.IsEmpty = isEmpty
|
||||
|
||||
isDir, err := file.IsDir(filepath.Join(p.Settings.Repo.WorkDir, ".git"))
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to check working directory: %w", err)
|
||||
}
|
||||
|
||||
if !isDir {
|
||||
|
@ -153,20 +162,21 @@ func (p *Plugin) Execute() error {
|
|||
batchCmd = append(batchCmd, git.ConfigUserEmail(p.Settings.Repo))
|
||||
batchCmd = append(batchCmd, git.ConfigSSLVerify(p.Settings.Repo, p.Network.InsecureSkipVerify))
|
||||
|
||||
for _, action := range p.Settings.Action.Value() {
|
||||
for _, actionStr := range p.Settings.Action.Value() {
|
||||
action := Action(actionStr)
|
||||
switch action {
|
||||
case "clone":
|
||||
case ActionClone:
|
||||
cmds, err := p.handleClone()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
batchCmd = append(batchCmd, cmds...)
|
||||
case "commit":
|
||||
case ActionCommit:
|
||||
batchCmd = append(batchCmd, p.handleCommit()...)
|
||||
case "push":
|
||||
case ActionPush:
|
||||
batchCmd = append(batchCmd, p.handlePush()...)
|
||||
case "pages":
|
||||
case ActionPages:
|
||||
cmds, err := p.handlePages()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -185,7 +195,8 @@ func (p *Plugin) Execute() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// HandleClone clones remote.
|
||||
// handleClone clones the remote repository into the configured working directory.
|
||||
// If the working directory is not empty, it returns an error.
|
||||
func (p *Plugin) handleClone() ([]*types.Cmd, error) {
|
||||
var cmds []*types.Cmd
|
||||
|
||||
|
@ -209,7 +220,7 @@ func (p *Plugin) handleCommit() []*types.Cmd {
|
|||
|
||||
cmds = append(cmds, git.Add(p.Settings.Repo))
|
||||
|
||||
if err := git.TestCleanTree(p.Settings.Repo).Run(); err != nil {
|
||||
if err := git.IsCleanTree(p.Settings.Repo).Run(); err != nil {
|
||||
cmds = append(cmds, git.Commit(p.Settings.Repo))
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@ type Pages struct {
|
|||
Delete bool
|
||||
}
|
||||
|
||||
type Action string
|
||||
|
||||
func New(e wp.ExecuteFunc, build ...string) *Plugin {
|
||||
p := &Plugin{
|
||||
Settings: &Settings{},
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSyncDirectories(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
exclude []string
|
||||
del bool
|
||||
src string
|
||||
dest string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "exclude .git and other patterns",
|
||||
exclude: []string{"*.log", "temp/"},
|
||||
del: false,
|
||||
src: "/path/to/src",
|
||||
dest: "/path/to/dest",
|
||||
want: []string{
|
||||
"rsync", "-r", "--exclude", ".git", "--exclude", "*.log",
|
||||
"--exclude", "temp/", ".", "/path/to/dest",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delete enabled",
|
||||
exclude: []string{},
|
||||
del: true,
|
||||
src: "/path/to/src",
|
||||
dest: "/path/to/dest",
|
||||
want: []string{"rsync", "-r", "--exclude", ".git", "--delete", ".", "/path/to/dest"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := SyncDirectories(tt.exclude, tt.del, tt.src, tt.dest)
|
||||
require.Equal(t, tt.want, cmd.Cmd.Args)
|
||||
require.Equal(t, tt.src, cmd.Cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"os/user"
|
||||
)
|
||||
|
||||
func getUserHomeDir() string {
|
||||
home := "/root"
|
||||
|
||||
if currentUser, err := user.Current(); err == nil {
|
||||
home = currentUser.HomeDir
|
||||
}
|
||||
|
||||
return home
|
||||
}
|
Loading…
Reference in New Issue