216 lines
4.3 KiB
Go
216 lines
4.3 KiB
Go
package cmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
//bolt "go.etcd.io/bbolt"
|
|
"gopkg.in/src-d/go-git.v4"
|
|
)
|
|
|
|
// githubCmd represents the github command
|
|
var githubCmd = &cobra.Command{
|
|
Use: "github",
|
|
Short: "Github archivist support",
|
|
Long: `Archivist tools to backup your Github account to local storage`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
err := ghList()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
},
|
|
}
|
|
|
|
var githubUser string
|
|
var githubToken string
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(githubCmd)
|
|
githubCmd.AddCommand(backupCmd)
|
|
|
|
githubCmd.PersistentFlags().StringVar(&githubUser, "github-user", "", "The github user to backup")
|
|
viper.BindPFlag("github.user", githubCmd.PersistentFlags().Lookup("github-user"))
|
|
githubCmd.PersistentFlags().StringVar(&githubToken, "github-token", "", "The github token to use")
|
|
viper.BindPFlag("github.token", githubCmd.PersistentFlags().Lookup("github-token"))
|
|
|
|
}
|
|
|
|
var backupCmd = &cobra.Command{
|
|
Use: "backup",
|
|
Short: "Run a backup on your Github account",
|
|
Long: ``,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
err := ghBackup()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
},
|
|
}
|
|
|
|
type repo struct {
|
|
FullName string `json:"full_name"`
|
|
UpdatedDate time.Time `json:"updated_at"`
|
|
CreatedDate time.Time `json:"created_at"`
|
|
}
|
|
|
|
func ghQuery(url string) ([]byte, string, error) {
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
req.Header.Add("Authorization", "Bearer "+viper.GetString("github.token"))
|
|
req.Header.Add("Accept", "application/vnd.github.v3+json")
|
|
|
|
client := http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
body, _ := ioutil.ReadAll(resp.Body)
|
|
|
|
next := ""
|
|
//links := strings.Split(resp.Header["Link"], ",")
|
|
for _, l := range strings.Split(resp.Header["Link"][0], ",") {
|
|
if strings.Contains(l, `rel="next"`) {
|
|
str := strings.Split(l, ";")[0]
|
|
next = strings.Trim(str, " <>")
|
|
}
|
|
}
|
|
|
|
return body, next, nil
|
|
}
|
|
|
|
func ghGetRepos() ([]repo, error) {
|
|
next := "https://api.github.com/user/repos"
|
|
var repos []repo
|
|
|
|
for {
|
|
var body []byte
|
|
var err error
|
|
body, next, err = ghQuery(next)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var temp []repo
|
|
err = json.Unmarshal(body, &temp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
repos = append(repos, temp...)
|
|
if next == "" {
|
|
break
|
|
}
|
|
}
|
|
|
|
return repos, nil
|
|
|
|
}
|
|
|
|
func ghList() error {
|
|
if Verbose {
|
|
fmt.Println("Listing", C.Green("Github"), "Repos for", C.Magenta(viper.Get("github.user")))
|
|
}
|
|
|
|
repos, err := ghGetRepos()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Println(viper.GetString("github.user"), "has", len(repos), "repositories.")
|
|
|
|
for _, r := range repos {
|
|
|
|
fmt.Println(r.FullName)
|
|
if Verbose {
|
|
fmt.Println("\tUpdated:", r.UpdatedDate)
|
|
fmt.Println("\tCreated:", r.CreatedDate)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ghBackup() error {
|
|
if Verbose {
|
|
fmt.Println("Backup up", C.Green("Github"), "repos for", C.Magenta(viper.Get("github.user")))
|
|
}
|
|
|
|
repos, err := ghGetRepos()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c := make(chan error)
|
|
for _, repo := range repos {
|
|
go ghBackupRepo(repo, c)
|
|
}
|
|
|
|
for i := 0; i < len(repos); i++ {
|
|
if <-c != nil {
|
|
return err
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ghBackupRepo(r repo, c chan error) {
|
|
|
|
var tempErr error
|
|
tempErr = nil
|
|
|
|
if Verbose {
|
|
fmt.Println("Backing up", C.Blue(r.FullName))
|
|
}
|
|
|
|
backupPath := DataDir + "/github/" + r.FullName
|
|
|
|
if _, err := os.Stat(backupPath); os.IsNotExist(err) {
|
|
if Verbose {
|
|
fmt.Println(C.Blue(r.FullName), "does not exist. Cloning.")
|
|
}
|
|
cloneURL := "https://" + viper.GetString("github.token") + "@github.com/" + r.FullName
|
|
|
|
_, err := git.PlainClone(backupPath, false, &git.CloneOptions{
|
|
URL: cloneURL,
|
|
})
|
|
if err != nil {
|
|
tempErr = err
|
|
}
|
|
} else {
|
|
if Verbose {
|
|
fmt.Println(C.Blue(r.FullName), "cloned already, running git pull")
|
|
}
|
|
|
|
l, err := git.PlainOpen(backupPath)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
tempErr = err
|
|
}
|
|
w, err := l.Worktree()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
tempErr = err
|
|
}
|
|
err = w.Pull(&git.PullOptions{RemoteName: "origin"})
|
|
if err != nil && err != git.NoErrAlreadyUpToDate {
|
|
fmt.Println(err)
|
|
tempErr = err
|
|
}
|
|
if Verbose {
|
|
ref, _ := l.Head()
|
|
fmt.Println(C.Blue(r.FullName), "updated to", C.Green(ref))
|
|
}
|
|
}
|
|
c <- tempErr
|
|
}
|