Add Dockerfile, rewrite source

This commit is contained in:
Sergey Zhemoytel 2024-11-19 17:42:08 +03:00
parent a9bbbf458c
commit 499b064598
17 changed files with 562 additions and 0 deletions

20
Dockerfile Normal file
View file

@ -0,0 +1,20 @@
FROM docker.io/golang:1.23-alpine AS builder
COPY . /build
RUN apk add --no-cache git && \
ping -c5 tvoygit.ru && \
export GOPROXY=direct && \
cd /build && \
go build -o ./app .
FROM scratch
COPY --from=builder /build/app /app
CMD [/app]

53
artifact_types/maven.go Normal file
View file

@ -0,0 +1,53 @@
package artifact_types
import (
"fmt"
)
type MavenArtifact struct {
GroupID string
ArtifactID string
Version string
Packaging string
}
func (a MavenArtifact) String() string {
return fmt.Sprintf("%s:%s:%s:%s", a.GroupID, a.ArtifactID, a.Version, a.Packaging)
}
func ParseMavenArtifact(artifact string) (*MavenArtifact, error) {
parts := splitArtifact(artifact, 4)
if len(parts) < 4 {
return nil, fmt.Errorf("invalid Maven artifact format: %s", artifact)
}
return &MavenArtifact{
GroupID: parts[0],
ArtifactID: parts[1],
Version: parts[2],
Packaging: parts[3],
}, nil
}
func splitArtifact(artifact string, n int) []string {
parts := make([]string, n)
for i := 0; i < n-1; i++ {
index := findNextColon(artifact)
if index == -1 {
break
}
parts[i] = artifact[:index]
artifact = artifact[index+1:]
}
parts[n-1] = artifact
return parts
}
func findNextColon(s string) int {
for i, r := range s {
if r == ':' {
return i
}
}
return -1
}

View file

@ -0,0 +1,48 @@
package artifact_types
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestParseMavenArtifact(t *testing.T) {
tests := []struct {
artifact string
expected *MavenArtifact
}{
{
artifact: "com.example:my-artifact:1.0.0:jar",
expected: &MavenArtifact{
GroupID: "com.example",
ArtifactID: "my-artifact",
Version: "1.0.0",
Packaging: "jar",
},
},
{
artifact: "org.apache.commons:commons-lang3:3.12.0:jar",
expected: &MavenArtifact{
GroupID: "org.apache.commons",
ArtifactID: "commons-lang3",
Version: "3.12.0",
Packaging: "jar",
},
},
{
artifact: "invalid-artifact",
expected: nil,
},
}
for _, test := range tests {
actual, err := ParseMavenArtifact(test.artifact)
if test.expected == nil {
assert.Error(t, err, "Expected error for invalid artifact: %s", test.artifact)
} else {
assert.NoError(t, err, "Unexpected error for valid artifact: %s", test.artifact)
assert.Equal(t, test.expected, actual, "Mismatched parsed Maven artifact for: %s", test.artifact)
}
}
}

34
artifact_types/rpm.go Normal file
View file

@ -0,0 +1,34 @@
package artifact_types
import (
"fmt"
"regexp"
"strings"
)
type RpmArtifact struct {
Name string
Version string
Release string
Arch string
}
func (a RpmArtifact) String() string {
return fmt.Sprintf("%s-%s-%s.%s.rpm", a.Name, a.Version, a.Release, a.Arch)
}
var rpmRegex = regexp.MustCompile(`^(.+)-([^-]+)-([^-]+)\.(.+)\.rpm$`)
func ParseRpmArtifact(artifact string) (*RpmArtifact, error) {
matches := rpmRegex.FindStringSubmatch(artifact)
if matches == nil || len(matches) != 5 {
return nil, fmt.Errorf("invalid RPM artifact format: %s", artifact)
}
return &RpmArtifact{
Name: matches[1],
Version: matches[2],
Release: matches[3],
Arch: matches[4],
}, nil
}

View file

