0
0
mirror of https://github.com/thegeeklab/wp-git-action.git synced 2024-06-02 18:29:41 +02:00

refactor: rework batch command execution

This commit is contained in:
Robert Kaussow 2024-05-05 17:02:10 +02:00
parent 92a7c252d2
commit 72a0772ab1
Signed by: xoxys
GPG Key ID: 4E692A2EAECC03C0
21 changed files with 473 additions and 551 deletions

View File

@ -19,7 +19,6 @@ GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@$(G
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GOTESTSUM_PACKAGE ?= gotest.tools/gotestsum@latest
GENERATE ?=
XGO_VERSION := go-1.22.x
XGO_TARGETS ?= linux/amd64,linux/arm-6,linux/arm-7,linux/arm64
@ -65,12 +64,7 @@ lint: golangci-lint
.PHONY: generate
generate:
$(GO) generate $(GENERATE)
.PHONY: generate-docs
generate-docs:
$(GO) generate ./cmd/$(EXECUTABLE)/flags.go
$(GO) generate $(PACKAGES)
.PHONY: test
test:

View File

@ -1,58 +0,0 @@
//go:build generate
// +build generate
package main
import (
"bytes"
"embed"
"fmt"
"os"
"text/template"
"github.com/thegeeklab/wp-git-action/plugin"
"github.com/thegeeklab/wp-plugin-go/docs"
wp "github.com/thegeeklab/wp-plugin-go/plugin"
wp_template "github.com/thegeeklab/wp-plugin-go/template"
"github.com/urfave/cli/v2"
)
//go:embed templates/docs-data.yaml.tmpl
var yamlTemplate embed.FS
func main() {
settings := &plugin.Settings{}
app := &cli.App{
Flags: settingsFlags(settings, wp.FlagsPluginCategory),
}
out, err := toYAML(app)
if err != nil {
panic(err)
}
fi, err := os.Create("../../docs/data/data-raw.yaml")
if err != nil {
panic(err)
}
defer fi.Close()
if _, err := fi.WriteString(out); err != nil {
panic(err)
}
}
func toYAML(app *cli.App) (string, error) {
var w bytes.Buffer
yamlTmpl, err := template.New("docs").Funcs(wp_template.LoadFuncMap()).ParseFS(yamlTemplate, "templates/docs-data.yaml.tmpl")
if err != nil {
fmt.Println(yamlTmpl)
return "", err
}
if err := yamlTmpl.ExecuteTemplate(&w, "docs-data.yaml.tmpl", docs.GetTemplateData(app)); err != nil {
return "", err
}
return w.String(), nil
}

View File

@ -1,163 +0,0 @@
package main
import (
"github.com/thegeeklab/wp-git-action/plugin"
"github.com/urfave/cli/v2"
)
// settingsFlags has the cli.Flags for the plugin.Settings.
//
//go:generate go run docs.go flags.go
func settingsFlags(settings *plugin.Settings, category string) []cli.Flag {
return []cli.Flag{
&cli.StringSliceFlag{
Name: "action",
Usage: "git action to execute",
EnvVars: []string{"PLUGIN_ACTION"},
Destination: &settings.Action,
Required: true,
Category: category,
},
&cli.StringFlag{
Name: "author-name",
Usage: "git author name",
EnvVars: []string{"PLUGIN_AUTHOR_NAME", "CI_COMMIT_AUTHOR"},
Destination: &settings.Repo.Author.Name,
Required: true,
Category: category,
},
&cli.StringFlag{
Name: "author-email",
Usage: "git author email",
EnvVars: []string{"PLUGIN_AUTHOR_EMAIL", "CI_COMMIT_AUTHOR_EMAIL"},
Destination: &settings.Repo.Author.Email,
Required: true,
Category: category,
},
&cli.StringFlag{
Name: "netrc.machine",
Usage: "netrc remote machine name",
EnvVars: []string{"PLUGIN_NETRC_MACHINE", "CI_NETRC_MACHINE"},
Destination: &settings.Netrc.Machine,
Value: "github.com",
Category: category,
},
&cli.StringFlag{
Name: "netrc.username",
Usage: "netrc login user on the remote machine",
EnvVars: []string{"PLUGIN_NETRC_USERNAME", "CI_NETRC_USERNAME"},
Destination: &settings.Netrc.Login,
Value: "token",
Category: category,
},
&cli.StringFlag{
Name: "netrc.password",
Usage: "netrc login password on the remote machine",
EnvVars: []string{"PLUGIN_NETRC_PASSWORD", "CI_NETRC_PASSWORD"},
Destination: &settings.Netrc.Password,
Category: category,
},
&cli.StringFlag{
Name: "ssh-key",
Usage: "ssh private key for the remote repository",
EnvVars: []string{"PLUGIN_SSH_KEY"},
Destination: &settings.SSHKey,
Category: category,
},
&cli.StringFlag{
Name: "remote-url",
Usage: "url of the remote repository",
EnvVars: []string{"PLUGIN_REMOTE_URL", "CI_REPO_CLONE_URL"},
Destination: &settings.Repo.RemoteURL,
Category: category,
},
&cli.StringFlag{
Name: "branch",
Usage: "name of the git source branch",
EnvVars: []string{"PLUGIN_BRANCH"},
Destination: &settings.Repo.Branch,
Value: "main",
Category: category,
},
&cli.StringFlag{
Name: "path",
Usage: "path to clone git repository",
EnvVars: []string{"PLUGIN_PATH"},
Destination: &settings.Repo.WorkDir,
Category: category,
},
&cli.StringFlag{
Name: "commit-message",
Usage: "commit message",
EnvVars: []string{"PLUGIN_MESSAGE"},
Destination: &settings.Repo.CommitMsg,
Value: "[skip ci] commit dirty state",
Category: category,
},
&cli.BoolFlag{
Name: "force-push",
Usage: "enable force push to remote repository",
EnvVars: []string{"PLUGIN_FORCE"},
Destination: &settings.Repo.ForcePush,
Value: false,
Category: category,
},
&cli.BoolFlag{
Name: "followtags",
Usage: "follow tags for pushes to remote repository",
EnvVars: []string{"PLUGIN_FOLLOWTAGS"},
Destination: &settings.Repo.PushFollowTags,
Value: false,
Category: category,
},
&cli.BoolFlag{
Name: "insecure-skip-ssl-verify",
Usage: "skip ssl verification of the remote machine",
EnvVars: []string{"PLUGIN_INSECURE_SKIP_SSL_VERIFY"},
Destination: &settings.Repo.InsecureSkipSSLVerify,
Value: false,
Category: category,
},
&cli.BoolFlag{
Name: "empty-commit",
Usage: "allow empty commits",
EnvVars: []string{"PLUGIN_EMPTY_COMMIT"},
Destination: &settings.Repo.EmptyCommit,
Value: false,
Category: category,
},
&cli.BoolFlag{
Name: "no-verify",
Usage: "bypass the pre-commit and commit-msg hooks",
EnvVars: []string{"PLUGIN_NO_VERIFY"},
Destination: &settings.Repo.NoVerify,
Value: false,
Category: category,
},
&cli.StringFlag{
Name: "pages.directory",
Usage: "source directory to be synchronized with the pages banch",
EnvVars: []string{"PLUGIN_PAGES_DIRECTORY"},
Destination: &settings.Pages.Directory,
Value: "docs/",
Category: category,
},
&cli.StringSliceFlag{
Name: "pages.exclude",
Usage: "files or directories to exclude from the pages rsync command",
EnvVars: []string{"PLUGIN_PAGES_EXCLUDE"},
Destination: &settings.Pages.Exclude,
Category: category,
},
&cli.BoolFlag{
Name: "pages.delete",
Usage: "add delete flag to pages rsync command",
EnvVars: []string{"PLUGIN_PAGES_DELETE"},
Destination: &settings.Pages.Delete,
Value: true,
Category: category,
},
}
}

View File

@ -1,10 +1,7 @@
package main
import (
"fmt"
"github.com/thegeeklab/wp-git-action/plugin"
wp "github.com/thegeeklab/wp-plugin-go/plugin"
)
//nolint:gochecknoglobals
@ -14,14 +11,5 @@ var (
)
func main() {
settings := &plugin.Settings{}
options := wp.Options{
Name: "wp-git-action",
Description: "Perform git actions.",
Version: BuildVersion,
VersionMetadata: fmt.Sprintf("date=%s", BuildDate),
Flags: settingsFlags(settings, wp.FlagsPluginCategory),
}
plugin.New(options, settings).Run()
plugin.New(nil, BuildVersion, BuildDate).Run()
}

View File

@ -1,18 +0,0 @@
---
{{- if .GlobalArgs }}
properties:
{{- range $v := .GlobalArgs }}
- name: {{ $v.Name }}
{{- with $v.Description }}
description: |
{{ . | ToSentence }}
{{- end }}
{{- with $v.Type }}
type: {{ . }}
{{- end }}
{{- with $v.Default }}
defaultValue: {{ . }}
{{- end }}
required: {{ default false $v.Required }}
{{ end -}}
{{ end -}}

View File

@ -60,15 +60,22 @@ properties:
defaultValue: false
required: false
- name: insecure_skip_ssl_verify
- name: insecure_skip_verify
description: |
Skip ssl verification of the remote machine.
Skip SSL verification.
Activating this option is insecure and should be avoided in most cases.
type: bool
defaultValue: false
required: false
- name: log_level
description: |
Plugin log level.
type: string
defaultValue: "info"
required: false
- name: message
description: |
Commit message.

View File

@ -2,43 +2,39 @@ package git
import (
"fmt"
"os"
"github.com/thegeeklab/wp-plugin-go/v2/types"
"golang.org/x/sys/execabs"
)
// FetchSource fetches the source from remote.
func FetchSource(repo Repository) *execabs.Cmd {
func FetchSource(repo Repository) *types.Cmd {
args := []string{
"fetch",
"origin",
fmt.Sprintf("+%s:", repo.Branch),
}
cmd := execabs.Command(
gitBin,
args...,
)
cmd := execabs.Command(gitBin, args...)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
return &types.Cmd{
Cmd: cmd,
}
}
// CheckoutHead handles branch checkout.
func CheckoutHead(repo Repository) *execabs.Cmd {
func CheckoutHead(repo Repository) *types.Cmd {
args := []string{
"checkout",
"-qf",
repo.Branch,
}
cmd := execabs.Command(
gitBin,
args...,
)
cmd := execabs.Command(gitBin, args...)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
return &types.Cmd{
Cmd: cmd,
}
}

View File

@ -1,13 +1,12 @@
package git
import (
"os"
"github.com/thegeeklab/wp-plugin-go/v2/types"
"golang.org/x/sys/execabs"
)
// ForceAdd forces the addition of all dirty files.
func ForceAdd(repo Repository) *execabs.Cmd {
func ForceAdd(repo Repository) *types.Cmd {
cmd := execabs.Command(
gitBin,
"add",
@ -15,19 +14,19 @@ func ForceAdd(repo Repository) *execabs.Cmd {
"--force",
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
return &types.Cmd{
Cmd: cmd,
}
}
// Add updates the index to match the working tree.
func Add(repo Repository) *execabs.Cmd {
func Add(repo Repository) *types.Cmd {
cmd := execabs.Command(
gitBin,
"add",
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
if repo.Add != "" {
cmd.Args = append(cmd.Args, repo.Add)
@ -35,11 +34,13 @@ func Add(repo Repository) *execabs.Cmd {
cmd.Args = append(cmd.Args, "--all")
}
return cmd
return &types.Cmd{
Cmd: cmd,
}
}
// TestCleanTree returns non-zero if diff between index and local repository.
func TestCleanTree(repo Repository) *execabs.Cmd {
func TestCleanTree(repo Repository) *types.Cmd {
cmd := execabs.Command(
gitBin,
"diff-index",
@ -48,13 +49,14 @@ func TestCleanTree(repo Repository) *execabs.Cmd {
"--ignore-submodules",
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
return &types.Cmd{
Cmd: cmd,
}
}
// EmptyCommit simply create an empty commit.
func EmptyCommit(repo Repository) *execabs.Cmd {
func EmptyCommit(repo Repository) *types.Cmd {
args := []string{
"commit",
"--allow-empty",
@ -67,17 +69,17 @@ func EmptyCommit(repo Repository) *execabs.Cmd {
args...,
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
if repo.NoVerify {
cmd.Args = append(cmd.Args, "--no-verify")
}
return cmd
return &types.Cmd{
Cmd: cmd,
}
}
// ForceCommit commits every change while skipping CI.
func ForceCommit(repo Repository) *execabs.Cmd {
func Commit(repo Repository) *types.Cmd {
args := []string{
"commit",
"-m",
@ -89,11 +91,12 @@ func ForceCommit(repo Repository) *execabs.Cmd {
args...,
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
if repo.NoVerify {
cmd.Args = append(cmd.Args, "--no-verify")
}
return cmd
return &types.Cmd{
Cmd: cmd,
}
}

View File

@ -1,14 +1,14 @@
package git
import (
"os"
"strconv"
"github.com/thegeeklab/wp-plugin-go/v2/types"
"golang.org/x/sys/execabs"
)
// repoUserEmail sets the global git author email.
func ConfigAutocorrect(repo Repository) *execabs.Cmd {
func ConfigAutocorrect(repo Repository) *types.Cmd {
args := []string{
"config",
"--local",
@ -16,18 +16,16 @@ func ConfigAutocorrect(repo Repository) *execabs.Cmd {
repo.Autocorrect,
}
cmd := execabs.Command(
gitBin,
args...,
)
cmd := execabs.Command(gitBin, args...)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
return &types.Cmd{
Cmd: cmd,
}
}
// repoUserEmail sets the global git author email.
func ConfigUserEmail(repo Repository) *execabs.Cmd {
func ConfigUserEmail(repo Repository) *types.Cmd {
args := []string{
"config",
"--local",
@ -35,18 +33,16 @@ func ConfigUserEmail(repo Repository) *execabs.Cmd {
repo.Author.Email,
}
cmd := execabs.Command(
gitBin,
args...,
)
cmd := execabs.Command(gitBin, args...)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
return &types.Cmd{
Cmd: cmd,
}
}
// repoUserName sets the global git author name.
func ConfigUserName(repo Repository) *execabs.Cmd {
func ConfigUserName(repo Repository) *types.Cmd {
args := []string{
"config",
"--local",
@ -54,31 +50,27 @@ func ConfigUserName(repo Repository) *execabs.Cmd {
repo.Author.Name,
}
cmd := execabs.Command(
gitBin,
args...,
)
cmd := execabs.Command(gitBin, args...)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
return &types.Cmd{
Cmd: cmd,
}
}
// ConfigSSLVerify disables globally the git ssl verification.
func ConfigSSLVerify(repo Repository) *execabs.Cmd {
func ConfigSSLVerify(repo Repository, skipVerify bool) *types.Cmd {
args := []string{
"config",
"--local",
"http.sslVerify",
strconv.FormatBool(!repo.InsecureSkipSSLVerify),
strconv.FormatBool(!skipVerify),
}
cmd := execabs.Command(
gitBin,
args...,
)
cmd := execabs.Command(gitBin, args...)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
return &types.Cmd{
Cmd: cmd,
}
}

View File

@ -1,19 +1,19 @@
package git
import (
"os"
"github.com/thegeeklab/wp-plugin-go/v2/types"
"golang.org/x/sys/execabs"
)
// RemoteRemove drops the defined remote from a git repo.
func Init(repo Repository) *execabs.Cmd {
// Init creates a new Git repository in the given Repository's WorkDir.
func Init(repo Repository) *types.Cmd {
cmd := execabs.Command(
gitBin,
"init",
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
return &types.Cmd{
Cmd: cmd,
}
}

View File

@ -2,13 +2,13 @@ package git
import (
"fmt"
"os"
"github.com/thegeeklab/wp-plugin-go/v2/types"
"golang.org/x/sys/execabs"
)
// RemoteRemove drops the defined remote from a git repo.
func RemoteRemove(repo Repository) *execabs.Cmd {
func RemoteRemove(repo Repository) *types.Cmd {
args := []string{
"remote",
"rm",
@ -20,13 +20,14 @@ func RemoteRemove(repo Repository) *execabs.Cmd {
args...,
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
return &types.Cmd{
Cmd: cmd,
}
}
// RemoteAdd adds an additional remote to a git repo.
func RemoteAdd(repo Repository) *execabs.Cmd {
func RemoteAdd(repo Repository) *types.Cmd {
args := []string{
"remote",
"add",
@ -39,38 +40,32 @@ func RemoteAdd(repo Repository) *execabs.Cmd {
args...,
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
return &types.Cmd{
Cmd: cmd,
}
}
// RemotePush pushs the changes from the local head to a remote branch.
func RemotePush(repo Repository) *execabs.Cmd {
func RemotePush(repo Repository) *types.Cmd {
args := []string{
"push",
repo.RemoteName,
fmt.Sprintf("HEAD:%s", repo.Branch),
}
cmd := execabs.Command(
gitBin,
args...,
)
cmd := execabs.Command(gitBin, args...)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
if repo.ForcePush {
cmd.Args = append(
cmd.Args,
"--force",
)
cmd.Args = append(cmd.Args, "--force")
}
if repo.PushFollowTags {
cmd.Args = append(
cmd.Args,
"--follow-tags")
cmd.Args = append(cmd.Args, "--follow-tags")
}
return cmd
return &types.Cmd{
Cmd: cmd,
}
}

View File

@ -1,14 +1,15 @@
package git
import (
"bytes"
"os"
"github.com/rs/zerolog/log"
"github.com/thegeeklab/wp-plugin-go/v2/types"
"golang.org/x/sys/execabs"
)
func Status(repo Repository) *execabs.Cmd {
// Status returns a command that runs `git status --porcelain` in the given repository's working directory.
func Status(repo Repository) *types.Cmd {
cmd := execabs.Command(
gitBin,
"status",
@ -17,25 +18,25 @@ func Status(repo Repository) *execabs.Cmd {
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
return &types.Cmd{
Cmd: 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,
// indicating that there are uncommitted changes in the repository.
func IsDirty(repo Repository) bool {
res := bytes.NewBufferString("")
cmd := Status(repo)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
cmd.Stdout = res
cmd.Stderr = res
err := runCommand(cmd)
out, err := cmd.CombinedOutput()
if err != nil {
return false
}
if res.Len() > 0 {
log.Debug().Msg(res.String())
if len(out) > 0 {
log.Debug().Msg(string(out))
return true
}

View File

@ -15,14 +15,13 @@ type Repository struct {
Add string
CommitMsg string
Autocorrect string
NoVerify bool
InsecureSkipSSLVerify bool
EmptyCommit bool
PushFollowTags bool
ForcePush bool
WorkDir string
InitExists bool
Autocorrect string
NoVerify bool
EmptyCommit bool
PushFollowTags bool
ForcePush bool
WorkDir string
IsEmpty bool
Author Author
}

View File

@ -5,9 +5,6 @@ import (
"os"
"os/user"
"path/filepath"
"github.com/thegeeklab/wp-plugin-go/trace"
"golang.org/x/sys/execabs"
)
const (
@ -87,17 +84,3 @@ func WriteNetrc(machine, login, password string) error {
strictFilePerm,
)
}
func runCommand(cmd *execabs.Cmd) error {
if cmd.Stdout == nil {
cmd.Stdout = os.Stdout
}
if cmd.Stderr == nil {
cmd.Stderr = os.Stderr
}
trace.Cmd(cmd)
return cmd.Run()
}

4
go.mod
View File

@ -4,9 +4,9 @@ go 1.22
require (
github.com/rs/zerolog v1.32.0
github.com/thegeeklab/wp-plugin-go v1.7.1
github.com/thegeeklab/wp-plugin-go/v2 v2.1.1-0.20240505145303-d2c3493d683f
github.com/urfave/cli/v2 v2.27.2
golang.org/x/sys v0.19.0
golang.org/x/sys v0.20.0
)
require (

10
go.sum
View File

@ -46,8 +46,10 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/thegeeklab/wp-plugin-go v1.7.1 h1:zfR/rfNPuyVhXJu1fsLfp4+Mz2pTf6WwW/mIqw9750I=
github.com/thegeeklab/wp-plugin-go v1.7.1/go.mod h1:Ixi5plt9tpFGTu6yc/Inm5DcDpp3xPTeohfr86gf2EU=
github.com/thegeeklab/wp-plugin-go/v2 v2.1.1-0.20240505143321-211b38e9086e h1:OcyNTNNECX6ibTLwRKFSmXmxH/gaY593BI+Eb6dTQgc=
github.com/thegeeklab/wp-plugin-go/v2 v2.1.1-0.20240505143321-211b38e9086e/go.mod h1:I/3M/4OPvr4FFS+s0aaImpX1llA/lS2KC6Bnp+qzsCs=
github.com/thegeeklab/wp-plugin-go/v2 v2.1.1-0.20240505145303-d2c3493d683f h1:DNFIuM8q3+x5MvXEEZNnR1tHXbJBtoTYuJ0ljmNRU9s=
github.com/thegeeklab/wp-plugin-go/v2 v2.1.1-0.20240505145303-d2c3493d683f/go.mod h1:I/3M/4OPvr4FFS+s0aaImpX1llA/lS2KC6Bnp+qzsCs=
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
@ -76,8 +78,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=

42
internal/doc/main.go Normal file
View File

@ -0,0 +1,42 @@
//go:build generate
// +build generate
package main
import (
"context"
"flag"
"net/http"
"os"
"time"
"github.com/thegeeklab/wp-git-action/plugin"
"github.com/thegeeklab/wp-plugin-go/v2/docs"
wp_template "github.com/thegeeklab/wp-plugin-go/v2/template"
)
func main() {
tmpl := "https://raw.githubusercontent.com/thegeeklab/woodpecker-plugins/main/templates/docs-data.yaml.tmpl"
client := http.Client{
Timeout: 30 * time.Second,
}
p := plugin.New(nil)
out, err := wp_template.Render(context.Background(), client, tmpl, docs.GetTemplateData(p.App))
if err != nil {
panic(err)
}
outputFile := flag.String("output", "", "Output file path")
flag.Parse()
if *outputFile == "" {
panic("no output file specified")
}
err = os.WriteFile(*outputFile, []byte(out), 0o644)
if err != nil {
panic(err)
}
}

View File

@ -8,6 +8,8 @@ import (
"path/filepath"
"github.com/thegeeklab/wp-git-action/git"
"github.com/thegeeklab/wp-plugin-go/v2/file"
"github.com/thegeeklab/wp-plugin-go/v2/types"
)
var (
@ -92,6 +94,10 @@ func (p *Plugin) Validate() error {
// Execute provides the implementation of the plugin.
func (p *Plugin) Execute() error {
var err error
batchCmd := make([]*types.Cmd, 0)
gitEnv := []string{
"GIT_AUTHOR_NAME",
"GIT_AUTHOR_EMAIL",
@ -111,26 +117,7 @@ func (p *Plugin) Execute() error {
return err
}
if err := p.handleInit(); err != nil {
return err
}
if err := git.ConfigAutocorrect(p.Settings.Repo).Run(); err != nil {
return err
}
if err := git.ConfigUserName(p.Settings.Repo).Run(); err != nil {
return err
}
if err := git.ConfigUserEmail(p.Settings.Repo).Run(); err != nil {
return err
}
if err := git.ConfigSSLVerify(p.Settings.Repo).Run(); err != nil {
return err
}
// Write SSH key and netrc file.
if p.Settings.SSHKey != "" {
if err := git.WriteSSHKey(p.Settings.SSHKey); err != nil {
return err
@ -141,80 +128,56 @@ func (p *Plugin) Execute() error {
return err
}
for _, action := range p.Settings.Action.Value() {
switch action {
case "clone":
if err := p.handleClone(); err != nil {
return err
}
case "commit":
if err := p.handleCommit(); err != nil {
return err
}
case "push":
if err := p.handlePush(); err != nil {
return err
}
case "pages":
if err := p.handlePages(); err != nil {
return err
}
}
}
return nil
}
// handleInit initializes the repository.
func (p *Plugin) handleInit() error {
path := filepath.Join(p.Settings.Repo.WorkDir, ".git")
// Handle repo initialization.
if err := os.MkdirAll(p.Settings.Repo.WorkDir, os.ModePerm); err != nil {
return err
}
if _, err := os.Stat(path); !os.IsNotExist(err) {
p.Settings.Repo.InitExists = true
return nil
}
return execute(git.Init(p.Settings.Repo))
}
// HandleClone clones remote.
func (p *Plugin) handleClone() error {
if p.Settings.Repo.InitExists {
return fmt.Errorf("%w: %s exists and not empty", ErrGitCloneDestintionNotValid, p.Settings.Repo.WorkDir)
}
if p.Settings.Repo.RemoteURL != "" {
if err := execute(git.RemoteAdd(p.Settings.Repo)); err != nil {
return err
}
}
if err := execute(git.FetchSource(p.Settings.Repo)); err != nil {
p.Settings.Repo.IsEmpty, err = file.IsDirEmpty(p.Settings.Repo.WorkDir)
if err != nil {
return err
}
return execute(git.CheckoutHead(p.Settings.Repo))
}
// HandleCommit commits changes locally.
func (p *Plugin) handleCommit() error {
if err := execute(git.Add(p.Settings.Repo)); err != nil {
isDir, err := file.IsDir(filepath.Join(p.Settings.Repo.WorkDir, ".git"))
if err != nil {
return err
}
if err := execute(git.TestCleanTree(p.Settings.Repo)); err != nil {
if err := execute(git.ForceCommit(p.Settings.Repo)); err != nil {
return err
if !isDir {
batchCmd = append(batchCmd, git.Init(p.Settings.Repo))
}
// Handle repo configuration.
batchCmd = append(batchCmd, git.ConfigAutocorrect(p.Settings.Repo))
batchCmd = append(batchCmd, git.ConfigUserName(p.Settings.Repo))
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() {
switch action {
case "clone":
cmds, err := p.handleClone()
if err != nil {
return err
}
batchCmd = append(batchCmd, cmds...)
case "commit":
batchCmd = append(batchCmd, p.handleCommit()...)
case "push":
batchCmd = append(batchCmd, p.handlePush()...)
case "pages":
cmds, err := p.handlePages()
if err != nil {
return err
}
batchCmd = append(batchCmd, cmds...)
}
}
if p.Settings.Repo.EmptyCommit {
if err := execute(git.EmptyCommit(p.Settings.Repo)); err != nil {
for _, cmd := range batchCmd {
if err := cmd.Run(); err != nil {
return err
}
}
@ -222,28 +185,69 @@ func (p *Plugin) handleCommit() error {
return nil
}
// HandleClone clones remote.
func (p *Plugin) handleClone() ([]*types.Cmd, error) {
var cmds []*types.Cmd
if !p.Settings.Repo.IsEmpty {
return cmds, fmt.Errorf("%w: %s exists and not empty", ErrGitCloneDestintionNotValid, p.Settings.Repo.WorkDir)
}
if p.Settings.Repo.RemoteURL != "" {
cmds = append(cmds, git.RemoteAdd(p.Settings.Repo))
}
cmds = append(cmds, git.FetchSource(p.Settings.Repo))
cmds = append(cmds, git.CheckoutHead(p.Settings.Repo))
return cmds, nil
}
// HandleCommit commits changes locally.
func (p *Plugin) handleCommit() []*types.Cmd {
var cmds []*types.Cmd
cmds = append(cmds, git.Add(p.Settings.Repo))
if err := git.TestCleanTree(p.Settings.Repo).Run(); err != nil {
cmds = append(cmds, git.Commit(p.Settings.Repo))
}
if p.Settings.Repo.EmptyCommit {
cmds = append(cmds, git.EmptyCommit(p.Settings.Repo))
}
return cmds
}
// HandlePush pushs changes to remote.
func (p *Plugin) handlePush() error {
return execute(git.RemotePush(p.Settings.Repo))
func (p *Plugin) handlePush() []*types.Cmd {
return []*types.Cmd{git.RemotePush(p.Settings.Repo)}
}
// HandlePages syncs, commits and pushes the changes from the pages directory to the pages branch.
func (p *Plugin) handlePages() error {
func (p *Plugin) handlePages() ([]*types.Cmd, error) {
var cmds []*types.Cmd
defer os.RemoveAll(p.Settings.Repo.WorkDir)
if err := p.handleClone(); err != nil {
return err
ccmd, err := p.handleClone()
if err != nil {
return cmds, err
}
if err := execute(
rsyncDirectories(p.Settings.Pages, p.Settings.Repo),
); err != nil {
return err
}
cmds = append(cmds, ccmd...)
cmds = append(cmds,
SyncDirectories(
p.Settings.Pages.Exclude.Value(),
p.Settings.Pages.Delete,
p.Settings.Pages.Directory,
p.Settings.Repo.WorkDir,
),
)
if err := p.handleCommit(); err != nil {
return err
}
cmds = append(cmds, p.handleCommit()...)
cmds = append(cmds, p.handlePush()...)
return p.handlePush()
return cmds, nil
}

View File

@ -1,11 +1,15 @@
package plugin
import (
"fmt"
"github.com/thegeeklab/wp-git-action/git"
wp "github.com/thegeeklab/wp-plugin-go/plugin"
wp "github.com/thegeeklab/wp-plugin-go/v2/plugin"
"github.com/urfave/cli/v2"
)
//go:generate go run ../internal/doc/main.go -output=../docs/data/data-raw.yaml
// Plugin implements provide the plugin.
type Plugin struct {
*wp.Plugin
@ -34,13 +38,179 @@ type Pages struct {
Delete bool
}
func New(options wp.Options, settings *Settings) *Plugin {
p := &Plugin{}
func New(e wp.ExecuteFunc, build ...string) *Plugin {
p := &Plugin{
Settings: &Settings{},
}
options.Execute = p.run
options := wp.Options{
Name: "wp-git-action",
Description: "Perform git actions",
Flags: Flags(p.Settings, wp.FlagsPluginCategory),
Execute: p.run,
HideWoodpeckerFlags: true,
}
if len(build) > 0 {
options.Version = build[0]
}
if len(build) > 1 {
options.VersionMetadata = fmt.Sprintf("date=%s", build[1])
}
if e != nil {
options.Execute = e
}
p.Plugin = wp.New(options)
p.Settings = settings
return p
}
// Flags returns a slice of CLI flags for the plugin.
func Flags(settings *Settings, category string) []cli.Flag {
return []cli.Flag{
&cli.StringSliceFlag{
Name: "action",
Usage: "git action to execute",
EnvVars: []string{"PLUGIN_ACTION"},
Destination: &settings.Action,
Required: true,
Category: category,
},
&cli.StringFlag{
Name: "author-name",
Usage: "git author name",
EnvVars: []string{"PLUGIN_AUTHOR_NAME", "CI_COMMIT_AUTHOR"},
Destination: &settings.Repo.Author.Name,
Required: true,
Category: category,
},
&cli.StringFlag{
Name: "author-email",
Usage: "git author email",
EnvVars: []string{"PLUGIN_AUTHOR_EMAIL", "CI_COMMIT_AUTHOR_EMAIL"},
Destination: &settings.Repo.Author.Email,
Required: true,
Category: category,
},
&cli.StringFlag{
Name: "netrc.machine",
Usage: "netrc remote machine name",
EnvVars: []string{"PLUGIN_NETRC_MACHINE", "CI_NETRC_MACHINE"},
Destination: &settings.Netrc.Machine,
Value: "github.com",
Category: category,
},
&cli.StringFlag{
Name: "netrc.username",
Usage: "netrc login user on the remote machine",
EnvVars: []string{"PLUGIN_NETRC_USERNAME", "CI_NETRC_USERNAME"},
Destination: &settings.Netrc.Login,
Value: "token",
Category: category,
},
&cli.StringFlag{
Name: "netrc.password",
Usage: "netrc login password on the remote machine",
EnvVars: []string{"PLUGIN_NETRC_PASSWORD", "CI_NETRC_PASSWORD"},
Destination: &settings.Netrc.Password,
Category: category,
},
&cli.StringFlag{
Name: "ssh-key",
Usage: "ssh private key for the remote repository",
EnvVars: []string{"PLUGIN_SSH_KEY"},
Destination: &settings.SSHKey,
Category: category,
},
&cli.StringFlag{
Name: "remote-url",
Usage: "url of the remote repository",
EnvVars: []string{"PLUGIN_REMOTE_URL", "CI_REPO_CLONE_URL"},
Destination: &settings.Repo.RemoteURL,
Category: category,
},
&cli.StringFlag{
Name: "branch",
Usage: "name of the git source branch",
EnvVars: []string{"PLUGIN_BRANCH"},
Destination: &settings.Repo.Branch,
Value: "main",
Category: category,
},
&cli.StringFlag{
Name: "path",
Usage: "path to clone git repository",
EnvVars: []string{"PLUGIN_PATH"},
Destination: &settings.Repo.WorkDir,
Category: category,
},
&cli.StringFlag{
Name: "commit-message",
Usage: "commit message",
EnvVars: []string{"PLUGIN_MESSAGE"},
Destination: &settings.Repo.CommitMsg,
Value: "[skip ci] commit dirty state",
Category: category,
},
&cli.BoolFlag{
Name: "force-push",
Usage: "enable force push to remote repository",
EnvVars: []string{"PLUGIN_FORCE"},
Destination: &settings.Repo.ForcePush,
Value: false,
Category: category,
},
&cli.BoolFlag{
Name: "followtags",
Usage: "follow tags for pushes to remote repository",
EnvVars: []string{"PLUGIN_FOLLOWTAGS"},
Destination: &settings.Repo.PushFollowTags,
Value: false,
Category: category,
},
&cli.BoolFlag{
Name: "empty-commit",
Usage: "allow empty commits",
EnvVars: []string{"PLUGIN_EMPTY_COMMIT"},
Destination: &settings.Repo.EmptyCommit,
Value: false,
Category: category,
},
&cli.BoolFlag{
Name: "no-verify",
Usage: "bypass the pre-commit and commit-msg hooks",
EnvVars: []string{"PLUGIN_NO_VERIFY"},
Destination: &settings.Repo.NoVerify,
Value: false,
Category: category,
},
&cli.StringFlag{
Name: "pages.directory",
Usage: "source directory to be synchronized with the pages banch",
EnvVars: []string{"PLUGIN_PAGES_DIRECTORY"},
Destination: &settings.Pages.Directory,
Value: "docs/",
Category: category,
},
&cli.StringSliceFlag{
Name: "pages.exclude",
Usage: "files or directories to exclude from the pages rsync command",
EnvVars: []string{"PLUGIN_PAGES_EXCLUDE"},
Destination: &settings.Pages.Exclude,
Category: category,
},
&cli.BoolFlag{
Name: "pages.delete",
Usage: "add delete flag to pages rsync command",
EnvVars: []string{"PLUGIN_PAGES_DELETE"},
Destination: &settings.Pages.Delete,
Value: true,
Category: category,
},
}
}

43
plugin/rsync.go Normal file
View File

@ -0,0 +1,43 @@
package plugin
import (
"github.com/thegeeklab/wp-plugin-go/v2/types"
"golang.org/x/sys/execabs"
)
func SyncDirectories(exclude []string, del bool, src, dest string) *types.Cmd {
args := []string{
"-r",
"--exclude",
".git",
}
for _, item := range exclude {
args = append(
args,
"--exclude",
item,
)
}
if del {
args = append(
args,
"--delete",
)
}
args = append(
args,
".",
dest,
)
cmd := execabs.Command("rsync", args...)
cmd.Dir = src
return &types.Cmd{
Cmd: cmd,
}
}

View File

@ -1,58 +0,0 @@
package plugin
import (
"os"
"strings"
"github.com/rs/zerolog/log"
"github.com/thegeeklab/wp-git-action/git"
"golang.org/x/sys/execabs"
)
// helper function to simply wrap os execte command.
func execute(cmd *execabs.Cmd) error {
log.Debug().Msgf("+ %s", strings.Join(cmd.Args, " "))
cmd.Env = os.Environ()
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
return cmd.Run()
}
func rsyncDirectories(pages Pages, repo git.Repository) *execabs.Cmd {
args := []string{
"-r",
"--exclude",
".git",
}
for _, item := range pages.Exclude.Value() {
args = append(
args,
"--exclude",
item,
)
}
if pages.Delete {
args = append(
args,
"--delete",
)
}
args = append(
args,
".",
repo.WorkDir,
)
cmd := execabs.Command(
"rsync",
args...,
)
cmd.Dir = pages.Directory
return cmd
}