-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
463 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,4 +6,5 @@ mixtus | |
mixtus.exe | ||
dist/ | ||
unsecured/ | ||
*.bak | ||
*.bak | ||
docs/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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= |
Oops, something went wrong.