@ -0,0 +1,52 @@
package artifact_types
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestParseRpmArtifact(t *testing.T) {
tests := []struct {
artifact string
expected *RpmArtifact
}{
{
artifact: "example-1.0.0-1.x86_64.rpm",
expected: &RpmArtifact{
Name: "example",
Version: "1.0.0",
Release: "1",
Arch: "x86_64",
},
},
{
artifact: "my-package-2.3.4-5.el7.noarch.rpm",
expected: &RpmArtifact{
Name: "my-package",
Version: "2.3.4",
Release: "5.el7",
Arch: "noarch",
},
},
{
artifact: "invalid-artifact.rpm",
expected: nil,
},
{
artifact: "invalid-artifact",
expected: nil,
},
}
for _, test := range tests {
actual, err := ParseRpmArtifact(test.artifact)
if test.expected == nil {
assert.Error(t, err, "Expected error for invalid artifact: %s", test.artifact)
} else {
assert.NoError(t, err, "Unexpected error for valid artifact: %s", test.artifact)
assert.Equal(t, test.expected, actual, "Mismatched parsed RPM artifact for: %s", test.artifact)
}
}
}

10
config.yaml.template Normal file
View file

@ -0,0 +1,10 @@
nexus:
url: "https://nexus.example.com"
username: "your-nexus-username"
password: "your-nexus-password"
gitea:
url: "https://gitea.example.com"
token: "your-gitea-token"

34
config/config.go Normal file
View file

@ -0,0 +1,34 @@
package config
import (
"log"
"os"
"gopkg.in/yaml.v3"
)
type Config struct {
Nexus struct {
URL string `yaml:"url"`
Username string `yaml:"username"`
Password string `yaml:"password"`
} `yaml:"nexus"`
Gitea struct {
URL string `yaml:"url"`
Token string `yaml:"token"`
} `yaml:"gitea"`
}
func LoadConfig(filename string) (*Config, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
var config Config
err = yaml.Unmarshal(data, &config)
if err != nil {
return nil, err
}
return &config, nil
}

19
config/config_test.go Normal file
View file

@ -0,0 +1,19 @@
package config
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestLoadConfig(t *testing.T) {
configFile := "config.yaml"
cfg, err := LoadConfig(configFile)
assert.NoError(t, err)
assert.NotNil(t, cfg)
assert.Equal(t, "https://nexus.example.com", cfg.Nexus.URL)
assert.Equal(t, "your-nexus-username", cfg.Nexus.Username)
assert.Equal(t, "your-nexus-password", cfg.Nexus.Password)
assert.Equal(t, "https://gitea.example.com", cfg.Gitea.URL)
assert.Equal(t, "your-gitea-token", cfg.Gitea.Token)
}

66
core/core.go Normal file
View file

@ -0,0 +1,66 @@
package core
import (
"flag"
"fmt"
"tvoygit.ru/djam/artmigrator/config"
"tvoygit.ru/djam/artmigrator/logger"
"tvoygit.ru/djam/artmigrator/repository/gitea"
"tvoygit.ru/djam/artmigrator/repository/nexus"
)
type App struct {
Config *config.Config
}
func NewApp(configFile string) (*App, error) {
cfg, err := config.LoadConfig(configFile)
if err != nil {
return nil, err
}
flag.StringVar(&cfg.Nexus.URL, "nexus-url", cfg.Nexus.URL, "Nexus URL")
flag.StringVar(&cfg.Nexus.Username, "nexus-username", cfg.Nexus.Username, "Nexus Username")
flag.StringVar(&cfg.Nexus.Password, "nexus-password", cfg.Nexus.Password, "Nexus Password")
flag.StringVar(&cfg.Gitea.URL, "gitea-url", cfg.Gitea.URL, "Gitea URL")
flag.StringVar(&cfg.Gitea.Token, "gitea-token", cfg.Gitea.Token, "Gitea Token")
flag.Parse()
return &App{Config: cfg}, nil
}
func (a *App) Run() error {
nexusClient := nexus.NewClient(a.Config.Nexus.URL, a.Config.Nexus.Username, a.Config.Nexus.Password)
giteaClient := gitea.NewClient(a.Config.Gitea.URL, a.Config.Gitea.Token)
repository := "your-repository-name"
artifacts, err := nexusClient.GetArtifacts(repository)
if err != nil {
return err
}
for _, artifact := range artifacts {
exists, err := giteaClient.ArtifactExists("owner", "repo", artifact)
if err != nil {
return err
}
if exists {
logger.Logger.Printf("Artifact %s already exists in Gitea", artifact)
continue
}
// Загрузка артефакта
data, err := nexusClient.GetArtifactData(repository, artifact)
if err != nil {
return err
}
err = giteaClient.UploadArtifact("owner", "repo", artifact, data)
if err != nil {
return err
}
logger.Logger.Printf("Artifact %s uploaded to Gitea", artifact)
}
return nil
}

