initial commit
This commit is contained in:
commit
d3a068fe2b
|
@ -0,0 +1,2 @@
|
||||||
|
.notes
|
||||||
|
app.yml
|
|
@ -0,0 +1,89 @@
|
||||||
|
# gogstea
|
||||||
|
|
||||||
|
## migration from gogs to gitea with less pain
|
||||||
|
|
||||||
|
High level operation:
|
||||||
|
|
||||||
|
The main idea is to do a copy. The script will take the users from csv data from gogs, previously obtained, and will create gitea users interacting with gitea API
|
||||||
|
|
||||||
|
```go
|
||||||
|
source := gogs.mysql --> select * from user; --> csv
|
||||||
|
|
||||||
|
dst:= gitea.postgres --> API /admin gitea --> go script read from csv and http post to gitea api
|
||||||
|
copy(dst, source)
|
||||||
|
```
|
||||||
|
|
||||||
|
### users migration
|
||||||
|
|
||||||
|
Export results of this query to .csv to migrate the users
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM user
|
||||||
|
WHERE email !='';
|
||||||
|
```
|
||||||
|
|
||||||
|
**Execution:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
CONFIG_FILE=./app.yml go run ./cmd/users/main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### orgs migration
|
||||||
|
|
||||||
|
Export results of this query to .csv to migrate the users
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
u.name,
|
||||||
|
ou.org_id,
|
||||||
|
uo.name AS owner_name,
|
||||||
|
uo.email AS owner_email
|
||||||
|
FROM org_user ou
|
||||||
|
INNER JOIN user u ON ou.org_id = u.id
|
||||||
|
INNER JOIN user uo ON uo.id = ou.is_owner
|
||||||
|
```
|
||||||
|
|
||||||
|
**Execution:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
CONFIG_FILE=./app.yml go run ./cmd/org/main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### repositories migration
|
||||||
|
|
||||||
|
You have to options: You can use the API to import them or like I did: manually import using the `unadopted` button from the UI.
|
||||||
|
|
||||||
|
**Execution:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
CONFIG_FILE=./app.yml go run ./cmd/org/main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### collaborators migration
|
||||||
|
|
||||||
|
Export results of this query to .csv to migrate the repositories
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
r.name AS repo_name,
|
||||||
|
uo.name AS repo_owner,
|
||||||
|
u.name AS collaborator_user,
|
||||||
|
co.mode,
|
||||||
|
CASE
|
||||||
|
WHEN co.mode = 1 THEN 'read'
|
||||||
|
WHEN co.mode = 2 THEN 'write'
|
||||||
|
WHEN co.mode = 3 THEN 'admin'
|
||||||
|
WHEN co.mode = 4 THEN 'owner'
|
||||||
|
ELSE 'failed'
|
||||||
|
END AS permission
|
||||||
|
FROM collaboration co
|
||||||
|
INNER JOIN repository r ON r.id = co.repo_id
|
||||||
|
INNER JOIN user uo ON uo.id = r.owner_id
|
||||||
|
INNER JOIN user u ON u.id = co.user_id;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Execution:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
CONFIG_FILE=./app.yml go run ./cmd/collaborator/main.go
|
||||||
|
```
|
|
@ -0,0 +1,12 @@
|
||||||
|
gitea:
|
||||||
|
url: "https://gitea.com/api/v1"
|
||||||
|
api_key: "YOUR-API-KEY-HERE"
|
||||||
|
users:
|
||||||
|
default_password: "SOME-STRONG-PASSS"
|
||||||
|
csv_path: "/home/user/gogstea/csv/user.csv"
|
||||||
|
organizations:
|
||||||
|
csv_path: "/home/user/gogstea/csv/organizations.csv"
|
||||||
|
issues:
|
||||||
|
csv_path: "/home/user/gogstea/csv/issues.csv"
|
||||||
|
collaborators:
|
||||||
|
csv_path: "/home/user/gogstea/csv/collaborators.csv"
|
|
@ -0,0 +1,160 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.urkob.com/mcr-swiss/gogstea/kit/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type user struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Login string `json:"login"`
|
||||||
|
LoginName string `json:"login_name"`
|
||||||
|
SourceID int `json:"source_id"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
Language string `json:"language"`
|
||||||
|
IsAdmin bool `json:"is_admin"`
|
||||||
|
LastLogin time.Time `json:"last_login"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
Restricted bool `json:"restricted"`
|
||||||
|
Active bool `json:"active"`
|
||||||
|
ProhibitLogin bool `json:"prohibit_login"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Visibility string `json:"visibility"`
|
||||||
|
FollowersCount int `json:"followers_count"`
|
||||||
|
FollowingCount int `json:"following_count"`
|
||||||
|
StarredReposCount int `json:"starred_repos_count"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancel(signalContext(context.Background()))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cfgFile := os.Getenv("CONFIG_FILE")
|
||||||
|
if cfgFile == "" {
|
||||||
|
// Get root path
|
||||||
|
_, filename, _, _ := runtime.Caller(0)
|
||||||
|
cfgFile = path.Join(path.Dir(filename), "configs", "app.yml")
|
||||||
|
}
|
||||||
|
cfg, err := config.LoadConfig(cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
outputFile, err := os.Create(wd + "/output.txt")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
|
||||||
|
cli := http.DefaultClient
|
||||||
|
|
||||||
|
parsedURL, err := url.Parse(fmt.Sprintf(cfg.Gitea.URL + "/admin/users"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, parsedURL.String(), http.NoBody)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: see how to pass the admin token if so
|
||||||
|
req.Header.Add("Authorization", "token "+cfg.Gitea.ApiKey)
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
resp, err := cli.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
bts, _ := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
panic(fmt.Errorf("%d | %s: %w", resp.StatusCode, string(bts), err))
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var users []user
|
||||||
|
if err := json.Unmarshal(bts, &users); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, u := range users {
|
||||||
|
if u.Username == "mcradmin" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
parsedURL, err := url.Parse(fmt.Sprintf("%s/admin/users/%s", cfg.Gitea.URL, u.Username))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, parsedURL.String(), http.NoBody)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Authorization", "token "+cfg.Gitea.ApiKey)
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
resp, err := cli.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
bts, _ := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
if _, err := outputFile.WriteString(fmt.Sprintf("FAILED %d | %s | %d: %s\n", u.ID, u.Email, resp.StatusCode, string(bts))); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := outputFile.WriteString(fmt.Sprintf("DELETED %d | %s\n", u.ID, u.Email)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func signalContext(ctx context.Context) context.Context {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-sigs
|
||||||
|
signal.Stop(sigs)
|
||||||
|
close(sigs)
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/csv"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"slices"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"gitea.urkob.com/mcr-swiss/gogstea/internal/domain"
|
||||||
|
"gitea.urkob.com/mcr-swiss/gogstea/kit/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
var okStatuses = []int{
|
||||||
|
http.StatusOK,
|
||||||
|
http.StatusCreated,
|
||||||
|
http.StatusNoContent,
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancel(signalContext(context.Background()))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cfgFile := os.Getenv("CONFIG_FILE")
|
||||||
|
if cfgFile == "" {
|
||||||
|
// Get root path
|
||||||
|
_, filename, _, _ := runtime.Caller(0)
|
||||||
|
cfgFile = path.Join(path.Dir(filename), "configs", "app.yml")
|
||||||
|
}
|
||||||
|
cfg, err := config.LoadConfig(cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f1, err := os.Open(cfg.Collaborators.CSVPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f1.Close()
|
||||||
|
|
||||||
|
r1 := csv.NewReader(f1)
|
||||||
|
r1.Comma = ',' // Set the delimiter to comma
|
||||||
|
r1.LazyQuotes = true
|
||||||
|
r1.TrimLeadingSpace = false
|
||||||
|
|
||||||
|
outputFile, err := os.Create(wd + "/collab-output.txt")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
record, err := r1.Read()
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, io.EOF) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
break // Stop on EOF or other errors.
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := http.DefaultClient
|
||||||
|
|
||||||
|
parsedURL, err := url.Parse(fmt.Sprintf("%s/repos/%s/%s/collaborators/%s", cfg.Gitea.URL, record[1], record[0], record[2]))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// permission, err := strconv.Atoi(record[3])
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
collabReq := domain.CollaboratorCreateRequest{
|
||||||
|
Permission: record[4],
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, err := json.Marshal(collabReq)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPut, parsedURL.String(), bytes.NewReader(bts))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Authorization", "token "+cfg.Gitea.ApiKey)
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
resp, err := cli.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(okStatuses, resp.StatusCode) {
|
||||||
|
bts, _ := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
if _, err := outputFile.WriteString(fmt.Sprintf("ERROR COLLAB | %d | %s | %s\n", resp.StatusCode, parsedURL.String(), string(bts))); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, err = io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := outputFile.WriteString(fmt.Sprintf("OK COLLAB | %s \n", string(bts))); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func signalContext(ctx context.Context) context.Context {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-sigs
|
||||||
|
signal.Stop(sigs)
|
||||||
|
close(sigs)
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/csv"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"gitea.urkob.com/mcr-swiss/gogstea/internal/domain"
|
||||||
|
"gitea.urkob.com/mcr-swiss/gogstea/kit/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancel(signalContext(context.Background()))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cfgFile := os.Getenv("CONFIG_FILE")
|
||||||
|
if cfgFile == "" {
|
||||||
|
// Get root path
|
||||||
|
_, filename, _, _ := runtime.Caller(0)
|
||||||
|
cfgFile = path.Join(path.Dir(filename), "configs", "app.yml")
|
||||||
|
}
|
||||||
|
cfg, err := config.LoadConfig(cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f1, err := os.Open(cfg.Issues.CSVPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f1.Close()
|
||||||
|
|
||||||
|
r1 := csv.NewReader(f1)
|
||||||
|
r1.Comma = ',' // Set the delimiter to comma
|
||||||
|
r1.LazyQuotes = true
|
||||||
|
r1.TrimLeadingSpace = false
|
||||||
|
|
||||||
|
outputFile, err := os.Create(wd + "/comments-output.txt")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
record, err := r1.Read()
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, io.EOF) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
break // Stop on EOF or other errors.
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := http.DefaultClient
|
||||||
|
|
||||||
|
parsedURL, err := url.Parse(fmt.Sprintf("%s/repos/{owner}/{repo}/issues", cfg.Gitea.URL, record[1], record[0]))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
issue := domain.CommentCreateRequest{
|
||||||
|
Body: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, err := json.Marshal(issue)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, parsedURL.String(), bytes.NewReader(bts))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Authorization", "token "+cfg.Gitea.ApiKey)
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
resp, err := cli.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
|
||||||
|
bts, _ := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
if _, err := outputFile.WriteString(fmt.Sprintf("ERROR ISSUE | %d | %s\n", resp.StatusCode, string(bts))); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, err = io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := outputFile.WriteString(fmt.Sprintf("OK ISSUE | %s \n", string(bts))); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func signalContext(ctx context.Context) context.Context {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-sigs
|
||||||
|
signal.Stop(sigs)
|
||||||
|
close(sigs)
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/csv"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.urkob.com/mcr-swiss/gogstea/internal/domain"
|
||||||
|
"gitea.urkob.com/mcr-swiss/gogstea/kit/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancel(signalContext(context.Background()))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cfgFile := os.Getenv("CONFIG_FILE")
|
||||||
|
if cfgFile == "" {
|
||||||
|
// Get root path
|
||||||
|
_, filename, _, _ := runtime.Caller(0)
|
||||||
|
cfgFile = path.Join(path.Dir(filename), "configs", "app.yml")
|
||||||
|
}
|
||||||
|
cfg, err := config.LoadConfig(cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f1, err := os.Open(cfg.Issues.CSVPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f1.Close()
|
||||||
|
|
||||||
|
r1 := csv.NewReader(f1)
|
||||||
|
r1.Comma = ',' // Set the delimiter to comma
|
||||||
|
r1.LazyQuotes = true
|
||||||
|
r1.TrimLeadingSpace = false
|
||||||
|
|
||||||
|
outputFile, err := os.Create(wd + "/issues-output.txt")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
record, err := r1.Read()
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, io.EOF) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
break // Stop on EOF or other errors.
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := http.DefaultClient
|
||||||
|
|
||||||
|
parsedURL, err := url.Parse(fmt.Sprintf("%s/repos/%s/%s/issues", cfg.Gitea.URL, record[1], record[0]))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
isClosedInt, err := strconv.Atoi(record[7])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var isClosed bool
|
||||||
|
switch isClosedInt {
|
||||||
|
case 0:
|
||||||
|
isClosed = false
|
||||||
|
case 1:
|
||||||
|
isClosed = false
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("isclosed is %d", isClosedInt))
|
||||||
|
}
|
||||||
|
|
||||||
|
issue := domain.IssueCreateRequest{
|
||||||
|
Assignee: record[2],
|
||||||
|
Assignees: nil,
|
||||||
|
Body: record[4],
|
||||||
|
Closed: isClosed,
|
||||||
|
DueDate: time.Time{},
|
||||||
|
Labels: []int{},
|
||||||
|
Milestone: 0,
|
||||||
|
Ref: "",
|
||||||
|
Title: record[3],
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, err := json.Marshal(issue)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, parsedURL.String(), bytes.NewReader(bts))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Authorization", "token "+cfg.Gitea.ApiKey)
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
resp, err := cli.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
|
||||||
|
bts, _ := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
if _, err := outputFile.WriteString(fmt.Sprintf("ERROR ISSUE | %d | %s | %s\n", resp.StatusCode, parsedURL.String(), string(bts))); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, err = io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := outputFile.WriteString(fmt.Sprintf("OK ISSUE | %s \n", string(bts))); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func signalContext(ctx context.Context) context.Context {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-sigs
|
||||||
|
signal.Stop(sigs)
|
||||||
|
close(sigs)
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/csv"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"gitea.urkob.com/mcr-swiss/gogstea/internal/domain"
|
||||||
|
"gitea.urkob.com/mcr-swiss/gogstea/kit/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancel(signalContext(context.Background()))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cfgFile := os.Getenv("CONFIG_FILE")
|
||||||
|
if cfgFile == "" {
|
||||||
|
// Get root path
|
||||||
|
_, filename, _, _ := runtime.Caller(0)
|
||||||
|
cfgFile = path.Join(path.Dir(filename), "configs", "app.yml")
|
||||||
|
}
|
||||||
|
cfg, err := config.LoadConfig(cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f1, err := os.Open(cfg.Organizations.CSVPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f1.Close()
|
||||||
|
|
||||||
|
r1 := csv.NewReader(f1)
|
||||||
|
r1.Comma = ',' // Set the delimiter to comma
|
||||||
|
r1.LazyQuotes = true
|
||||||
|
r1.TrimLeadingSpace = false
|
||||||
|
|
||||||
|
outputFile, err := os.Create(wd + "/org-output.txt")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
record, err := r1.Read()
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, io.EOF) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
break // Stop on EOF or other errors.
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := http.DefaultClient
|
||||||
|
|
||||||
|
parsedURL, err := url.Parse(fmt.Sprintf("%s/admin/users/%s/orgs", cfg.Gitea.URL, record[2]))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyReq := domain.OrgCreateRequest{
|
||||||
|
Description: record[0],
|
||||||
|
Email: "",
|
||||||
|
FullName: "",
|
||||||
|
Location: "",
|
||||||
|
RepoAdminChangeTeamAccess: true,
|
||||||
|
Username: record[0],
|
||||||
|
Visibility: "private",
|
||||||
|
Website: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, err := json.Marshal(bodyReq)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, parsedURL.String(), bytes.NewReader(bts))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Authorization", "token "+cfg.Gitea.ApiKey)
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
resp, err := cli.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
bts, _ := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
if _, err := outputFile.WriteString(fmt.Sprintf("%s | %d: %s\n", record[3], resp.StatusCode, string(bts))); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bts, err = io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := outputFile.WriteString(fmt.Sprintf("OK REPO %s | %s | OK\n", bodyReq.Username, string(bts))); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func signalContext(ctx context.Context) context.Context {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-sigs
|
||||||
|
signal.Stop(sigs)
|
||||||
|
close(sigs)
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/csv"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"gitea.urkob.com/mcr-swiss/gogstea/internal/domain"
|
||||||
|
"gitea.urkob.com/mcr-swiss/gogstea/kit/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancel(signalContext(context.Background()))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cfgFile := os.Getenv("CONFIG_FILE")
|
||||||
|
if cfgFile == "" {
|
||||||
|
// Get root path
|
||||||
|
_, filename, _, _ := runtime.Caller(0)
|
||||||
|
cfgFile = path.Join(path.Dir(filename), "configs", "app.yml")
|
||||||
|
}
|
||||||
|
cfg, err := config.LoadConfig(cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 1 MIGRATE USERS
|
||||||
|
{
|
||||||
|
f1, err := os.Open(cfg.Users.CSVPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f1.Close()
|
||||||
|
|
||||||
|
r1 := csv.NewReader(f1)
|
||||||
|
r1.Comma = ',' // Set the delimiter to comma
|
||||||
|
r1.LazyQuotes = true
|
||||||
|
r1.TrimLeadingSpace = false
|
||||||
|
|
||||||
|
outputFile, err := os.Create(wd + "/output.txt")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
record, err := r1.Read()
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(err, io.EOF) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
break // Stop on EOF or other errors.
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := http.DefaultClient
|
||||||
|
|
||||||
|
parsedURL, err := url.Parse(fmt.Sprintf(cfg.Gitea.URL + "/admin/users"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
u := domain.UserCreateRequest{
|
||||||
|
Email: record[4],
|
||||||
|
FullName: record[2],
|
||||||
|
LoginName: record[1],
|
||||||
|
MustChangePassword: true,
|
||||||
|
Password: cfg.Users.DefaultPassword,
|
||||||
|
Restricted: false,
|
||||||
|
SendNotify: false,
|
||||||
|
SourceID: 0,
|
||||||
|
Username: record[1],
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, err := json.Marshal(u)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, parsedURL.String(), bytes.NewReader(bts))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Authorization", "token "+cfg.Gitea.ApiKey)
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
resp, err := cli.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
bts, _ := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
if _, err := outputFile.WriteString(fmt.Sprintf("%s | %d: %s\n", u.Email, resp.StatusCode, string(bts))); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
bts, err = io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var uResp domain.UserGet
|
||||||
|
if err := json.Unmarshal(bts, &uResp); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := outputFile.WriteString(fmt.Sprintf("USER %d | %s | OK\n", uResp.ID, uResp.Email)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func signalContext(ctx context.Context) context.Context {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-sigs
|
||||||
|
signal.Stop(sigs)
|
||||||
|
close(sigs)
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
module gitea.urkob.com/mcr-swiss/gogstea
|
||||||
|
|
||||||
|
go 1.23.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
go.uber.org/zap v1.27.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require go.uber.org/multierr v1.10.0 // indirect
|
|
@ -0,0 +1,16 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
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/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||||
|
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
|
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,5 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
type CollaboratorCreateRequest struct {
|
||||||
|
Permission string `json:"permission"`
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
type CommentCreateRequest struct {
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type IssueCreateRequest struct {
|
||||||
|
Assignee string `json:"assignee"`
|
||||||
|
Assignees []string `json:"assignees"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Closed bool `json:"closed"`
|
||||||
|
DueDate time.Time `json:"due_date"`
|
||||||
|
Labels []int `json:"labels"`
|
||||||
|
Milestone int `json:"milestone"`
|
||||||
|
Ref string `json:"ref"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
type OrgCreateRequest struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Visibility string `json:"visibility"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type UserCreateRequest struct {
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
LoginName string `json:"login_name"`
|
||||||
|
MustChangePassword bool `json:"must_change_password"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Restricted bool `json:"restricted"`
|
||||||
|
SendNotify bool `json:"send_notify"`
|
||||||
|
SourceID int `json:"source_id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserGet struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Login string `json:"login"`
|
||||||
|
LoginName string `json:"login_name"`
|
||||||
|
SourceID int `json:"source_id"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
Language string `json:"language"`
|
||||||
|
IsAdmin bool `json:"is_admin"`
|
||||||
|
LastLogin time.Time `json:"last_login"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
Restricted bool `json:"restricted"`
|
||||||
|
Active bool `json:"active"`
|
||||||
|
ProhibitLogin bool `json:"prohibit_login"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Visibility string `json:"visibility"`
|
||||||
|
FollowersCount int `json:"followers_count"`
|
||||||
|
FollowingCount int `json:"following_count"`
|
||||||
|
StarredReposCount int `json:"starred_repos_count"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Gitea struct {
|
||||||
|
URL string `yaml:"url"`
|
||||||
|
ApiKey string `yaml:"api_key"`
|
||||||
|
} `yaml:"gitea"`
|
||||||
|
Users struct {
|
||||||
|
DefaultPassword string `yaml:"default_password"`
|
||||||
|
CSVPath string `yaml:"csv_path"`
|
||||||
|
} `yaml:"users"`
|
||||||
|
Organizations struct {
|
||||||
|
CSVPath string `yaml:"csv_path"`
|
||||||
|
} `yaml:"organizations"`
|
||||||
|
Issues struct {
|
||||||
|
CSVPath string `yaml:"csv_path"`
|
||||||
|
} `yaml:"issues"`
|
||||||
|
Collaborators struct {
|
||||||
|
CSVPath string `yaml:"csv_path"`
|
||||||
|
} `yaml:"collaborators"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadConfig(path string) (*Config, error) {
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var config Config
|
||||||
|
if err := yaml.Unmarshal(data, &config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewZapCfg(dev bool) zap.Config {
|
||||||
|
prodConfig := zap.NewProductionConfig()
|
||||||
|
if dev {
|
||||||
|
prodConfig = zap.NewDevelopmentConfig()
|
||||||
|
}
|
||||||
|
return prodConfig
|
||||||
|
}
|
Loading…
Reference in New Issue