Skip to content

Commit

Permalink
feat: birth.
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez committed Aug 5, 2020
1 parent bdffd2e commit 36bffd9
Show file tree
Hide file tree
Showing 10 changed files with 463 additions and 2 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ mixtus
mixtus.exe
dist/
unsecured/
*.bak
*.bak
docs/
5 changes: 5 additions & 0 deletions .golangci.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
[linters-settings.misspell]
locale = "US"

[linters-settings.funlen]
lines = 80 # default 60
statements = 50 # default 40

[linters]
enable-all = true
disable = [
Expand All @@ -37,6 +41,7 @@
"testpackage",
"goerr113",
"nestif",
"nlreturn",
]

[issues]
Expand Down
23 changes: 23 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

// Config is the bot configuration.
type Config struct {
Token string
Git GitInfo
Source RepoInfo
Target RepoInfo
Debug bool
}

// GitInfo represents the Git user configuration used for commit.
type GitInfo struct {
UserName string
UserEmail string
}

// RepoInfo represents the information about a GitHub repository.
type RepoInfo struct {
Owner string
RepoName string
DocPath string
}
134 changes: 134 additions & 0 deletions core.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package main

import (
"context"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"

"github.com/ldez/go-git-cmd-wrapper/add"
"github.com/ldez/go-git-cmd-wrapper/checkout"
"github.com/ldez/go-git-cmd-wrapper/clone"
"github.com/ldez/go-git-cmd-wrapper/commit"
"github.com/ldez/go-git-cmd-wrapper/config"
"github.com/ldez/go-git-cmd-wrapper/git"
"github.com/ldez/go-git-cmd-wrapper/push"
"github.com/ldez/go-git-cmd-wrapper/types"
"github.com/traefik/mixtus/file"
)

func run(cfg Config) error {
dir, err := ioutil.TempDir("", "mixtus-*")
if err != nil {
return err
}

defer func() { _ = os.RemoveAll(dir) }()

docTarget := filepath.Join(dir, cfg.Target.DocPath)

docGitURL := makeRemoteURL(cfg.Target, cfg.Token, true)

output, err := git.Clone(clone.Repository(docGitURL), clone.Depth("1"), clone.Directory(dir), git.Debugger(cfg.Debug))
if err != nil {
log.Println(output)
return fmt.Errorf("failed to clone documentation repository: %w", err)
}

// clean doc
err = os.RemoveAll(docTarget)
if err != nil {
return fmt.Errorf("failed to reset documentation: %w", err)
}

// copy source docs into targeted directory
err = file.Copy(cfg.Source.DocPath, docTarget)
if err != nil {
return fmt.Errorf("failed to copy documentation: %w", err)
}

err = os.Chdir(dir)
if err != nil {
return err
}

output, err = setupGitUserInfo(cfg.Git)
if err != nil {
fmt.Println(output)
return fmt.Errorf("failed to setup Git user configuration: %w", err)
}

output, err = git.Raw("status", func(g *types.Cmd) { g.AddOptions("-s") })
if err != nil {
fmt.Println(output)
return fmt.Errorf("failed to get Git status: %w", err)
}

if len(output) == 0 {
log.Println("Nothing to commit.")
return nil
}

branchName := filepath.Base(dir)

output, err = git.Checkout(checkout.NewBranch(branchName), git.Debugger(cfg.Debug))
if err != nil {
log.Println(output)
return fmt.Errorf("failed to create a new branch: %w", err)
}

output, err = git.Add(add.PathSpec(cfg.Target.DocPath), git.Debugger(cfg.Debug))
if err != nil {
log.Println(output)
return fmt.Errorf("failed to add files: %w", err)
}

output, err = git.Commit(commit.Message(fmt.Sprintf("Update documentation for %s", cfg.Source.RepoName)), git.Debugger(cfg.Debug))
if err != nil {
log.Println(output)
return fmt.Errorf("failed to commit: %w", err)
}

output, err = git.Push(push.Remote("origin"), push.Repo(cfg.Target.RepoName), git.Debugger(cfg.Debug))
if err != nil {
log.Println(output)
return fmt.Errorf("failed to push: %s", err)
}

ctx := context.Background()

return createPullRequest(ctx, cfg, branchName)
}

