Compare commits

..

No commits in common. "master" and "1.0.2" have entirely different histories.

25 changed files with 366 additions and 283 deletions

View File

@ -1,74 +1,89 @@
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: build-linux name: cleanup-before
steps: steps:
- name: build-linux-amd64 - name: clean
image: golang image: alpine
commands: commands:
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES - rm -rf /build/*
environment: volumes:
GOOS: linux - name: build
GOARCH: amd64 path: /build
GOOPTIONS: -mod=vendor
SRCFILES: cmd/g2g/*.go
PROJECTNAME: g2g
when: when:
event: event: tag
exclude:
- tag volumes:
- name: build-linux-arm64 - name: build
image: golang host:
commands: path: /tmp/g2g/build
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES
environment:
GOOS: linux
GOARCH: arm64
GOOPTIONS: -mod=vendor
SRCFILES: cmd/g2g/*.go
PROJECTNAME: g2g
when:
event:
exclude:
- tag
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: gitea-release-linux name: default-linux-amd64
steps: steps:
- name: build-linux-amd64 - name: build
image: golang image: golang:1.17
commands: commands:
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES - ./ci-build.sh build
- tar -czvf $PROJECTNAME-$DRONE_TAG-$GOOS-$GOARCH.tar.gz $PROJECTNAME
- echo $PROJECTNAME $DRONE_TAG > VERSION
environment: environment:
GOOS: linux GOOS: linux
GOARCH: amd64 GOARCH: amd64
GOOPTIONS: -mod=vendor volumes:
SRCFILES: cmd/g2g/*.go - name: build
PROJECTNAME: g2g path: /build
when:
event: volumes:
- tag - name: build
- name: build-linux-arm64 host:
image: golang path: /tmp/g2g/build
depends_on:
- cleanup-before
---
kind: pipeline
type: docker
name: default-linux-arm64
steps:
- name: build
image: golang:1.17
commands: commands:
- go build -o $PROJECTNAME $GOOPTIONS $SRCFILES - ./ci-build.sh build
- tar -czvf $PROJECTNAME-$DRONE_TAG-$GOOS-$GOARCH.tar.gz $PROJECTNAME
- echo $PROJECTNAME $DRONE_TAG > VERSION
environment: environment:
GOOS: linux GOOS: linux
GOARCH: arm64 GOARCH: arm64
GOOPTIONS: -mod=vendor volumes:
SRCFILES: cmd/g2g/*.go - name: build
PROJECTNAME: g2g path: /build
volumes:
- name: build
host:
path: /tmp/g2g/build
depends_on:
- cleanup-before
---
kind: pipeline
type: docker
name: gitea-release
steps:
- name: move
image: alpine
commands:
- mv build/* ./
volumes:
- name: build
path: /drone/src/build
when: when:
event: event: tag
- tag
- name: release - name: release
image: plugins/gitea-release image: plugins/gitea-release
settings: settings:
@ -80,6 +95,50 @@ steps:
- sha256 - sha256
- sha512 - sha512
title: VERSION title: VERSION
volumes:
- name: build
path: /drone/src/build
when: when:
event: event: tag
- tag - name: ls
image: alpine
commands:
- find .
volumes:
- name: build
path: /drone/src/build
when:
event: tag
volumes:
- name: build
host:
path: /tmp/g2g/build
depends_on:
- default-linux-amd64
- default-linux-arm64
---
kind: pipeline
type: docker
name: cleanup-after
steps:
- name: clean
image: alpine
commands:
- rm -rf /build/*
volumes:
- name: build
path: /build
when:
event: tag
volumes:
- name: build
host:
path: /tmp/g2g/build
depends_on:
- gitea-release

17
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
// 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": "auto",
"program": "${fileDirname}",
"env": {},
"args": []
}
]
}

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"go.formatTool": "goimports"
}

19
Makefile Normal file
View File

@ -0,0 +1,19 @@
# g2g Makefile
GOCMD=go
GOBUILDCMD=${GOCMD} build
GOOPTIONS=-mod=vendor -ldflags="-s -w"
VERSION := $(shell cat ./VERSION)
RMCMD=rm
BINNAME=g2g
SRCFILES=cmd/g2g/*.go
all: build
build:
${GOBUILDCMD} ${GOOPTIONS} ${SRCFILES}
clean:
${RMCMD} -f ${BINNAME}

View File

@ -21,14 +21,17 @@ make
request_timeout=1200s request_timeout=1200s
threads=4 threads=4
github_stars_pages="https://api.github.com/users/%s/starred?page=%d"
github_max_per_page=500
github_page_num=3
github_auth_username="user" github_auth_username="user"
github_auth_password="pass" github_auth_password="pass"
gitea_username="user" gitea_username="user"
gitea_dest_username="user_or_org" gitea_dest_username="user_or_org"
gitea_repo_url_tmpl="https://git.example.com/api/v1/repos/%s/%s" gitea_repo_url_tmpl="https://gogs.example.com/api/v1/repos/%s/%s"
gitea_orgs_url_tmpl="https://git.example.com/api/v1/orgs/%s" gitea_orgs_url_tmpl="https://git.paulbsd.com/api/v1/orgs/%s"
gitea_migrate_url="https://git.example.com/api/v1/repos/migrate" gitea_migrate_url="https://gogs.example.com/api/v1/repos/migrate"
gitea_auth_token="token xxxx" gitea_auth_token="token xxxx"
gitea_mirror=true gitea_mirror=true
``` ```
@ -46,7 +49,7 @@ gitea_mirror=true
## License ## License
```text ```text
Copyright (c) 2020, 2021 PaulBSD Copyright (c) 2020 PaulBSD
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@ -1 +1 @@
1.0.4 0.9

62
ci-build.sh Executable file
View File

@ -0,0 +1,62 @@
#!/bin/bash
set -e
PROJECTNAME=g2g
RELEASENAME=${PROJECTNAME}
VERSION="0"
GOOPTIONS="-mod=vendor"
SRCFILES=cmd/g2g/*.go
build() {
echo "Begin of build"
if [[ ! -z $DRONE_TAG ]]
then
echo "Drone tag set, let's do a release"
VERSION=$DRONE_TAG
echo "${PROJECTNAME} ${VERSION}" > /build/VERSION
elif [[ ! -z $DRONE_TAG ]]
then
echo "Drone not set, let's only do a build"
VERSION=$DRONE_COMMIT
fi
if [[ ! -z $VERSION && ! -z $GOOS && ! -z $GOARCH ]]
then
echo "Let's set a release name"
RELEASENAME=${PROJECTNAME}-${VERSION}-${GOOS}-${GOARCH}
fi
echo "Building project"
go build -o ${PROJECTNAME} ${GOOPTIONS} ${SRCFILES}
if [[ ! -z $DRONE_TAG ]]
then
echo "Let's make archives"
mkdir -p /build
tar -czvf /build/${RELEASENAME}.tar.gz ${PROJECTNAME}
fi
echo "Removing binary file"
rm ${PROJECTNAME}
echo "End of build"
}
clean() {
rm -rf $RELEASEDIR
}
case $1 in
"build")
build
;;
"clean")
clean
;;
*)
echo "No options choosen"
exit 1
;;
esac

View File

@ -2,13 +2,16 @@
request_timeout=1200s request_timeout=1200s
threads=4 threads=4
github_stars_pages="https://api.github.com/users/%s/starred?page=%d"
github_max_per_page=500
github_page_num=3
github_auth_username="user" github_auth_username="user"
github_auth_password="pass" github_auth_password="pass"
gitea_username="user" gitea_username="user"
gitea_dest_username="user_or_org" gitea_dest_username="user_or_org"
gitea_repo_url_tmpl="https://git.example.com/api/v1/repos/%s/%s" gitea_repo_url_tmpl="https://gogs.example.com/api/v1/repos/%s/%s"
gitea_orgs_url_tmpl="https://git.example.com/api/v1/orgs/%s" gitea_orgs_url_tmpl="https://gogs.example.com/api/v1/orgs/%s"
gitea_migrate_url="https://git.example.com/api/v1/repos/migrate" gitea_migrate_url="https://gogs.example.com/api/v1/repos/migrate"
gitea_auth_token="token xxxx" gitea_auth_token="token xxxx"
gitea_mirror=true gitea_mirror=true

11
go.mod
View File

@ -1,7 +1,10 @@
module git.paulbsd.com/paulbsd/g2g module git.paulbsd.com/paulbsd/g2g
go 1.20 go 1.17
require gopkg.in/ini.v1 v1.67.0 require (
github.com/gopherjs/gopherjs v0.0.0-20210406100015-1e088ea4ee04 // indirect
require github.com/stretchr/testify v1.7.0 // indirect github.com/smartystreets/assertions v1.2.0 // indirect
github.com/smartystreets/goconvey v1.6.4 // indirect
gopkg.in/ini.v1 v1.62.0
)

29
go.sum
View File

@ -1,12 +1,17 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gopherjs/gopherjs v0.0.0-20210406100015-1e088ea4ee04 h1:Enykqupm0u6qiUZAc+SiFkMJVqt4o8knNcKJu8NdlJ0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/gopherjs/gopherjs v0.0.0-20210406100015-1e088ea4ee04/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

View File

@ -32,6 +32,8 @@ func (config *Config) GetConfig() error {
type Config struct { type Config struct {
G2gRequestTimeout time.Duration `ini:"g2g_request_timeout"` G2gRequestTimeout time.Duration `ini:"g2g_request_timeout"`
G2gThreads int `ini:"g2g_threads"` G2gThreads int `ini:"g2g_threads"`
GitHubMaxPerPage int `ini:"github_max_per_page"`
GitHubPageNum int `ini:"github_page_num"`
GitHubAuthUsername string `ini:"github_auth_username"` GitHubAuthUsername string `ini:"github_auth_username"`
GitHubAuthPassword string `ini:"github_auth_password"` GitHubAuthPassword string `ini:"github_auth_password"`
GitHubContentType string GitHubContentType string

View File

@ -5,7 +5,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io/ioutil"
"log" "log"
"net/http" "net/http"
"sync" "sync"
@ -17,11 +17,10 @@ import (
func GetReposFromGitHub(config *config.Config) ([]GitHubRepo, error) { func GetReposFromGitHub(config *config.Config) ([]GitHubRepo, error) {
var repopartiallist []GitHubRepo var repopartiallist []GitHubRepo
var repofulllist []GitHubRepo var repofulllist []GitHubRepo
var pagenum = 1
fmt.Println("Getting GitHub starred repos") fmt.Println("Getting GitHub starred repos")
for { for num := 1; num <= config.GitHubPageNum; num++ {
url := fmt.Sprintf("https://api.github.com/users/%s/starred?page=%d", config.GitHubAuthUsername, pagenum) url := fmt.Sprintf("https://api.github.com/users/%s/starred?per_page=%d&page=%d", config.GitHubAuthUsername, config.GitHubMaxPerPage, num)
fmt.Println(url) fmt.Println(url)
resp, err := GetGitHubResponse(config, url) resp, err := GetGitHubResponse(config, url)
@ -29,7 +28,7 @@ func GetReposFromGitHub(config *config.Config) ([]GitHubRepo, error) {
return nil, err return nil, err
} }
respbody, err := io.ReadAll(resp.Body) respbody, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -39,17 +38,12 @@ func GetReposFromGitHub(config *config.Config) ([]GitHubRepo, error) {
return nil, err return nil, err
} }
if len(repopartiallist) == 0 {
break
}
for _, elem := range repopartiallist { for _, elem := range repopartiallist {
repofulllist = append(repofulllist, elem) repofulllist = append(repofulllist, elem)
} }
pagenum++
} }
fmt.Printf("%d repositories fetched from Github\n", len(repofulllist)) fmt.Println(fmt.Sprintf("%d repositories fetched from Github", len(repofulllist)))
return repofulllist, nil return repofulllist, nil
} }
@ -70,7 +64,7 @@ func GetGitHubResponse(config *config.Config, url string) (*http.Response, error
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("error on status code %s", resp.Status) return nil, fmt.Errorf("Error on status code %s", resp.Status)
} }
return resp, nil return resp, nil
@ -83,9 +77,6 @@ func CheckGiteaExistingRepo(config *config.Config, repo GitHubRepo) (bool, error
gitearepourl := fmt.Sprintf(config.GiteaRepoURLTmpl, config.GiteaDestUsername, repo.Name) gitearepourl := fmt.Sprintf(config.GiteaRepoURLTmpl, config.GiteaDestUsername, repo.Name)
req, err := http.NewRequest("GET", gitearepourl, nil) req, err := http.NewRequest("GET", gitearepourl, nil)
if err != nil {
return false, err
}
req.Header.Set("Authorization", config.GiteaAuthToken) req.Header.Set("Authorization", config.GiteaAuthToken)
client := &http.Client{} client := &http.Client{}
@ -99,7 +90,7 @@ func CheckGiteaExistingRepo(config *config.Config, repo GitHubRepo) (bool, error
} else if resp.StatusCode == http.StatusNotFound { } else if resp.StatusCode == http.StatusNotFound {
isExists = false isExists = false
} else { } else {
return false, fmt.Errorf("can't determine error, cancelling, error %s in gitea webservice", resp.Status) return false, fmt.Errorf("Can't determine error, cancelling, error %s in gitea webservice", resp.Status)
} }
return isExists, nil return isExists, nil
@ -125,18 +116,15 @@ func GetGiteaUserUID(config *config.Config) error {
} }
if resp.StatusCode != 200 { if resp.StatusCode != 200 {
err = errors.New("error invoking gitea webservice") err = errors.New("Error invoking gitea webservice")
return err return err
} }
respbody, err := io.ReadAll(resp.Body) respbody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
err = json.Unmarshal(respbody, &giteaorg) err = json.Unmarshal(respbody, &giteaorg)
if err != nil { if err != nil {
err = errors.New("failed to parse user ID from gitea webservice, check auth") err = errors.New("Failed to parse user ID from gitea webservice, check auth")
return err return err
} }
@ -170,21 +158,46 @@ func MigrateReposToGitea(config *config.Config, wg *sync.WaitGroup, jobs chan Gi
for { for {
elem, more := <-jobs elem, more := <-jobs
if more { if more {
client := &http.Client{}
existingrepo, err := CheckGiteaExistingRepo(config, elem) existingrepo, err := CheckGiteaExistingRepo(config, elem)
if err != nil { if err != nil {
return err return err
} }
if !existingrepo { if !existingrepo {
err = MigrateRepo(elem, config) repo := GiteaRepo{
UID: config.GiteaUID,
RepoName: elem.Name,
Mirror: true,
CloneAddr: elem.CloneURL}
jsondata, err := json.Marshal(repo)
req, err := http.NewRequest("POST", config.GiteaMigrateURL, bytes.NewBufferString(string(jsondata)))
if err != nil { if err != nil {
return err
}
req.Header.Add("Content-Type", "application/json")
req.Header.Set("Authorization", config.GiteaAuthToken)
client.Timeout = config.G2gRequestTimeout
fmt.Println(fmt.Sprintf("Migrating repo %s to gitea rest api", elem.Name))
resp, err := client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != 201 {
err = fmt.Errorf("Error when migrating repo %s to gitea with status code %d on gitea webservice", elem.Name, resp.StatusCode)
log.Println(err) log.Println(err)
continue
} }
} else { } else {
fmt.Printf("Not migrating, %s exists in gitea\n", elem.Name) fmt.Println(fmt.Sprintf("Not migrating, %s exists in gitea", elem.Name))
} }
} else { } else {
fmt.Printf("All repo migrated on thread num %d\n", thr) fmt.Println(fmt.Sprintf("All repo migrated on thread num %d", thr))
wg.Done() wg.Done()
done <- true done <- true
return nil return nil
@ -192,40 +205,6 @@ func MigrateReposToGitea(config *config.Config, wg *sync.WaitGroup, jobs chan Gi
} }
} }
func MigrateRepo(elem GitHubRepo, config *config.Config) (err error) {
client := &http.Client{}
repo := GiteaRepo{
UID: config.GiteaUID,
RepoName: elem.Name,
Mirror: true,
CloneAddr: elem.CloneURL}
jsondata, err := json.Marshal(repo)
if err != nil {
return err
}
req, err := http.NewRequest("POST", config.GiteaMigrateURL, bytes.NewBufferString(string(jsondata)))
if err != nil {
return err
}
req.Header.Add("Content-Type", "application/json")
req.Header.Set("Authorization", config.GiteaAuthToken)
client.Timeout = config.G2gRequestTimeout
fmt.Printf("Migrating repo %s to gitea rest api\n", elem.Name)
resp, err := client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != 201 {
err = fmt.Errorf("error when migrating repo %s to gitea with status code %d on gitea webservice", elem.Name, resp.StatusCode)
log.Println(err)
}
return err
}
// GitHubRepo githubrepo struct // GitHubRepo githubrepo struct
type GitHubRepo struct { type GitHubRepo struct {
Name string `json:"name"` Name string `json:"name"`

12
vendor/gopkg.in/ini.v1/.editorconfig generated vendored
View File

@ -1,12 +0,0 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*_test.go]
trim_trailing_whitespace = false

1
vendor/gopkg.in/ini.v1/.gitignore generated vendored
View File

@ -4,4 +4,3 @@ ini.sublime-workspace
testdata/conf_reflect.ini testdata/conf_reflect.ini
.idea .idea
/.vscode /.vscode
.DS_Store

27
vendor/gopkg.in/ini.v1/.golangci.yml generated vendored
View File

@ -1,27 +0,0 @@
linters-settings:
staticcheck:
checks: [
"all",
"-SA1019" # There are valid use cases of strings.Title
]
nakedret:
max-func-lines: 0 # Disallow any unnamed return statement
linters:
enable:
- deadcode
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- structcheck
- typecheck
- unused
- varcheck
- nakedret
- gofmt
- rowserrcheck
- unconvert
- goimports
- unparam

4
vendor/gopkg.in/ini.v1/README.md generated vendored
View File

@ -1,6 +1,6 @@
# INI # INI
[![GitHub Workflow Status](https://img.shields.io/github/checks-status/go-ini/ini/main?logo=github&style=for-the-badge)](https://github.com/go-ini/ini/actions?query=branch%3Amain) [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/go-ini/ini/Go?logo=github&style=for-the-badge)](https://github.com/go-ini/ini/actions?query=workflow%3AGo)
[![codecov](https://img.shields.io/codecov/c/github/go-ini/ini/master?logo=codecov&style=for-the-badge)](https://codecov.io/gh/go-ini/ini) [![codecov](https://img.shields.io/codecov/c/github/go-ini/ini/master?logo=codecov&style=for-the-badge)](https://codecov.io/gh/go-ini/ini)
[![GoDoc](https://img.shields.io/badge/GoDoc-Reference-blue?style=for-the-badge&logo=go)](https://pkg.go.dev/github.com/go-ini/ini?tab=doc) [![GoDoc](https://img.shields.io/badge/GoDoc-Reference-blue?style=for-the-badge&logo=go)](https://pkg.go.dev/github.com/go-ini/ini?tab=doc)
[![Sourcegraph](https://img.shields.io/badge/view%20on-Sourcegraph-brightgreen.svg?style=for-the-badge&logo=sourcegraph)](https://sourcegraph.com/github.com/go-ini/ini) [![Sourcegraph](https://img.shields.io/badge/view%20on-Sourcegraph-brightgreen.svg?style=for-the-badge&logo=sourcegraph)](https://sourcegraph.com/github.com/go-ini/ini)
@ -24,7 +24,7 @@ Package ini provides INI file read and write functionality in Go.
## Installation ## Installation
The minimum requirement of Go is **1.13**. The minimum requirement of Go is **1.6**.
```sh ```sh
$ go get gopkg.in/ini.v1 $ go get gopkg.in/ini.v1

9
vendor/gopkg.in/ini.v1/codecov.yml generated vendored
View File

@ -4,13 +4,6 @@ coverage:
project: project:
default: default:
threshold: 1% threshold: 1%
informational: true
patch:
defualt:
only_pulls: true
informational: true
comment: comment:
layout: 'diff' layout: 'diff, files'
github_checks: false

View File

@ -14,9 +14,12 @@
package ini package ini
var ( const (
// Deprecated: Use "DefaultSection" instead. // Deprecated: Use "DefaultSection" instead.
DEFAULT_SECTION = DefaultSection DEFAULT_SECTION = DefaultSection
)
var (
// Deprecated: AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE. // Deprecated: AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE.
AllCapsUnderscore = SnackCase AllCapsUnderscore = SnackCase
) )

15
vendor/gopkg.in/ini.v1/error.go generated vendored
View File

@ -32,18 +32,3 @@ func IsErrDelimiterNotFound(err error) bool {
func (err ErrDelimiterNotFound) Error() string { func (err ErrDelimiterNotFound) Error() string {
return fmt.Sprintf("key-value delimiter not found: %s", err.Line) return fmt.Sprintf("key-value delimiter not found: %s", err.Line)
} }
// ErrEmptyKeyName indicates the error type of no key name is found which there should be one.
type ErrEmptyKeyName struct {
Line string
}
// IsErrEmptyKeyName returns true if the given error is an instance of ErrEmptyKeyName.
func IsErrEmptyKeyName(err error) bool {
_, ok := err.(ErrEmptyKeyName)
return ok
}
func (err ErrEmptyKeyName) Error() string {
return fmt.Sprintf("empty key name: %s", err.Line)
}

44
vendor/gopkg.in/ini.v1/file.go generated vendored
View File

@ -142,12 +142,6 @@ func (f *File) GetSection(name string) (*Section, error) {
return secs[0], err return secs[0], err
} }
// HasSection returns true if the file contains a section with given name.
func (f *File) HasSection(name string) bool {
section, _ := f.GetSection(name)
return section != nil
}
// SectionsByName returns all sections with given name. // SectionsByName returns all sections with given name.
func (f *File) SectionsByName(name string) ([]*Section, error) { func (f *File) SectionsByName(name string) ([]*Section, error) {
if len(name) == 0 { if len(name) == 0 {
@ -174,9 +168,8 @@ func (f *File) SectionsByName(name string) ([]*Section, error) {
func (f *File) Section(name string) *Section { func (f *File) Section(name string) *Section {
sec, err := f.GetSection(name) sec, err := f.GetSection(name)
if err != nil { if err != nil {
if name == "" { // Note: It's OK here because the only possible error is empty section name,
name = DefaultSection // but if it's empty, this piece of code won't be executed.
}
sec, _ = f.NewSection(name) sec, _ = f.NewSection(name)
return sec return sec
} }
@ -342,7 +335,6 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
// Use buffer to make sure target is safe until finish encoding. // Use buffer to make sure target is safe until finish encoding.
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
lastSectionIdx := len(f.sectionList) - 1
for i, sname := range f.sectionList { for i, sname := range f.sectionList {
sec := f.SectionWithIndex(sname, f.sectionIndexes[i]) sec := f.SectionWithIndex(sname, f.sectionIndexes[i])
if len(sec.Comment) > 0 { if len(sec.Comment) > 0 {
@ -372,13 +364,12 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
} }
} }
isLastSection := i == lastSectionIdx
if sec.isRawSection { if sec.isRawSection {
if _, err := buf.WriteString(sec.rawBody); err != nil { if _, err := buf.WriteString(sec.rawBody); err != nil {
return nil, err return nil, err
} }
if PrettySection && !isLastSection { if PrettySection {
// Put a line between sections // Put a line between sections
if _, err := buf.WriteString(LineBreak); err != nil { if _, err := buf.WriteString(LineBreak); err != nil {
return nil, err return nil, err
@ -444,14 +435,16 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
kname = `"""` + kname + `"""` kname = `"""` + kname + `"""`
} }
writeKeyValue := func(val string) (bool, error) { for _, val := range key.ValueWithShadows() {
if _, err := buf.WriteString(kname); err != nil { if _, err := buf.WriteString(kname); err != nil {
return false, err return nil, err
} }
if key.isBooleanType { if key.isBooleanType {
buf.WriteString(LineBreak) if kname != sec.keyList[len(sec.keyList)-1] {
return true, nil buf.WriteString(LineBreak)
}
continue KeyList
} }
// Write out alignment spaces before "=" sign // Write out alignment spaces before "=" sign
@ -468,27 +461,10 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
val = `"` + val + `"` val = `"` + val + `"`
} }
if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil { if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil {
return false, err
}
return false, nil
}
shadows := key.ValueWithShadows()
if len(shadows) == 0 {
if _, err := writeKeyValue(""); err != nil {
return nil, err return nil, err
} }
} }
for _, val := range shadows {
exitLoop, err := writeKeyValue(val)
if err != nil {
return nil, err
} else if exitLoop {
continue KeyList
}
}
for _, val := range key.nestedValues { for _, val := range key.nestedValues {
if _, err := buf.WriteString(indent + " " + val + LineBreak); err != nil { if _, err := buf.WriteString(indent + " " + val + LineBreak); err != nil {
return nil, err return nil, err
@ -496,7 +472,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
} }
} }
if PrettySection && !isLastSection { if PrettySection {
// Put a line between sections // Put a line between sections
if _, err := buf.WriteString(LineBreak); err != nil { if _, err := buf.WriteString(LineBreak); err != nil {
return nil, err return nil, err

12
vendor/gopkg.in/ini.v1/ini.go generated vendored
View File

@ -1,3 +1,5 @@
// +build go1.6
// Copyright 2014 Unknwon // Copyright 2014 Unknwon
// //
// Licensed under the Apache License, Version 2.0 (the "License"): you may // Licensed under the Apache License, Version 2.0 (the "License"): you may
@ -23,15 +25,15 @@ import (
) )
const ( const (
// DefaultSection is the name of default section. You can use this constant or the string literal.
// In most of cases, an empty string is all you need to access the section.
DefaultSection = "DEFAULT"
// Maximum allowed depth when recursively substituing variable names. // Maximum allowed depth when recursively substituing variable names.
depthValues = 99 depthValues = 99
) )
var ( var (
// DefaultSection is the name of default section. You can use this var or the string literal.
// In most of cases, an empty string is all you need to access the section.
DefaultSection = "DEFAULT"
// LineBreak is the delimiter to determine or compose a new line. // LineBreak is the delimiter to determine or compose a new line.
// This variable will be changed to "\r\n" automatically on Windows at package init time. // This variable will be changed to "\r\n" automatically on Windows at package init time.
LineBreak = "\n" LineBreak = "\n"
@ -123,8 +125,6 @@ type LoadOptions struct {
ReaderBufferSize int ReaderBufferSize int
// AllowNonUniqueSections indicates whether to allow sections with the same name multiple times. // AllowNonUniqueSections indicates whether to allow sections with the same name multiple times.
AllowNonUniqueSections bool AllowNonUniqueSections bool
// AllowDuplicateShadowValues indicates whether values for shadowed keys should be deduplicated.
AllowDuplicateShadowValues bool
} }
// DebugFunc is the type of function called to log parse events. // DebugFunc is the type of function called to log parse events.

36
vendor/gopkg.in/ini.v1/key.go generated vendored
View File

@ -54,16 +54,14 @@ func (k *Key) addShadow(val string) error {
return errors.New("cannot add shadow to auto-increment or boolean key") return errors.New("cannot add shadow to auto-increment or boolean key")
} }
if !k.s.f.options.AllowDuplicateShadowValues { // Deduplicate shadows based on their values.
// Deduplicate shadows based on their values. if k.value == val {
if k.value == val { return nil
}
for i := range k.shadows {
if k.shadows[i].value == val {
return nil return nil
} }
for i := range k.shadows {
if k.shadows[i].value == val {
return nil
}
}
} }
shadow := newKey(k.s, k.name, val) shadow := newKey(k.s, k.name, val)
@ -110,24 +108,15 @@ func (k *Key) Value() string {
return k.value return k.value
} }
// ValueWithShadows returns raw values of key and its shadows if any. Shadow // ValueWithShadows returns raw values of key and its shadows if any.
// keys with empty values are ignored from the returned list.
func (k *Key) ValueWithShadows() []string { func (k *Key) ValueWithShadows() []string {
if len(k.shadows) == 0 { if len(k.shadows) == 0 {
if k.value == "" {
return []string{}
}
return []string{k.value} return []string{k.value}
} }
vals := make([]string, len(k.shadows)+1)
vals := make([]string, 0, len(k.shadows)+1) vals[0] = k.value
if k.value != "" { for i := range k.shadows {
vals = append(vals, k.value) vals[i+1] = k.shadows[i].value
}
for _, s := range k.shadows {
if s.value != "" {
vals = append(vals, s.value)
}
} }
return vals return vals
} }
@ -792,8 +781,10 @@ func (k *Key) parseUint64s(strs []string, addInvalid, returnOnInvalid bool) ([]u
return vals, err return vals, err
} }
type Parser func(str string) (interface{}, error) type Parser func(str string) (interface{}, error)
// parseTimesFormat transforms strings to times in given format. // parseTimesFormat transforms strings to times in given format.
func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) { func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) {
vals := make([]time.Time, 0, len(strs)) vals := make([]time.Time, 0, len(strs))
@ -810,6 +801,7 @@ func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnO
return vals, err return vals, err
} }
// doParse transforms strings to different types // doParse transforms strings to different types
func (k *Key) doParse(strs []string, addInvalid, returnOnInvalid bool, parser Parser) ([]interface{}, error) { func (k *Key) doParse(strs []string, addInvalid, returnOnInvalid bool, parser Parser) ([]interface{}, error) {
vals := make([]interface{}, 0, len(strs)) vals := make([]interface{}, 0, len(strs))

47
vendor/gopkg.in/ini.v1/parser.go generated vendored
View File

@ -131,7 +131,7 @@ func readKeyName(delimiters string, in []byte) (string, int, error) {
// Check if key name surrounded by quotes. // Check if key name surrounded by quotes.
var keyQuote string var keyQuote string
if line[0] == '"' { if line[0] == '"' {
if len(line) > 6 && line[0:3] == `"""` { if len(line) > 6 && string(line[0:3]) == `"""` {
keyQuote = `"""` keyQuote = `"""`
} else { } else {
keyQuote = `"` keyQuote = `"`
@ -164,10 +164,6 @@ func readKeyName(delimiters string, in []byte) (string, int, error) {
if endIdx < 0 { if endIdx < 0 {
return "", -1, ErrDelimiterNotFound{line} return "", -1, ErrDelimiterNotFound{line}
} }
if endIdx == 0 {
return "", -1, ErrEmptyKeyName{line}
}
return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil
} }
@ -236,7 +232,7 @@ func (p *parser) readValue(in []byte, bufferSize int) (string, error) {
} }
var valQuote string var valQuote string
if len(line) > 3 && line[0:3] == `"""` { if len(line) > 3 && string(line[0:3]) == `"""` {
valQuote = `"""` valQuote = `"""`
} else if line[0] == '`' { } else if line[0] == '`' {
valQuote = "`" valQuote = "`"
@ -293,8 +289,12 @@ func (p *parser) readValue(in []byte, bufferSize int) (string, error) {
hasSurroundedQuote(line, '"')) && !p.options.PreserveSurroundedQuote { hasSurroundedQuote(line, '"')) && !p.options.PreserveSurroundedQuote {
line = line[1 : len(line)-1] line = line[1 : len(line)-1]
} else if len(valQuote) == 0 && p.options.UnescapeValueCommentSymbols { } else if len(valQuote) == 0 && p.options.UnescapeValueCommentSymbols {
line = strings.ReplaceAll(line, `\;`, ";") if strings.Contains(line, `\;`) {
line = strings.ReplaceAll(line, `\#`, "#") line = strings.Replace(line, `\;`, ";", -1)
}
if strings.Contains(line, `\#`) {
line = strings.Replace(line, `\#`, "#", -1)
}
} else if p.options.AllowPythonMultilineValues && lastChar == '\n' { } else if p.options.AllowPythonMultilineValues && lastChar == '\n' {
return p.readPythonMultilines(line, bufferSize) return p.readPythonMultilines(line, bufferSize)
} }
@ -306,9 +306,15 @@ func (p *parser) readPythonMultilines(line string, bufferSize int) (string, erro
parserBufferPeekResult, _ := p.buf.Peek(bufferSize) parserBufferPeekResult, _ := p.buf.Peek(bufferSize)
peekBuffer := bytes.NewBuffer(parserBufferPeekResult) peekBuffer := bytes.NewBuffer(parserBufferPeekResult)
indentSize := 0
for { for {
peekData, peekErr := peekBuffer.ReadBytes('\n') peekData, peekErr := peekBuffer.ReadBytes('\n')
if peekErr != nil && peekErr != io.EOF { if peekErr != nil {
if peekErr == io.EOF {
p.debug("readPythonMultilines: io.EOF, peekData: %q, line: %q", string(peekData), line)
return line, nil
}
p.debug("readPythonMultilines: failed to peek with error: %v", peekErr) p.debug("readPythonMultilines: failed to peek with error: %v", peekErr)
return "", peekErr return "", peekErr
} }
@ -327,6 +333,19 @@ func (p *parser) readPythonMultilines(line string, bufferSize int) (string, erro
return line, nil return line, nil
} }
// Determine indent size and line prefix.
currentIndentSize := len(peekMatches[1])
if indentSize < 1 {
indentSize = currentIndentSize
p.debug("readPythonMultilines: indent size is %d", indentSize)
}
// Make sure each line is indented at least as far as first line.
if currentIndentSize < indentSize {
p.debug("readPythonMultilines: end of value, current indent: %d, expected indent: %d, line: %q", currentIndentSize, indentSize, line)
return line, nil
}
// Advance the parser reader (buffer) in-sync with the peek buffer. // Advance the parser reader (buffer) in-sync with the peek buffer.
_, err := p.buf.Discard(len(peekData)) _, err := p.buf.Discard(len(peekData))
if err != nil { if err != nil {
@ -334,7 +353,8 @@ func (p *parser) readPythonMultilines(line string, bufferSize int) (string, erro
return "", err return "", err
} }
line += "\n" + peekMatches[0] // Handle indented empty line.
line += "\n" + peekMatches[1][indentSize:] + peekMatches[2]
} }
} }
@ -445,8 +465,6 @@ func (f *File) parse(reader io.Reader) (err error) {
// Reset auto-counter and comments // Reset auto-counter and comments
p.comment.Reset() p.comment.Reset()
p.count = 1 p.count = 1
// Nested values can't span sections
isLastValueEmpty = false
inUnparseableSection = false inUnparseableSection = false
for i := range f.options.UnparseableSections { for i := range f.options.UnparseableSections {
@ -467,9 +485,8 @@ func (f *File) parse(reader io.Reader) (err error) {
kname, offset, err := readKeyName(f.options.KeyValueDelimiters, line) kname, offset, err := readKeyName(f.options.KeyValueDelimiters, line)
if err != nil { if err != nil {
switch {
// Treat as boolean key when desired, and whole line is key name. // Treat as boolean key when desired, and whole line is key name.
case IsErrDelimiterNotFound(err): if IsErrDelimiterNotFound(err) {
switch { switch {
case f.options.AllowBooleanKeys: case f.options.AllowBooleanKeys:
kname, err := p.readValue(line, parserBufferSize) kname, err := p.readValue(line, parserBufferSize)
@ -487,8 +504,6 @@ func (f *File) parse(reader io.Reader) (err error) {
case f.options.SkipUnrecognizableLines: case f.options.SkipUnrecognizableLines:
continue continue
} }
case IsErrEmptyKeyName(err) && f.options.SkipUnrecognizableLines:
continue
} }
return err return err
} }

2
vendor/gopkg.in/ini.v1/section.go generated vendored
View File

@ -217,7 +217,7 @@ func (s *Section) KeysHash() map[string]string {
defer s.f.lock.RUnlock() defer s.f.lock.RUnlock()
} }
hash := make(map[string]string, len(s.keysHash)) hash := map[string]string{}
for key, value := range s.keysHash { for key, value := range s.keysHash {
hash[key] = value hash[key] = value
} }

10
vendor/modules.txt vendored
View File

@ -1,5 +1,9 @@
# github.com/stretchr/testify v1.7.0 # github.com/gopherjs/gopherjs v0.0.0-20210406100015-1e088ea4ee04
## explicit; go 1.13 ## explicit
# gopkg.in/ini.v1 v1.67.0 # github.com/smartystreets/assertions v1.2.0
## explicit
# github.com/smartystreets/goconvey v1.6.4
## explicit
# gopkg.in/ini.v1 v1.62.0
## explicit ## explicit
gopkg.in/ini.v1 gopkg.in/ini.v1