initial commit
This commit is contained in:
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
.env
|
||||
debug
|
||||
|
||||
# Created by https://www.gitignore.io/api/visualstudiocode
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history
|
||||
|
||||
|
||||
# End of https://www.gitignore.io/api/visualstudiocode
|
||||
|
||||
# Created by https://www.gitignore.io/api/go
|
||||
|
||||
### Go ###
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
|
||||
# End of https://www.gitignore.io/api/go
|
||||
21
.vscode/launch.json
vendored
Normal file
21
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"remotePath": "",
|
||||
"port": 2345,
|
||||
"host": "127.0.0.1",
|
||||
"program": "${workspaceFolder}/main.go",
|
||||
"env": {},
|
||||
"args": [],
|
||||
"showLog": true
|
||||
}
|
||||
]
|
||||
}
|
||||
69
main.go
Normal file
69
main.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.gyulakerezsi.ro/migrate-github/migration"
|
||||
"git.gyulakerezsi.ro/migrate-github/utils"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
type ghRepo struct {
|
||||
Name string `json:"name"`
|
||||
FullName string `json:"full_name"`
|
||||
CloneURL string `json:"clone_url"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
godotenv.Load()
|
||||
|
||||
gogsTrgt := os.Getenv("TARGET_GOGS")
|
||||
gogsPat := os.Getenv("GOGS_PAT")
|
||||
user := os.Getenv("GH_USER")
|
||||
|
||||
response, err := http.Get("https://api.github.com/users/" + user + "/repos")
|
||||
utils.PanicOnError(err)
|
||||
|
||||
migrationClient := migration.NewClient(gogsTrgt, gogsPat)
|
||||
|
||||
repos := make([]ghRepo, 0)
|
||||
utils.UnmarshalFromResponse(response, &repos)
|
||||
|
||||
migrated := make([]string, 0)
|
||||
for _, el := range repos {
|
||||
fmt.Printf("Migrate %s to your git? [Y/n] > ", el.FullName)
|
||||
resp := utils.ReadLine()
|
||||
|
||||
switch resp = strings.ToLower(strings.TrimSpace(resp)); resp {
|
||||
case "n":
|
||||
fmt.Println("skipping")
|
||||
default:
|
||||
fmt.Print("Target repo name [leave blank to use the curent one] > ")
|
||||
|
||||
newName := utils.ReadLine()
|
||||
if newName == "" {
|
||||
newName = el.Name
|
||||
}
|
||||
|
||||
fmt.Printf("migrating %s ==> %s/%s\n", el.CloneURL, migrationClient.User.Username, newName)
|
||||
err := migrationClient.Migrate(el.CloneURL, el.Description, newName)
|
||||
utils.PanicOnError(err)
|
||||
|
||||
migrated = append(migrated, el.FullName+" -> "+migrationClient.User.Username+"/"+newName)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("\n\n================================================")
|
||||
if len(migrated) > 0 {
|
||||
fmt.Println("Migrated:")
|
||||
for _, el := range migrated {
|
||||
fmt.Printf("\t%s\n", el)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Nothing migrated")
|
||||
}
|
||||
}
|
||||
83
migration/client.go
Normal file
83
migration/client.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package migration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"git.gyulakerezsi.ro/migrate-github/utils"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
apiURL string
|
||||
pat string
|
||||
httpClient *http.Client
|
||||
User *userResponse
|
||||
}
|
||||
|
||||
type userResponse struct {
|
||||
Username string `json:"username"`
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
type migrateRequest struct {
|
||||
CloneAddr string `json:"clone_addr"`
|
||||
UID int `json:"uid"`
|
||||
RepoName string `json:"repo_name"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// Migrate will send a migrate request to the gogs server
|
||||
func (c *client) Migrate(cloneURL, description, targetName string) error {
|
||||
jsonBody, err := json.Marshal(&migrateRequest{cloneURL, c.User.ID, targetName, description})
|
||||
utils.PanicOnError(err)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, c.apiURL+"repos/migrate", bytes.NewReader(jsonBody))
|
||||
utils.PanicOnError(err)
|
||||
|
||||
c.setHeadersOnRequest(req)
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
utils.PanicOnError(err)
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("got non-success status code: %s\n==========request=========\n%+v\n=================response=============\n%+v", resp.Status, req, resp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) setHeadersOnRequest(req *http.Request) {
|
||||
req.Header.Set("Authorization", "token "+c.pat)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
if req.Method == http.MethodPost {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) getUIDFromGogs() {
|
||||
req, err := http.NewRequest(http.MethodGet, c.apiURL+"user", http.NoBody)
|
||||
utils.PanicOnError(err)
|
||||
c.setHeadersOnRequest(req)
|
||||
|
||||
response, err := c.httpClient.Do(req)
|
||||
utils.PanicOnError(err)
|
||||
|
||||
user := userResponse{}
|
||||
utils.UnmarshalFromResponse(response, &user)
|
||||
c.User = &user
|
||||
}
|
||||
|
||||
// NewClient creates new migrationClient
|
||||
func NewClient(baseURL, pat string) *client {
|
||||
client := &client{}
|
||||
client.apiURL = strings.TrimRight(baseURL, "/") + "/api/v1/"
|
||||
client.pat = pat
|
||||
client.httpClient = &http.Client{}
|
||||
client.getUIDFromGogs()
|
||||
|
||||
return client
|
||||
}
|
||||
8
utils/panicOnError.go
Normal file
8
utils/panicOnError.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package utils
|
||||
|
||||
// PanicOnError will panic if err goven is not nil
|
||||
func PanicOnError(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
21
utils/readLine.go
Normal file
21
utils/readLine.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var rd *bufio.Reader
|
||||
|
||||
// ReadLine reads a line from stdin and strips newline from the end
|
||||
func ReadLine() string {
|
||||
if rd == nil {
|
||||
rd = bufio.NewReader(os.Stdin)
|
||||
}
|
||||
|
||||
str, err := rd.ReadString('\n')
|
||||
PanicOnError(err)
|
||||
|
||||
return strings.TrimRight(str, "\r\n")
|
||||
}
|
||||
16
utils/unmarshalFromResponse.go
Normal file
16
utils/unmarshalFromResponse.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// UnmarshalFromResponse will unmarshal json from response body
|
||||
func UnmarshalFromResponse(response *http.Response, v interface{}) {
|
||||
responseBody, err := ioutil.ReadAll(response.Body)
|
||||
PanicOnError(err)
|
||||
|
||||
err = json.Unmarshal(responseBody, &v)
|
||||
PanicOnError(err)
|
||||
}
|
||||
Reference in New Issue
Block a user