13
go.mod Normal file
View file

@ -0,0 +1,13 @@
module github.com/yourusername/art_migrator
go 1.19
require (
github.com/stretchr/testify v1.9.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
)

17
go.sum Normal file
View file

@ -0,0 +1,17 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

12
logger/logger.go Normal file
View file

@ -0,0 +1,12 @@
package logger
import (
"log"
"os"
)
var Logger *log.Logger
func init() {
Logger = log.New(os.Stdout, "", log.LstdFlags)
}

19
main.go Normal file
View file

@ -0,0 +1,19 @@
package main
import (
"log"
"tvoygit.ru/djam/artmigrator/core"
)
func main() {
app, err := core.NewApp("config.yaml")
if err != nil {
log.Fatalf("Failed to create app: %v", err)
}
err = app.Run()
if err != nil {
log.Fatalf("Failed to run app: %v", err)
}
}

66
repository/gitea/gitea.go Normal file
View file

@ -0,0 +1,66 @@
package gitea
import (
"fmt"
"io/ioutil"
"net/http"
"tvoygit.ru/djam/artmigrator/logger"
)
type Client struct {
BaseURL string
HTTPClient *http.Client
Token string
}
func NewClient(baseURL, token string) *Client {
return &Client{
BaseURL: baseURL,
HTTPClient: &http.Client{},
Token: token,
}
}
func (c *Client) ArtifactExists(owner, repo, artifact string) (bool, error) {
url := fmt.Sprintf("%s/api/v1/repos/%s/%s/contents/%s", c.BaseURL, owner, repo, artifact)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return false, err
}
req.Header.Add("Authorization", fmt.Sprintf("token %s", c.Token))
resp, err := c.HTTPClient.Do(req)
if err != nil {
return false, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return false, nil
}
return true, nil
}
func (c *Client) UploadArtifact(owner, repo, artifactPath string, data []byte) error {
url := fmt.Sprintf("%s/api/v1/repos/%s/%s/contents/%s", c.BaseURL, owner, repo, artifactPath)
req, err := http.NewRequest("POST", url, nil)
if err != nil {
return err
}
req.Header.Add("Authorization", fmt.Sprintf("token %s", c.Token))
// Здесь нужно заполнить тело запроса данными о загружаемом артефакте
resp, err := c.HTTPClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := ioutil.ReadAll(resp.Body)
logger.Logger.Printf("Failed to upload artifact: %s", body)
return fmt.Errorf("failed to upload artifact")
}
return nil
}

View file

@ -0,0 +1,14 @@
package gitea
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestArtifactExists(t *testing.T) {
client := NewClient("https://gitea.example.com", "token")
exists, err := client.ArtifactExists("owner", "repo", "test-artifact")
assert.NoError(t, err)
assert.Equal(t, false, exists)
}

71
repository/nexus/nexus.go Normal file
View file

@ -0,0 +1,71 @@
package nexus
import (
"fmt"
"io/ioutil"
"net/http"
"strings"
"tvoygit.ru/djam/artmigrator/logger"
)
type Client struct {
BaseURL string
HTTPClient *http.Client
Username string
Password string
}
func NewClient(baseURL, username, password string) *Client {
return &Client{
BaseURL: baseURL,
HTTPClient: &http.Client{},
Username: username,
Password: password,
}
}
func (c *Client) GetArtifacts(repository string) ([]string, error) {
url := fmt.Sprintf("%s/repository/%s/", c.BaseURL, repository)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.SetBasicAuth(c.Username, c.Password)
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// Здесь нужно парсить ответ и вытащить список артефактов
artifacts := strings.Split(string(body), "\n")
return artifacts, nil
}
func (c *Client) GetArtifactData(repository, artifact string) ([]byte, error) {
url := fmt.Sprintf("%s/repository/%s/%s", c.BaseURL, repository, artifact)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.SetBasicAuth(c.Username, c.Password)
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}

View file

@ -0,0 +1,14 @@
package nexus
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetArtifacts(t *testing.T) {
client := NewClient("https://nexus.example.com", "user", "pass")
artifacts, err := client.GetArtifacts("test-repo")
assert.NoError(t, err)
assert.NotEmpty(t, artifacts)
}