0
0
mirror of https://github.com/thegeeklab/wp-git-action.git synced 2024-11-24 22:50:39 +00:00

refactor: rework plugin structure and add pages action (#7)

This commit is contained in:
Robert Kaussow 2022-12-02 22:21:35 +01:00 committed by GitHub
parent 686de4edfd
commit e8c4aad467
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 507 additions and 240 deletions

View File

@ -3,3 +3,4 @@ github
url url
gh gh
drone-git-action drone-git-action
rsync

View File

@ -24,9 +24,12 @@ kind: pipeline
name: default name: default
steps: steps:
- name: commit artifact - name: commit changelog
image: thegeeklab/drone-git-action image: thegeeklab/drone-git-action
settings: settings:
action:
- commit
- push
netrc_password: ghp_3LbMg9Kncpdkhjp3bh3dMnKNXLjVMTsXk4sM netrc_password: ghp_3LbMg9Kncpdkhjp3bh3dMnKNXLjVMTsXk4sM
author_name: octobot author_name: octobot
author_email: octobot@example.com author_email: octobot@example.com
@ -41,6 +44,30 @@ steps:
<!-- spellchecker-enable --> <!-- spellchecker-enable -->
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
### Examples
#### Publish GitHub pages
The plugin can be used to publish GitHub pages to the pages branch. Remember that the `pages` action cannot be combined with other actions.
```YAML
kind: pipeline
name: default
steps:
- name: publish
image: thegeeklab/drone-git-action
settings:
action:
- pages
author_email: bot@thegeeklab.de
author_name: thegeeklab-bot
message: "update pages"
branch: gh-pages
pages_directory: docs/
netrc_password: ghp_3LbMg9Kncpdkhjp3bh3dMnKNXLjVMTsXk4sM
```
## Build ## Build
Build the binary with the following command: Build the binary with the following command:

View File

@ -1,7 +1,14 @@
--- ---
properties: properties:
- name: actions - name: action
description: "Git actions to to execute. Supported actions: `clone|commit|push`." description: |
Git actions to be executed. Supported actions: `clone | commit | push | pages`. Specified actions are executed in the specified order
- **clone:** Clones the repository in `remote` and checks out the `branch` to `path`.
- **commit:** Adds a commit to the default drone repository or the repository in `remote`.
- **push:** Pushes all commits to the default drone repository or the repository set in `remote`.
- **pages:** The `pages` action is a special action that cannot be combined with other actions. It is intended for use for
GitHub pages. It synchronizes the contents of `pages_directory` with the target `branch` using `rsync` and pushes the changes automatically.
required: true required: true
type: list type: list
@ -43,7 +50,7 @@ properties:
type: string type: string
- name: path - name: path
description: Path to git repository. description: Path to clone the git repository.
type: string type: string
- name: message - name: message
@ -75,3 +82,17 @@ properties:
description: Bypass the pre-commit and commit-msg hooks. description: Bypass the pre-commit and commit-msg hooks.
defaultvalue: false defaultvalue: false
type: bool type: bool
- name: pages_directory
description: Source directory to be synchronized with the pages `branch`.
defaultvalue: docs/
type: string
- name: pages_exclude
description: Files or directories to exclude from the rsync command.
type: list
- name: pages_delete
description: When set to `true`, the `--delete` flag is added to the rsync command to remove files from the `branch` that do not exist in the `pages_directory` either.
defaultvalue: true
type: bool

View File

@ -9,33 +9,32 @@ import (
func settingsFlags(settings *plugin.Settings, category string) []cli.Flag { func settingsFlags(settings *plugin.Settings, category string) []cli.Flag {
return []cli.Flag{ return []cli.Flag{
&cli.StringSliceFlag{ &cli.StringSliceFlag{
Name: "actions", Name: "action",
Usage: "git actions to to execute", Usage: "git action to to execute",
EnvVars: []string{"PLUGIN_ACTIONS"}, EnvVars: []string{"PLUGIN_ACTION"},
Destination: &settings.Actions, Destination: &settings.Action,
Required: true, Required: true,
Category: category, Category: category,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "commit-author-name", Name: "author-name",
Usage: "git author name", Usage: "git author name",
EnvVars: []string{"PLUGIN_AUTHOR_NAME", "DRONE_COMMIT_AUTHOR"}, EnvVars: []string{"PLUGIN_AUTHOR_NAME", "DRONE_COMMIT_AUTHOR"},
Destination: &settings.Commit.Author.Name, Destination: &settings.Repo.Author.Name,
Required: true, Required: true,
Category: category, Category: category,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "commit-author-email", Name: "author-email",
Usage: "git author email", Usage: "git author email",
EnvVars: []string{"PLUGIN_AUTHOR_EMAIL", "DRONE_COMMIT_AUTHOR_EMAIL"}, EnvVars: []string{"PLUGIN_AUTHOR_EMAIL", "DRONE_COMMIT_AUTHOR_EMAIL"},
Destination: &settings.Commit.Author.Email, Destination: &settings.Repo.Author.Email,
Required: true, Required: true,
Category: category, Category: category,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "netrc-machine", Name: "netrc.machine",
Usage: "netrc remote machine name", Usage: "netrc remote machine name",
EnvVars: []string{"PLUGIN_NETRC_MACHINE", "DRONE_NETRC_MACHINE"}, EnvVars: []string{"PLUGIN_NETRC_MACHINE", "DRONE_NETRC_MACHINE"},
Destination: &settings.Netrc.Machine, Destination: &settings.Netrc.Machine,
@ -43,7 +42,7 @@ func settingsFlags(settings *plugin.Settings, category string) []cli.Flag {
Category: category, Category: category,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "netrc-username", Name: "netrc.username",
Usage: "netrc login user on the remote machine", Usage: "netrc login user on the remote machine",
EnvVars: []string{"PLUGIN_NETRC_USERNAME", "DRONE_NETRC_USERNAME"}, EnvVars: []string{"PLUGIN_NETRC_USERNAME", "DRONE_NETRC_USERNAME"},
Destination: &settings.Netrc.Login, Destination: &settings.Netrc.Login,
@ -51,7 +50,7 @@ func settingsFlags(settings *plugin.Settings, category string) []cli.Flag {
Category: category, Category: category,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "netrc-password", Name: "netrc.password",
Usage: "netrc login password on the remote machine", Usage: "netrc login password on the remote machine",
EnvVars: []string{"PLUGIN_NETRC_PASSWORD", "DRONE_NETRC_PASSWORD"}, EnvVars: []string{"PLUGIN_NETRC_PASSWORD", "DRONE_NETRC_PASSWORD"},
Destination: &settings.Netrc.Password, Destination: &settings.Netrc.Password,
@ -68,41 +67,38 @@ func settingsFlags(settings *plugin.Settings, category string) []cli.Flag {
&cli.StringFlag{ &cli.StringFlag{
Name: "remote", Name: "remote",
Usage: "url of the remote repository", Usage: "url of the remote repository",
EnvVars: []string{"PLUGIN_REMOTE"}, EnvVars: []string{"PLUGIN_REMOTE", "DRONE_REMOTE_URL"},
Destination: &settings.Remote, Destination: &settings.Repo.RemoteURL,
Category: category, Category: category,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "branch", Name: "branch",
Usage: "name of the git branch", Usage: "name of the git source branch",
EnvVars: []string{"PLUGIN_BRANCH"}, EnvVars: []string{"PLUGIN_BRANCH"},
Destination: &settings.Branch, Destination: &settings.Repo.Branch,
Value: "main", Value: "main",
Category: category, Category: category,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "path", Name: "path",
Usage: "path to git repository", Usage: "path to clone git repository",
EnvVars: []string{"PLUGIN_PATH"}, EnvVars: []string{"PLUGIN_PATH"},
Destination: &settings.Path, Destination: &settings.Repo.WorkDir,
Category: category, Category: category,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "message", Name: "commit-message",
Usage: "commit message", Usage: "commit message",
EnvVars: []string{"PLUGIN_MESSAGE"}, EnvVars: []string{"PLUGIN_MESSAGE"},
Destination: &settings.Message, Destination: &settings.Repo.CommitMsg,
Value: "[skip ci] commit dirty state", Value: "[skip ci] commit dirty state",
Category: category, Category: category,
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "force", Name: "force-push",
Usage: "enable force push to remote repository", Usage: "enable force push to remote repository",
EnvVars: []string{"PLUGIN_FORCE"}, EnvVars: []string{"PLUGIN_FORCE"},
Destination: &settings.Force, Destination: &settings.Repo.ForcePush,
Value: false, Value: false,
Category: category, Category: category,
}, },
@ -110,7 +106,7 @@ func settingsFlags(settings *plugin.Settings, category string) []cli.Flag {
Name: "followtags", Name: "followtags",
Usage: "follow tags for pushes to remote repository", Usage: "follow tags for pushes to remote repository",
EnvVars: []string{"PLUGIN_FOLLOWTAGS"}, EnvVars: []string{"PLUGIN_FOLLOWTAGS"},
Destination: &settings.FollowTags, Destination: &settings.Repo.PushFollowTags,
Value: false, Value: false,
Category: category, Category: category,
}, },
@ -118,7 +114,7 @@ func settingsFlags(settings *plugin.Settings, category string) []cli.Flag {
Name: "insecure-ssl-verify", Name: "insecure-ssl-verify",
Usage: "set SSL verification of the remote machine", Usage: "set SSL verification of the remote machine",
EnvVars: []string{"PLUGIN_INSECURE_SSL_VERIFY"}, EnvVars: []string{"PLUGIN_INSECURE_SSL_VERIFY"},
Destination: &settings.InsecureSSLVerify, Destination: &settings.Repo.InsecureSSLVerify,
Value: false, Value: false,
Category: category, Category: category,
}, },
@ -126,7 +122,7 @@ func settingsFlags(settings *plugin.Settings, category string) []cli.Flag {
Name: "empty-commit", Name: "empty-commit",
Usage: "allow empty commits", Usage: "allow empty commits",
EnvVars: []string{"PLUGIN_EMPTY_COMMIT"}, EnvVars: []string{"PLUGIN_EMPTY_COMMIT"},
Destination: &settings.EmptyCommit, Destination: &settings.Repo.EmptyCommit,
Value: false, Value: false,
Category: category, Category: category,
}, },
@ -134,9 +130,32 @@ func settingsFlags(settings *plugin.Settings, category string) []cli.Flag {
Name: "no-verify", Name: "no-verify",
Usage: "bypass the pre-commit and commit-msg hooks", Usage: "bypass the pre-commit and commit-msg hooks",
EnvVars: []string{"PLUGIN_NO_VERIFY"}, EnvVars: []string{"PLUGIN_NO_VERIFY"},
Destination: &settings.NoVerify, Destination: &settings.Repo.NoVerify,
Value: false, Value: false,
Category: category, Category: category,
}, },
&cli.StringFlag{
Name: "pages.directory",
Usage: "source directory for pages sync",
EnvVars: []string{"PLUGIN_PAGES_DIRECTORY"},
Destination: &settings.Pages.Directory,
Value: "docs/",
Category: category,
},
&cli.StringSliceFlag{
Name: "pages.exclude",
Usage: "exclude flag added to pages rsnyc command",
EnvVars: []string{"PLUGIN_PAGES_EXCLUDE"},
Destination: &settings.Pages.Exclude,
Category: category,
},
&cli.BoolFlag{
Name: "pages.delete",
Usage: "delete flag added to pages rsync command",
EnvVars: []string{"PLUGIN_PAGES_DELETE"},
Destination: &settings.Pages.Delete,
Value: true,
Category: category,
},
} }
} }

View File

@ -7,7 +7,7 @@ LABEL org.opencontainers.image.url="https://github.com/thegeeklab/drone-git-acti
LABEL org.opencontainers.image.source="https://github.com/thegeeklab/drone-git-action" LABEL org.opencontainers.image.source="https://github.com/thegeeklab/drone-git-action"
LABEL org.opencontainers.image.documentation="https://github.com/thegeeklab/drone-git-action" LABEL org.opencontainers.image.documentation="https://github.com/thegeeklab/drone-git-action"
RUN apk --update add --no-cache git && \ RUN apk --update add --no-cache git rsync && \
rm -rf /var/cache/apk/* && \ rm -rf /var/cache/apk/* && \
rm -rf /tmp/* rm -rf /tmp/*

View File

@ -7,7 +7,7 @@ LABEL org.opencontainers.image.url="https://github.com/thegeeklab/drone-git-acti
LABEL org.opencontainers.image.source="https://github.com/thegeeklab/drone-git-action" LABEL org.opencontainers.image.source="https://github.com/thegeeklab/drone-git-action"
LABEL org.opencontainers.image.documentation="https://github.com/thegeeklab/drone-git-action" LABEL org.opencontainers.image.documentation="https://github.com/thegeeklab/drone-git-action"
RUN apk --update add --no-cache git && \ RUN apk --update add --no-cache git rsync && \
rm -rf /var/cache/apk/* && \ rm -rf /var/cache/apk/* && \
rm -rf /tmp/* rm -rf /tmp/*

View File

@ -7,7 +7,7 @@ LABEL org.opencontainers.image.url="https://github.com/thegeeklab/drone-git-acti
LABEL org.opencontainers.image.source="https://github.com/thegeeklab/drone-git-action" LABEL org.opencontainers.image.source="https://github.com/thegeeklab/drone-git-action"
LABEL org.opencontainers.image.documentation="https://github.com/thegeeklab/drone-git-action" LABEL org.opencontainers.image.documentation="https://github.com/thegeeklab/drone-git-action"
RUN apk --update add --no-cache git && \ RUN apk --update add --no-cache git rsync && \
rm -rf /var/cache/apk/* && \ rm -rf /var/cache/apk/* && \
rm -rf /tmp/* rm -rf /tmp/*

35
git/clone.go Normal file
View File

@ -0,0 +1,35 @@
package git
import (
"fmt"
"os"
"os/exec"
)
// FetchSource fetches the source from remote.
func FetchSource(repo Repository) *exec.Cmd {
cmd := exec.Command(
"git",
"fetch",
"origin",
fmt.Sprintf("+%s:", repo.Branch),
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
}
// CheckoutHead handles branch checkout.
func CheckoutHead(repo Repository) *exec.Cmd {
cmd := exec.Command(
"git",
"checkout",
"-qf",
repo.Branch,
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
}

View File

@ -1,74 +1,89 @@
package git package git
import ( import (
"os"
"os/exec" "os/exec"
) )
// ForceAdd forces the addition of all dirty files. // ForceAdd forces the addition of all dirty files.
func ForceAdd() *exec.Cmd { func ForceAdd(repo Repository) *exec.Cmd {
cmd := exec.Command( cmd := exec.Command(
"git", "git",
"add", "add",
"--all", "--all",
"--force") "--force",
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd return cmd
} }
// Add updates the index to match the working tree. // Add updates the index to match the working tree.
func Add() *exec.Cmd { func Add(repo Repository) *exec.Cmd {
cmd := exec.Command( cmd := exec.Command(
"git", "git",
"add", "add",
"--all") )
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
if repo.Add != "" {
cmd.Args = append(cmd.Args, repo.Add)
} else {
cmd.Args = append(cmd.Args, "--all")
}
return cmd return cmd
} }
// TestCleanTree returns non-zero if diff between index and local repository // TestCleanTree returns non-zero if diff between index and local repository
func TestCleanTree() *exec.Cmd { func TestCleanTree(repo Repository) *exec.Cmd {
cmd := exec.Command( cmd := exec.Command(
"git", "git",
"diff-index", "diff-index",
"--quiet", "--quiet",
"HEAD", "HEAD",
"--ignore-submodules") "--ignore-submodules",
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd return cmd
} }
// EmptyCommit simply create an empty commit // EmptyCommit simply create an empty commit
func EmptyCommit(msg string, noVerify bool) *exec.Cmd { func EmptyCommit(repo Repository) *exec.Cmd {
cmd := exec.Command( cmd := exec.Command(
"git", "git",
"commit", "commit",
"--allow-empty", "--allow-empty",
"-m", "-m",
msg, repo.CommitMsg,
) )
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
if noVerify { if repo.NoVerify {
cmd.Args = append( cmd.Args = append(cmd.Args, "--no-verify")
cmd.Args,
"--no-verify")
} }
return cmd return cmd
} }
// ForceCommit commits every change while skipping CI. // ForceCommit commits every change while skipping CI.
func ForceCommit(msg string, noVerify bool) *exec.Cmd { func ForceCommit(repo Repository) *exec.Cmd {
cmd := exec.Command( cmd := exec.Command(
"git", "git",
"commit", "commit",
"-m", "-m",
msg, repo.CommitMsg,
) )
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
if noVerify { if repo.NoVerify {
cmd.Args = append( cmd.Args = append(cmd.Args, "--no-verify")
cmd.Args,
"--no-verify")
} }
return cmd return cmd

View File

@ -1,42 +1,67 @@
package git package git
import ( import (
"os"
"os/exec" "os/exec"
"strconv" "strconv"
) )
// SetUserEmail sets the global git author email. // repoUserEmail sets the global git author email.
func SetUserEmail(email string) *exec.Cmd { func ConfigAutocorrect(repo Repository) *exec.Cmd {
cmd := exec.Command(
"git",
"config",
"--local",
"help.autocorrect",
repo.Autocorrect,
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
}
// repoUserEmail sets the global git author email.
func ConfigUserEmail(repo Repository) *exec.Cmd {
cmd := exec.Command( cmd := exec.Command(
"git", "git",
"config", "config",
"--local", "--local",
"user.email", "user.email",
email) repo.Author.Email,
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd return cmd
} }
// SetUserName sets the global git author name. // repoUserName sets the global git author name.
func SetUserName(author string) *exec.Cmd { func ConfigUserName(repo Repository) *exec.Cmd {
cmd := exec.Command( cmd := exec.Command(
"git", "git",
"config", "config",
"--local", "--local",
"user.name", "user.name",
author) repo.Author.Name,
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd return cmd
} }
// SetSSLSkipVerify disables globally the git ssl verification. // repoSSLVerify disables globally the git ssl verification.
func SetSSLVerify(sslVerify bool) *exec.Cmd { func ConfigSSLVerify(repo Repository) *exec.Cmd {
cmd := exec.Command( cmd := exec.Command(
"git", "git",
"config", "config",
"--local", "--local",
"http.sslVerify", "http.sslVerify",
strconv.FormatBool(sslVerify)) strconv.FormatBool(repo.SSLVerify),
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd return cmd
} }

View File

@ -1,52 +1,59 @@
package git package git
import ( import (
"fmt"
"os"
"os/exec" "os/exec"
) )
// RemoteRemove drops the defined remote from a git repo. // RemoteRemove drops the defined remote from a git repo.
func RemoteRemove(name string) *exec.Cmd { func RemoteRemove(repo Repository) *exec.Cmd {
cmd := exec.Command( cmd := exec.Command(
"git", "git",
"remote", "remote",
"rm", "rm",
name) repo.RemoteName,
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd return cmd
} }
// RemoteAdd adds an additional remote to a git repo. // RemoteAdd adds an additional remote to a git repo.
func RemoteAdd(name, url string) *exec.Cmd { func RemoteAdd(repo Repository) *exec.Cmd {
cmd := exec.Command( cmd := exec.Command(
"git", "git",
"remote", "remote",
"add", "add",
name, repo.RemoteName,
url) repo.RemoteURL,
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd return cmd
} }
// RemotePush pushs the changes from the local head to a remote branch. // RemotePush pushs the changes from the local head to a remote branch.
func RemotePush(remote, branch string, force, followtags bool) *exec.Cmd { func RemotePush(repo Repository) *exec.Cmd {
return RemotePushNamedBranch(remote, "HEAD", branch, force, followtags)
}
// RemotePushNamedBranch puchs changes from a local to a remote branch.
func RemotePushNamedBranch(remote, localbranch, branch string, force, followtags bool) *exec.Cmd {
cmd := exec.Command( cmd := exec.Command(
"git", "git",
"push", "push",
remote, repo.RemoteName,
localbranch+":"+branch) fmt.Sprintf("HEAD:%s", repo.Branch),
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
if force { if repo.ForcePush {
cmd.Args = append( cmd.Args = append(
cmd.Args, cmd.Args,
"--force") "--force",
)
} }
if followtags { if repo.PushFollowTags {
cmd.Args = append( cmd.Args = append(
cmd.Args, cmd.Args,
"--follow-tags") "--follow-tags")

42
git/status.go Normal file
View File

@ -0,0 +1,42 @@
package git
import (
"bytes"
"fmt"
"os"
"os/exec"
)
func Status(repo Repository) *exec.Cmd {
cmd := exec.Command(
"git",
"status",
"--porcelain",
)
cmd.Dir = repo.WorkDir
cmd.Stderr = os.Stderr
return cmd
}
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)
if err != nil {
return false
}
if res.Len() > 0 {
fmt.Print(res.String())
return true
}
return false
}

27
git/type.go Normal file
View File

@ -0,0 +1,27 @@
package git
type Author struct {
Name string
Email string
}
type Repository struct {
RemoteName string
RemoteURL string
Branch string
Add string
CommitMsg string
Autocorrect string
NoVerify bool
InsecureSSLVerify bool
EmptyCommit bool
PushFollowTags bool
ForcePush bool
SSLVerify bool
WorkDir string
InitExists bool
Author Author
}

View File

@ -3,8 +3,10 @@ package git
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"os/user" "os/user"
"path/filepath" "path/filepath"
"strings"
) )
const netrcFile = ` const netrcFile = `
@ -27,17 +29,13 @@ func WriteSSHKey(privateKey string) error {
home = currentUser.HomeDir home = currentUser.HomeDir
} }
sshpath := filepath.Join( sshpath := filepath.Join(home, ".ssh")
home,
".ssh")
if err := os.MkdirAll(sshpath, 0o700); err != nil { if err := os.MkdirAll(sshpath, 0o700); err != nil {
return err return err
} }
confpath := filepath.Join( confpath := filepath.Join(sshpath, "config")
sshpath,
"config")
if err := os.WriteFile( if err := os.WriteFile(
confpath, confpath,
@ -47,10 +45,7 @@ func WriteSSHKey(privateKey string) error {
return err return err
} }
privpath := filepath.Join( privpath := filepath.Join(sshpath, "id_rsa")
sshpath,
"id_rsa",
)
if err := os.WriteFile( if err := os.WriteFile(
privpath, privpath,
@ -89,3 +84,20 @@ func WriteNetrc(machine, login, password string) error {
0o600, 0o600,
) )
} }
func trace(cmd *exec.Cmd) {
fmt.Fprintf(os.Stdout, "+ %s\n", strings.Join(cmd.Args, " "))
}
func runCommand(cmd *exec.Cmd) error {
if cmd.Stdout == nil {
cmd.Stdout = os.Stdout
}
if cmd.Stderr == nil {
cmd.Stderr = os.Stderr
}
trace(cmd)
return cmd.Run()
}

View File

@ -1,111 +0,0 @@
package plugin
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"github.com/thegeeklab/drone-git-action/git"
)
// InitRepo initializes the repository.
func (p Plugin) initRepo() error {
path := filepath.Join(p.settings.Path, ".git")
if err := os.MkdirAll(p.settings.Path, os.ModePerm); err != nil {
return err
}
if err := os.Chdir(p.settings.Path); err != nil {
return err
}
if isDirEmpty(path) {
return execute(exec.Command(
"git",
"init",
))
}
return nil
}
// AddRemote adds a remote to repository.
func (p Plugin) addRemote() error {
if p.settings.Remote != "" {
if err := execute(git.RemoteAdd("origin", p.settings.Remote)); err != nil {
return err
}
}
return nil
}
// FetchSource fetches the source from remote.
func (p Plugin) fetchSource() error {
return execute(exec.Command(
"git",
"fetch",
"origin",
fmt.Sprintf("+%s:", p.settings.Branch),
))
}
// CheckoutHead handles branch checkout.
func (p Plugin) checkoutHead() error {
return execute(exec.Command(
"git",
"checkout",
"-qf",
p.settings.Branch,
))
}
// HandleClone clones remote.
func (p Plugin) handleClone() error {
if err := p.addRemote(); err != nil {
return err
}
if err := p.fetchSource(); err != nil {
return err
}
if err := p.checkoutHead(); err != nil {
return err
}
return nil
}
// HandleCommit commits changes locally.
func (p Plugin) handleCommit() error {
if err := execute(git.Add()); err != nil {
return err
}
if err := execute(git.TestCleanTree()); err != nil {
if err := execute(git.ForceCommit(p.settings.Message, p.settings.NoVerify)); err != nil {
return err
}
} else {
if p.settings.EmptyCommit {
if err := execute(git.EmptyCommit(p.settings.Message, p.settings.NoVerify)); err != nil {
return err
}
}
}
return nil
}
// HandlePush pushs changes to remote.
func (p Plugin) handlePush() error {
return execute(git.RemotePushNamedBranch(
"origin",
p.settings.Branch,
p.settings.Branch,
p.settings.Force,
p.settings.FollowTags,
))
}

View File

@ -3,6 +3,8 @@ package plugin
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"path/filepath"
"github.com/thegeeklab/drone-git-action/git" "github.com/thegeeklab/drone-git-action/git"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -14,37 +16,38 @@ type Netrc struct {
Password string Password string
} }
type Commit struct { type Pages struct {
Author Author Directory string
} Exclude cli.StringSlice
Delete bool
type Author struct {
Name string
Email string
} }
// Settings for the Plugin. // Settings for the Plugin.
type Settings struct { type Settings struct {
Actions cli.StringSlice Action cli.StringSlice
SSHKey string SSHKey string
Remote string
Branch string
Path string
Message string
Force bool
FollowTags bool
InsecureSSLVerify bool
EmptyCommit bool
NoVerify bool
Netrc Netrc Netrc Netrc
Commit Commit Pages Pages
Author Author Repo git.Repository
} }
// Validate handles the settings validation of the plugin. // Validate handles the settings validation of the plugin.
func (p *Plugin) Validate() error { func (p *Plugin) Validate() error {
for _, action := range p.settings.Actions.Value() { var err error
p.settings.Repo.Autocorrect = "never"
p.settings.Repo.RemoteName = "origin"
p.settings.Repo.Add = ""
if p.settings.Repo.WorkDir == "" {
p.settings.Repo.WorkDir, err = os.Getwd()
}
if err != nil {
return err
}
for _, action := range p.settings.Action.Value() {
switch action { switch action {
case "clone": case "clone":
continue continue
@ -52,7 +55,30 @@ func (p *Plugin) Validate() error {
continue continue
case "push": case "push":
if p.settings.SSHKey == "" && p.settings.Netrc.Password == "" { if p.settings.SSHKey == "" && p.settings.Netrc.Password == "" {
return fmt.Errorf("either SSH key or netrc password are required") return fmt.Errorf("either SSH key or netrc password is required")
}
case "pages":
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")
if _, err := os.Stat(p.settings.Pages.Directory); os.IsNotExist(err) {
return fmt.Errorf("pages directory '%s' must exist", p.settings.Pages.Directory)
}
if info, _ := os.Stat(p.settings.Pages.Directory); !info.IsDir() {
return fmt.Errorf("pages directory '%s' is not a directory", p.settings.Pages.Directory)
}
if p.settings.SSHKey == "" && p.settings.Netrc.Password == "" {
return fmt.Errorf("either SSH key or netrc password is required")
}
if p.settings.Pages.Directory == "" {
return fmt.Errorf("pages source directory needs to be set")
}
if len(p.settings.Action.Value()) > 1 {
return fmt.Errorf("pages action can not be combined with other actions")
} }
default: default:
return fmt.Errorf("unknown action %s", action) return fmt.Errorf("unknown action %s", action)
@ -81,19 +107,20 @@ func (p *Plugin) Execute() error {
return err return err
} }
if p.settings.Path != "" { if err := p.initRepo(); err != nil {
if err := p.initRepo(); err != nil { return err
return err
}
} }
if err := git.SetUserName(p.settings.Commit.Author.Name).Run(); err != nil { if err := git.ConfigAutocorrect(p.settings.Repo).Run(); err != nil {
return err return err
} }
if err := git.SetUserEmail(p.settings.Commit.Author.Email).Run(); err != nil { if err := git.ConfigUserName(p.settings.Repo).Run(); err != nil {
return err return err
} }
if err := git.SetSSLVerify(p.settings.InsecureSSLVerify).Run(); err != nil { if err := git.ConfigUserEmail(p.settings.Repo).Run(); err != nil {
return err
}
if err := git.ConfigSSLVerify(p.settings.Repo).Run(); err != nil {
return err return err
} }
@ -107,7 +134,7 @@ func (p *Plugin) Execute() error {
return err return err
} }
for _, action := range p.settings.Actions.Value() { for _, action := range p.settings.Action.Value() {
switch action { switch action {
case "clone": case "clone":
if err := p.handleClone(); err != nil { if err := p.handleClone(); err != nil {
@ -121,8 +148,103 @@ func (p *Plugin) Execute() error {
if err := p.handlePush(); err != nil { if err := p.handlePush(); err != nil {
return err return err
} }
case "pages":
if err := p.handlePages(); err != nil {
return err
}
} }
} }
return nil return nil
} }
// InitRepo initializes the repository.
func (p *Plugin) initRepo() error {
path := filepath.Join(p.settings.Repo.WorkDir, ".git")
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
}
cmd := exec.Command(
"git",
"init",
)
cmd.Dir = p.settings.Repo.WorkDir
return execute(cmd)
}
// HandleClone clones remote.
func (p *Plugin) handleClone() error {
if p.settings.Repo.InitExists {
return fmt.Errorf("destination '%s' already exists and is not an empty directory", 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 {
return err
}
if err := execute(git.CheckoutHead(p.settings.Repo)); err != nil {
return err
}
return nil
}
// HandleCommit commits changes locally.
func (p *Plugin) handleCommit() error {
if err := execute(git.Add(p.settings.Repo)); 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
}
} else {
if p.settings.Repo.EmptyCommit {
if err := execute(git.EmptyCommit(p.settings.Repo)); err != nil {
return err
}
}
}
return nil
}
// HandlePush pushs changes to remote.
func (p *Plugin) handlePush() error {
return execute(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 {
defer os.RemoveAll(p.settings.Repo.WorkDir)
if err := p.handleClone(); err != nil {
return err
}
if err := execute(
rsyncDirectories(p.settings.Pages, p.settings.Repo),
); err != nil {
return err
}
if err := p.handleCommit(); err != nil {
return err
}
return p.handlePush()
}

View File

@ -2,10 +2,11 @@ package plugin
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"github.com/thegeeklab/drone-git-action/git"
) )
// helper function to simply wrap os execte command. // helper function to simply wrap os execte command.
@ -19,15 +20,39 @@ func execute(cmd *exec.Cmd) error {
return cmd.Run() return cmd.Run()
} }
// helper function returns true if directory dir is empty. func rsyncDirectories(pages Pages, repo git.Repository) *exec.Cmd {
func isDirEmpty(dir string) bool { args := []string{
f, err := os.Open(dir) "-r",
if err != nil { "--exclude",
return true ".git",
} }
defer f.Close() for _, item := range pages.Exclude.Value() {
args = append(
args,
"--exclude",
item,
)
}
_, err = f.Readdir(1) if pages.Delete {
return err == io.EOF args = append(
args,
"--delete",
)
}
args = append(
args,
".",
repo.WorkDir,
)
cmd := exec.Command(
"rsync",
args...,
)
cmd.Dir = pages.Directory
return cmd
} }