func makeRemoteURL(target RepoInfo, token string, ssh bool) string {
if ssh {
return fmt.Sprintf("git@github.com:%s/%s.git", target.Owner, target.RepoName)
}

prefix := "https://"
if len(token) > 0 {
prefix += token + "@"
}

return fmt.Sprintf("%sgithub.com/%s/%s.git", prefix, target.Owner, target.RepoName)
}

func setupGitUserInfo(gitInfo GitInfo) (string, error) {
if len(gitInfo.UserEmail) != 0 {
output, err := git.Config(config.Entry("user.email", gitInfo.UserEmail))
if err != nil {
return output, err
}
}

if len(gitInfo.UserName) != 0 {
output, err := git.Config(config.Entry("user.name", gitInfo.UserName))
if err != nil {
return output, err
}
}

return "", nil
}
71 changes: 71 additions & 0 deletions file/copy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package file

import (
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
)

// Copy copies src to dst.
func Copy(src, dst string) error {
info, err := os.Stat(src)
if err != nil {
return err
}
return tryCopy(src, dst, info)
}

func tryCopy(src, dst string, info os.FileInfo) error {
if info.IsDir() {
return directoryCopy(src, dst, info)
}
return fileCopy(src, dst, info)
}

func fileCopy(src, dst string, info os.FileInfo) error {
f, err := os.Create(dst)
if err != nil {
return err
}
defer safeClose(f.Close)

if err = os.Chmod(f.Name(), info.Mode()); err != nil {
return err
}

s, err := os.Open(src)
if err != nil {
return err
}
defer safeClose(s.Close)

_, err = io.Copy(f, s)
return err
}

func directoryCopy(src, dst string, info os.FileInfo) error {
if err := os.MkdirAll(dst, info.Mode()); err != nil {
return err
}

infos, err := ioutil.ReadDir(src)
if err != nil {
return err
}

for _, info := range infos {
if err := tryCopy(filepath.Join(src, info.Name()), filepath.Join(dst, info.Name()), info); err != nil {
return err
}
}

return nil
}

func safeClose(fn func() error) {
if err := fn(); err != nil {
log.Println(err)
}
}
60 changes: 60 additions & 0 deletions github.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

import (
"context"
"fmt"
"log"

"github.com/google/go-github/v32/github"
"golang.org/x/oauth2"
)

type message struct {
Title string
Body string
}

func createPullRequest(ctx context.Context, cfg Config, branchName string) error {
client := newGitHubClient(ctx, cfg.Token)

msg := message{
Title: fmt.Sprintf("Update documentation for %s", cfg.Source.RepoName),
Body: fmt.Sprintf(`Update documentation for [%s](https://github.com/%s/%s).`,
cfg.Source.RepoName, cfg.Source.Owner, cfg.Source.RepoName),
}

newPR := &github.NewPullRequest{
Title: github.String(msg.Title),
Head: github.String(branchName),
Base: github.String("master"),
Body: github.String(msg.Body),
MaintainerCanModify: github.Bool(true),
}

pullRequest, _, err := client.PullRequests.Create(ctx, cfg.Target.Owner, cfg.Target.RepoName, newPR)
if err != nil {
return err
}

log.Println(pullRequest.GetHTMLURL())

labels := []string{"status/3-needs-merge", fmt.Sprintf("area/%s", cfg.Source.RepoName)}

_, _, err = client.Issues.AddLabelsToIssue(ctx, cfg.Target.Owner, cfg.Target.RepoName, pullRequest.GetNumber(), labels)
if err != nil {
return err
}

return nil
}

func newGitHubClient(ctx context.Context, token string) *github.Client {
if len(token) == 0 {
return github.NewClient(nil)
}

ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
return github.NewClient(oauth2.NewClient(ctx, ts))
}
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/traefik/mixtus

go 1.14

require (
github.com/google/go-github/v32 v32.1.0
github.com/ldez/go-git-cmd-wrapper v1.2.0
github.com/stretchr/testify v1.6.1
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
)
27 changes: 27 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/ldez/go-git-cmd-wrapper v1.2.0 h1:jWSL+C9RFpMVaX+5lmouZo2yp4v3UJsYOwnoKgJSzIE=
github.com/ldez/go-git-cmd-wrapper v1.2.0/go.mod h1:Nf4t6+pbkhWuCiS2bmPv5xfh9JXPAAboaF4KOoYA7wM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading

0 comments on commit 36bffd9

Please sign in to comment.