Alt Linux Apt-Rpm repository support for Forgejo packages.

This commit is contained in:
Aleksandr Gamzin 2024-12-11 14:31:58 +03:00
parent 021c8fe15a
commit 362d37b489
31 changed files with 2344 additions and 22 deletions

2
go.mod
View File

@ -218,9 +218,11 @@ require (
github.com/jessevdk/go-flags v1.5.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/larzconwell/bzip2 v0.0.0-20160405040150-ecf7a0ddeda1 // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect

4
go.sum
View File

@ -443,6 +443,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 h1:cTxwSmnaqLoo+4tLukHoB9iqHOu3LmLhRmgUxZo6Vp4=
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
@ -468,6 +470,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/larzconwell/bzip2 v0.0.0-20160405040150-ecf7a0ddeda1 h1:V1CKKq9+klx5qgClRNrr9btXaZoyWfhY8INwCbrqpV4=
github.com/larzconwell/bzip2 v0.0.0-20160405040150-ecf7a0ddeda1/go.mod h1:Zq5BehdDAeg6PtAOzBvVComb8iNktGVia+oYqUoJrAk=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=

View File

@ -0,0 +1,28 @@
package alt
import (
"context"
packages_model "code.gitea.io/gitea/models/packages"
alt_module "code.gitea.io/gitea/modules/packages/alt"
)
type PackageSearchOptions struct {
OwnerID int64
GroupID int64
Architecture string
}
// GetGroups gets all available groups
func GetGroups(ctx context.Context, ownerID int64) ([]string, error) {
return packages_model.GetDistinctPropertyValues(
ctx,
packages_model.TypeAlt,
ownerID,
packages_model.PropertyTypeFile,
alt_module.PropertyGroup,
nil,
)
}

View File

@ -29,6 +29,7 @@ import (
"code.gitea.io/gitea/modules/packages/pub"
"code.gitea.io/gitea/modules/packages/pypi"
"code.gitea.io/gitea/modules/packages/rpm"
"code.gitea.io/gitea/modules/packages/alt"
"code.gitea.io/gitea/modules/packages/rubygems"
"code.gitea.io/gitea/modules/packages/swift"
"code.gitea.io/gitea/modules/packages/vagrant"
@ -187,6 +188,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
metadata = &pypi.Metadata{}
case TypeRpm:
metadata = &rpm.VersionMetadata{}
case TypeAlt:
metadata = &alt.VersionMetadata{}
case TypeRubyGems:
metadata = &rubygems.Metadata{}
case TypeSwift:

View File

@ -51,6 +51,7 @@ const (
TypePub Type = "pub"
TypePyPI Type = "pypi"
TypeRpm Type = "rpm"
TypeAlt Type = "alt"
TypeRubyGems Type = "rubygems"
TypeSwift Type = "swift"
TypeVagrant Type = "vagrant"
@ -76,6 +77,7 @@ var TypeList = []Type{
TypePub,
TypePyPI,
TypeRpm,
TypeAlt,
TypeRubyGems,
TypeSwift,
TypeVagrant,
@ -122,6 +124,8 @@ func (pt Type) Name() string {
return "PyPI"
case TypeRpm:
return "RPM"
case TypeAlt:
return "Alt"
case TypeRubyGems:
return "RubyGems"
case TypeSwift:
@ -173,6 +177,8 @@ func (pt Type) SVGName() string {
return "gitea-python"
case TypeRpm:
return "gitea-rpm"
case TypeAlt:
return "gitea-alt"
case TypeRubyGems:
return "gitea-rubygems"
case TypeSwift:

View File

@ -34,6 +34,7 @@ type PackageBlob struct {
HashSHA1 string `xorm:"hash_sha1 char(40) UNIQUE(sha1) INDEX NOT NULL"`
HashSHA256 string `xorm:"hash_sha256 char(64) UNIQUE(sha256) INDEX NOT NULL"`
HashSHA512 string `xorm:"hash_sha512 char(128) UNIQUE(sha512) INDEX NOT NULL"`
HashBlake2b string `xorm:"hash_blake2b char(128) UNIQUE(blake2b) INDEX NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created INDEX NOT NULL"`
}
@ -49,6 +50,7 @@ func GetOrInsertBlob(ctx context.Context, pb *PackageBlob) (*PackageBlob, bool,
"hash_sha1": pb.HashSHA1,
"hash_sha256": pb.HashSHA256,
"hash_sha512": pb.HashSHA512,
"hash_blake2b":pb.HashBlake2b,
}).Get(existing)
if err != nil {
return nil, false, err

View File

@ -0,0 +1,265 @@
package alt
import (
"fmt"
"io"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/validation"
"github.com/sassoftware/go-rpmutils"
)
const (
PropertyMetadata = "alt.metadata"
PropertyGroup = "alt.group"
PropertyArchitecture = "alt.architecture"
PropertyVersion = "alt.version"
SettingKeyPrivate = "alt.key.private"
SettingKeyPublic = "alt.key.public"
RepositoryPackage = "_alt"
RepositoryVersion = "_repository"
)
const (
// Can't use the syscall constants because they are not available for windows build.
sIFMT = 0xf000
sIFDIR = 0x4000
sIXUSR = 0x40
sIXGRP = 0x8
sIXOTH = 0x1
)
// https://rpm-software-management.github.io/rpm/manual/spec.html
// https://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/pkgformat.html
type Package struct {
Name string
Version string
VersionMetadata *VersionMetadata
FileMetadata *FileMetadata
}
type VersionMetadata struct {
License string `json:"license,omitempty"`
ProjectURL string `json:"project_url,omitempty"`
Summary string `json:"summary,omitempty"`
Description string `json:"description,omitempty"`
}
type FileMetadata struct {
Architecture string `json:"architecture,omitempty"`
Epoch string `json:"epoch,omitempty"`
Version string `json:"version,omitempty"`
Release string `json:"release,omitempty"`
Vendor string `json:"vendor,omitempty"`
Group string `json:"group,omitempty"`
Packager string `json:"packager,omitempty"`
SourceRpm string `json:"source_rpm,omitempty"`
BuildHost string `json:"build_host,omitempty"`
BuildTime uint64 `json:"build_time,omitempty"`
FileTime uint64 `json:"file_time,omitempty"`
InstalledSize uint64 `json:"installed_size,omitempty"`
ArchiveSize uint64 `json:"archive_size,omitempty"`
Provides []*Entry `json:"provide,omitempty"`
Requires []*Entry `json:"require,omitempty"`
Conflicts []*Entry `json:"conflict,omitempty"`
Obsoletes []*Entry `json:"obsolete,omitempty"`
Files []*File `json:"files,omitempty"`
Changelogs []*Changelog `json:"changelogs,omitempty"`
}
type Entry struct {
Name string `json:"name" xml:"name,attr"`
Flags uint32 `json:"flags,omitempty" xml:"flags,attr,omitempty"`
Version string `json:"version,omitempty" xml:"ver,attr,omitempty"`
Epoch string `json:"epoch,omitempty" xml:"epoch,attr,omitempty"`
Release string `json:"release,omitempty" xml:"rel,attr,omitempty"`
}
type File struct {
Path string `json:"path" xml:",chardata"`
Type string `json:"type,omitempty" xml:"type,attr,omitempty"`
IsExecutable bool `json:"is_executable" xml:"-"`
}
type Changelog struct {
Author string `json:"author,omitempty" xml:"author,attr"`
Date timeutil.TimeStamp `json:"date,omitempty" xml:"date,attr"`
Text string `json:"text,omitempty" xml:",chardata"`
}
// ParsePackage parses the RPM package file
func ParsePackage(r io.Reader) (*Package, error) {
rpm, err := rpmutils.ReadRpm(r)
if err != nil {
return nil, err
}
nevra, err := rpm.Header.GetNEVRA()
if err != nil {
return nil, err
}
version := fmt.Sprintf("%s-%s", nevra.Version, nevra.Release)
if nevra.Epoch != "" && nevra.Epoch != "0" {
version = fmt.Sprintf("%s-%s", nevra.Epoch, version)
}
p := &Package{
Name: nevra.Name,
Version: version,
VersionMetadata: &VersionMetadata{
Summary: getString(rpm.Header, rpmutils.SUMMARY),
Description: getString(rpm.Header, rpmutils.DESCRIPTION),
License: getString(rpm.Header, rpmutils.LICENSE),
ProjectURL: getString(rpm.Header, rpmutils.URL),
},
FileMetadata: &FileMetadata{
Architecture: nevra.Arch,
Epoch: nevra.Epoch,
Version: nevra.Version,
Release: nevra.Release,
Vendor: getString(rpm.Header, rpmutils.VENDOR),
Group: getString(rpm.Header, rpmutils.GROUP),
Packager: getString(rpm.Header, rpmutils.PACKAGER),
SourceRpm: getString(rpm.Header, rpmutils.SOURCERPM),
BuildHost: getString(rpm.Header, rpmutils.BUILDHOST),
BuildTime: getUInt64(rpm.Header, rpmutils.BUILDTIME),
FileTime: getUInt64(rpm.Header, rpmutils.FILEMTIMES),
InstalledSize: getUInt64(rpm.Header, rpmutils.SIZE),
ArchiveSize: getUInt64(rpm.Header, rpmutils.SIG_PAYLOADSIZE),
Provides: getEntries(rpm.Header, rpmutils.PROVIDENAME, rpmutils.PROVIDEVERSION, rpmutils.PROVIDEFLAGS),
Requires: getEntries(rpm.Header, rpmutils.REQUIRENAME, rpmutils.REQUIREVERSION, rpmutils.REQUIREFLAGS),
Conflicts: getEntries(rpm.Header, rpmutils.CONFLICTNAME, rpmutils.CONFLICTVERSION, rpmutils.CONFLICTFLAGS),
Obsoletes: getEntries(rpm.Header, rpmutils.OBSOLETENAME, rpmutils.OBSOLETEVERSION, rpmutils.OBSOLETEFLAGS),
Files: getFiles(rpm.Header),
Changelogs: getChangelogs(rpm.Header),
},
}
if !validation.IsValidURL(p.VersionMetadata.ProjectURL) {
p.VersionMetadata.ProjectURL = ""
}
return p, nil
}
func getString(h *rpmutils.RpmHeader, tag int) string {
values, err := h.GetStrings(tag)
if err != nil || len(values) < 1 {
return ""
}
return values[0]
}
func getUInt64(h *rpmutils.RpmHeader, tag int) uint64 {
values, err := h.GetUint64s(tag)
if err != nil || len(values) < 1 {
return 0
}
return values[0]
}
func getEntries(h *rpmutils.RpmHeader, namesTag, versionsTag, flagsTag int) []*Entry {
names, err := h.GetStrings(namesTag)
if err != nil || len(names) == 0 {
return nil
}
flags, err := h.GetUint64s(flagsTag)
if err != nil || len(flags) == 0 {
return nil
}
versions, err := h.GetStrings(versionsTag)
if err != nil || len(versions) == 0 {
return nil
}
if len(names) != len(flags) || len(names) != len(versions) {
return nil
}
entries := make([]*Entry, 0, len(names))
for i := range names {
e := &Entry{
Name: names[i],
}
e.Flags = uint32(flags[i])
e.Version = versions[i]
entries = append(entries, e)
}
return entries
}
func getFiles(h *rpmutils.RpmHeader) []*File {
baseNames, _ := h.GetStrings(rpmutils.BASENAMES)
dirNames, _ := h.GetStrings(rpmutils.DIRNAMES)
dirIndexes, _ := h.GetUint32s(rpmutils.DIRINDEXES)
fileFlags, _ := h.GetUint32s(rpmutils.FILEFLAGS)
fileModes, _ := h.GetUint32s(rpmutils.FILEMODES)
files := make([]*File, 0, len(baseNames))
for i := range baseNames {
if len(dirIndexes) <= i {
continue
}
dirIndex := dirIndexes[i]
if len(dirNames) <= int(dirIndex) {
continue
}
var fileType string
var isExecutable bool
if i < len(fileFlags) && (fileFlags[i]&rpmutils.RPMFILE_GHOST) != 0 {
fileType = "ghost"
} else if i < len(fileModes) {
if (fileModes[i] & sIFMT) == sIFDIR {
fileType = "dir"
} else {
mode := fileModes[i] & ^uint32(sIFMT)
isExecutable = (mode&sIXUSR) != 0 || (mode&sIXGRP) != 0 || (mode&sIXOTH) != 0
}
}
files = append(files, &File{
Path: dirNames[dirIndex] + baseNames[i],
Type: fileType,
IsExecutable: isExecutable,
})
}
return files
}
func getChangelogs(h *rpmutils.RpmHeader) []*Changelog {
texts, err := h.GetStrings(rpmutils.CHANGELOGTEXT)
if err != nil || len(texts) == 0 {
return nil
}
authors, err := h.GetStrings(rpmutils.CHANGELOGNAME)
if err != nil || len(authors) == 0 {
return nil
}
times, err := h.GetUint32s(rpmutils.CHANGELOGTIME)
if err != nil || len(times) == 0 {
return nil
}
if len(texts) != len(authors) || len(texts) != len(times) {
return nil
}
changelogs := make([]*Changelog, 0, len(texts))
for i := range texts {
changelogs = append(changelogs, &Changelog{
Author: authors[i],
Date: timeutil.TimeStamp(times[i]),
Text: texts[i],
})
}
return changelogs
}

View File

@ -94,7 +94,7 @@ type FileMetadata struct {
// ParsePackage Function that receives arch package archive data and returns it's metadata.
func ParsePackage(r *packages.HashedBuffer) (*Package, error) {
md5, _, sha256, _ := r.Sums()
md5, _, sha256, _, _ := r.Sums()
_, err := r.Seek(0, io.SeekStart)
if err != nil {
return nil, err

View File

@ -76,6 +76,6 @@ func (b *HashedBuffer) Write(p []byte) (int, error) {
}
// Sums gets the MD5, SHA1, SHA256 and SHA512 checksums of the data
func (b *HashedBuffer) Sums() (hashMD5, hashSHA1, hashSHA256, hashSHA512 []byte) {
func (b *HashedBuffer) Sums() (hashMD5, hashSHA1, hashSHA256, hashSHA512, HashBlake2b []byte) {
return b.hash.Sums()
}

View File

@ -36,7 +36,7 @@ func TestHashedBuffer(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, c.Data, string(data))
hashMD5, hashSHA1, hashSHA256, hashSHA512 := buf.Sums()
hashMD5, hashSHA1, hashSHA256, hashSHA512, _ := buf.Sums()
assert.Equal(t, c.HashMD5, hex.EncodeToString(hashMD5))
assert.Equal(t, c.HashSHA1, hex.EncodeToString(hashSHA1))
assert.Equal(t, c.HashSHA256, hex.EncodeToString(hashSHA256))

View File

@ -8,6 +8,7 @@ import (
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"golang.org/x/crypto/blake2b"
"encoding"
"errors"
"hash"
@ -19,13 +20,14 @@ const (
marshaledSizeSHA1 = 96
marshaledSizeSHA256 = 108
marshaledSizeSHA512 = 204
marshaledSizeBlake2b = 128
marshaledSize = marshaledSizeMD5 + marshaledSizeSHA1 + marshaledSizeSHA256 + marshaledSizeSHA512
marshaledSize = marshaledSizeMD5 + marshaledSizeSHA1 + marshaledSizeSHA256 + marshaledSizeSHA512 + marshaledSizeBlake2b
)
// HashSummer provide a Sums method
type HashSummer interface {
Sums() (hashMD5, hashSHA1, hashSHA256, hashSHA512 []byte)
Sums() (hashMD5, hashSHA1, hashSHA256, hashSHA512, hashBlake2b []byte)
}
// MultiHasher calculates multiple checksums
@ -34,6 +36,7 @@ type MultiHasher struct {
sha1 hash.Hash
sha256 hash.Hash
sha512 hash.Hash
blake2b hash.Hash
combinedWriter io.Writer
}
@ -44,14 +47,16 @@ func NewMultiHasher() *MultiHasher {
sha1 := sha1.New()
sha256 := sha256.New()
sha512 := sha512.New()
blake2b, _ := blake2b.New512(nil)
combinedWriter := io.MultiWriter(md5, sha1, sha256, sha512)
combinedWriter := io.MultiWriter(md5, sha1, sha256, sha512, blake2b)
return &MultiHasher{
md5,
sha1,
sha256,
sha512,
blake2b,
combinedWriter,
}
}
@ -74,12 +79,17 @@ func (h *MultiHasher) MarshalBinary() ([]byte, error) {
if err != nil {
return nil, err
}
blake2bBytes, err := h.blake2b.(encoding.BinaryMarshaler).MarshalBinary()
if err != nil {
return nil, err
}
b := make([]byte, 0, marshaledSize)
b = append(b, md5Bytes...)
b = append(b, sha1Bytes...)
b = append(b, sha256Bytes...)
b = append(b, sha512Bytes...)
b = append(b, blake2bBytes...)
return b, nil
}
@ -89,22 +99,27 @@ func (h *MultiHasher) UnmarshalBinary(b []byte) error {
return errors.New("invalid hash state size")
}
if err := h.md5.(encoding.BinaryUnmarshaler).UnmarshalBinary(b[:marshaledSizeMD5]); err != nil {
return err
}
b = b[marshaledSizeMD5:]
if err := h.sha1.(encoding.BinaryUnmarshaler).UnmarshalBinary(b[:marshaledSizeSHA1]); err != nil {
return err
}
b = b[marshaledSizeSHA1:]
if err := h.sha256.(encoding.BinaryUnmarshaler).UnmarshalBinary(b[:marshaledSizeSHA256]); err != nil {
return err
}
b = b[marshaledSizeSHA256:]
return h.sha512.(encoding.BinaryUnmarshaler).UnmarshalBinary(b[:marshaledSizeSHA512])
if err := h.sha512.(encoding.BinaryUnmarshaler).UnmarshalBinary(b[:marshaledSizeSHA512]); err != nil {
return err
}
b = b[marshaledSizeSHA512:]
if err := h.blake2b.(encoding.BinaryUnmarshaler).UnmarshalBinary(b[:marshaledSizeBlake2b]); err != nil {
return err
}
return nil
}
// Write implements io.Writer
@ -113,10 +128,11 @@ func (h *MultiHasher) Write(p []byte) (int, error) {
}
// Sums gets the MD5, SHA1, SHA256 and SHA512 checksums of the data
func (h *MultiHasher) Sums() (hashMD5, hashSHA1, hashSHA256, hashSHA512 []byte) {
func (h *MultiHasher) Sums() (hashMD5, hashSHA1, hashSHA256, hashSHA512, hashBlake2b []byte) {
hashMD5 = h.md5.Sum(nil)
hashSHA1 = h.sha1.Sum(nil)
hashSHA256 = h.sha256.Sum(nil)
hashSHA512 = h.sha512.Sum(nil)
return hashMD5, hashSHA1, hashSHA256, hashSHA512
hashBlake2b = h.blake2b.Sum(nil)
return hashMD5, hashSHA1, hashSHA256, hashSHA512, hashBlake2b
}

View File

@ -16,6 +16,7 @@ const (
expectedSHA1 = "060b3b99f88e96085b4a68e095bc9e3d1d91e1bc"
expectedSHA256 = "6ccce4863b70f258d691f59609d31b4502e1ba5199942d3bc5d35d17a4ce771d"
expectedSHA512 = "7f70e439ba8c52025c1f06cdf6ae443c4b8ed2e90059cdb9bbbf8adf80846f185a24acca9245b128b226d61753b0d7ed46580a69c8999eeff3bc13a4d0bd816c"
expectedBlake2b = ""
)
func TestMultiHasherSums(t *testing.T) {
@ -23,7 +24,7 @@ func TestMultiHasherSums(t *testing.T) {
h := NewMultiHasher()
h.Write([]byte("gitea"))
hashMD5, hashSHA1, hashSHA256, hashSHA512 := h.Sums()
hashMD5, hashSHA1, hashSHA256, hashSHA512, _ := h.Sums()
assert.Equal(t, expectedMD5, hex.EncodeToString(hashMD5))
assert.Equal(t, expectedSHA1, hex.EncodeToString(hashSHA1))
@ -44,7 +45,7 @@ func TestMultiHasherSums(t *testing.T) {
h2.Write([]byte("ea"))
hashMD5, hashSHA1, hashSHA256, hashSHA512 := h2.Sums()
hashMD5, hashSHA1, hashSHA256, hashSHA512, _ := h2.Sums()
assert.Equal(t, expectedMD5, hex.EncodeToString(hashMD5))
assert.Equal(t, expectedSHA1, hex.EncodeToString(hashSHA1))

View File

@ -42,6 +42,7 @@ var (
LimitSizePub int64
LimitSizePyPI int64
LimitSizeRpm int64
LimitSizeAlt int64
LimitSizeRubyGems int64
LimitSizeSwift int64
LimitSizeVagrant int64
@ -106,6 +107,7 @@ func loadPackagesFrom(rootCfg ConfigProvider) (err error) {
Packages.LimitSizeSwift = mustBytes(sec, "LIMIT_SIZE_SWIFT")
Packages.LimitSizeVagrant = mustBytes(sec, "LIMIT_SIZE_VAGRANT")
Packages.DefaultRPMSignEnabled = sec.Key("DEFAULT_RPM_SIGN_ENABLED").MustBool(false)
Packages.LimitSizeAlt = mustBytes(sec, "LIMIT_SIZE_ALT")
return nil
}

View File

@ -3709,6 +3709,13 @@ rpm.install = To install the package, run the following command:
rpm.repository = Repository Info
rpm.repository.architectures = Architectures
rpm.repository.multiple_groups = This package is available in multiple groups.
alt.registry = Setup this registry from the command line:
alt.registry.install = To install the package, run the following command:
alt.install = Install package
alt.setup = Add a repository to the list of connected repositories (choose the necessary architecture instead of '_arch_'):
alt.repository = Repository Info
alt.repository.architectures = Architectures
alt.repository.multiple_groups = This package is available in multiple groups.
rubygems.install = To install the package using gem, run the following command:
rubygems.install2 = or add it to the Gemfile:
rubygems.dependencies.runtime = Runtime Dependencies

View File

@ -3738,6 +3738,13 @@ rpm.distros.suse=на дистрибутивах семейства SUSE
rpm.install=Чтобы установить пакет, выполните следующую команду:
rpm.repository = О репозитории
rpm.repository.architectures = Архитектуры
alt.registry=Настроить реестр из командной строки:
alt.registry.install=Чтобы установить пакет, выполните следующую команду:
alt.install=Установить пакет:
alt.setup=Добавить репозиторий в список подключенных репозиториев (вместо "_arch_" выбрать необходимую архитектуру):
alt.repository=О репозитории
alt.repository.architectures=Архитектуры
alt.repository.multiple_groups=Этот пакет доступен в нескольких группах.
rubygems.install=Чтобы установить пакет с помощью gem, выполните следующую команду:
rubygems.install2=или добавьте его в Gemfile:
rubygems.dependencies.runtime=Зависимости времени выполнения

View File

@ -0,0 +1,314 @@
package alt
import (
stdctx "context"
"errors"
"fmt"
"io"
"net/http"
"strings"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/modules/json"
packages_module "code.gitea.io/gitea/modules/packages"
alt_module "code.gitea.io/gitea/modules/packages/alt"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
"code.gitea.io/gitea/services/context"
notify_service "code.gitea.io/gitea/services/notify"
packages_service "code.gitea.io/gitea/services/packages"
alt_service "code.gitea.io/gitea/services/packages/alt"
)
func apiError(ctx *context.Context, status int, obj any) {
helper.LogAndProcessError(ctx, status, obj, func(message string) {
ctx.PlainText(status, message)
})
}
// https://dnf.readthedocs.io/en/latest/conf_ref.html
func GetRepositoryConfig(ctx *context.Context) {
group := ctx.Params("group")
var groupParts []string
if group != "" {
groupParts = strings.Split(group, "/")
}
url := fmt.Sprintf("%sapi/packages/%s/alt", setting.AppURL, ctx.Package.Owner.Name)
ctx.PlainText(http.StatusOK, `[gitea-`+strings.Join(append([]string{ctx.Package.Owner.LowerName}, groupParts...), "-")+`]
name=`+strings.Join(append([]string{ctx.Package.Owner.Name, setting.AppName}, groupParts...), " - ")+`
baseurl=`+strings.Join(append([]string{url}, groupParts...), "/")+`
enabled=1
gpgcheck=1
gpgkey=`+url+`/repository.key`)
}
// Gets or creates the PGP public key used to sign repository metadata files
func GetRepositoryKey(ctx *context.Context) {
_, pub, err := alt_service.GetOrCreateKeyPair(ctx, ctx.Package.Owner.ID)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
ctx.ServeContent(strings.NewReader(pub), &context.ServeHeaderOptions{
ContentType: "application/pgp-keys",
Filename: "repository.key",
})
}
func CheckRepositoryFileExistence(ctx *context.Context) {
pv, err := alt_service.GetOrCreateRepositoryVersion(ctx, ctx.Package.Owner.ID)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, ctx.Params("filename"), ctx.Params("group"))
if err != nil {
if errors.Is(err, util.ErrNotExist) {
ctx.Status(http.StatusNotFound)
} else {
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
ctx.SetServeHeaders(&context.ServeHeaderOptions{
Filename: pf.Name,
LastModified: pf.CreatedUnix.AsLocalTime(),
})
ctx.Status(http.StatusOK)
}
// Gets a pre-generated repository metadata file
func GetRepositoryFile(ctx *context.Context, arch string) {
pv, err := alt_service.GetOrCreateRepositoryVersion(ctx, ctx.Package.Owner.ID)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
ctx,
pv,
&packages_service.PackageFileInfo{
Filename: ctx.Params("filename"),
CompositeKey: arch + "__" + ctx.Params("group"),
},
)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
helper.ServePackageFile(ctx, s, u, pf)
}
func UploadPackageFile(ctx *context.Context) {
upload, needToClose, err := ctx.UploadStream()
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
if needToClose {
defer upload.Close()
}
buf, err := packages_module.CreateHashedBufferFromReader(upload)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer buf.Close()
if setting.Packages.DefaultRPMSignEnabled || ctx.FormBool("sign") {
priv, _, err := alt_service.GetOrCreateKeyPair(ctx, ctx.Package.Owner.ID)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
signedBuf, err := alt_service.SignPackage(buf, priv)
if err != nil {
apiError(ctx, http.StatusBadRequest, err)
return
}
defer signedBuf.Close()
buf = signedBuf
}
pck, err := alt_module.ParsePackage(buf)
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
apiError(ctx, http.StatusBadRequest, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
if _, err := buf.Seek(0, io.SeekStart); err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
fileMetadataRaw, err := json.Marshal(pck.FileMetadata)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
group := ctx.Params("group")
_, _, err = packages_service.CreatePackageOrAddFileToExisting(
ctx,
&packages_service.PackageCreationInfo{
PackageInfo: packages_service.PackageInfo{
Owner: ctx.Package.Owner,
PackageType: packages_model.TypeAlt,
Name: pck.Name,
Version: pck.Version,
},
Creator: ctx.Doer,
Metadata: pck.VersionMetadata,
},
&packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{
Filename: fmt.Sprintf("%s-%s.%s.rpm", pck.Name, pck.Version, pck.FileMetadata.Architecture),
CompositeKey: group,
},
Creator: ctx.Doer,
Data: buf,
IsLead: true,
Properties: map[string]string{
alt_module.PropertyGroup: group,
alt_module.PropertyArchitecture: pck.FileMetadata.Architecture,
alt_module.PropertyMetadata: string(fileMetadataRaw),
},
},
)
if err != nil {
switch err {
case packages_model.ErrDuplicatePackageVersion, packages_model.ErrDuplicatePackageFile:
apiError(ctx, http.StatusConflict, err)
case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
apiError(ctx, http.StatusForbidden, err)
default:
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
if err := alt_service.BuildSpecificRepositoryFiles(ctx, ctx.Package.Owner.ID, group); err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
ctx.Status(http.StatusCreated)
}
func DownloadPackageFile(ctx *context.Context) {
name := ctx.Params("name")
version := ctx.Params("version")
s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
PackageType: packages_model.TypeAlt,
Name: name,
Version: version,
},
&packages_service.PackageFileInfo{
Filename: fmt.Sprintf("%s-%s.%s.rpm", name, version, ctx.Params("architecture")),
CompositeKey: ctx.Params("group"),
},
)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
helper.ServePackageFile(ctx, s, u, pf)
}
func DeletePackageFile(webctx *context.Context) {
group := webctx.Params("group")
name := webctx.Params("name")
version := webctx.Params("version")
architecture := webctx.Params("architecture")
var pd *packages_model.PackageDescriptor
err := db.WithTx(webctx, func(ctx stdctx.Context) error {
pv, err := packages_model.GetVersionByNameAndVersion(ctx,
webctx.Package.Owner.ID,
packages_model.TypeAlt,
name,
version,
)
if err != nil {
return err
}
pf, err := packages_model.GetFileForVersionByName(
ctx,
pv.ID,
fmt.Sprintf("%s-%s.%s.rpm", name, version, architecture),
group,
)
if err != nil {
return err
}
if err := packages_service.DeletePackageFile(ctx, pf); err != nil {
return err
}
has, err := packages_model.HasVersionFileReferences(ctx, pv.ID)
if err != nil {
return err
}
if !has {
pd, err = packages_model.GetPackageDescriptor(ctx, pv)
if err != nil {
return err
}
if err := packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
return err
}
}
return nil
})
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(webctx, http.StatusNotFound, err)
} else {
apiError(webctx, http.StatusInternalServerError, err)
}
return
}
if pd != nil {
notify_service.PackageDelete(webctx, webctx.Doer, pd)
}
if err := alt_service.BuildSpecificRepositoryFiles(webctx, webctx.Package.Owner.ID, group); err != nil {
apiError(webctx, http.StatusInternalServerError, err)
return
}
webctx.Status(http.StatusNoContent)
}

View File

@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/packages/alpine"
"code.gitea.io/gitea/routers/api/packages/alt"
"code.gitea.io/gitea/routers/api/packages/arch"
"code.gitea.io/gitea/routers/api/packages/cargo"
"code.gitea.io/gitea/routers/api/packages/chef"
@ -623,6 +624,78 @@ func CommonRoutes() *web.Route {
ctx.Status(http.StatusNotFound)
})
}, reqPackageAccess(perm.AccessModeRead))
r.Group("/alt", func() {
r.Group("/repository.key", func() {
r.Head("", alt.GetRepositoryKey)
r.Get("", alt.GetRepositoryKey)
})
var (
repoPattern = regexp.MustCompile(`\A(.*?)\.repo\z`)
uploadPattern = regexp.MustCompile(`\A(.*?)/upload\z`)
baseRepoPattern = regexp.MustCompile(`(\S+)\.repo/(\S+)\/base/(\S+)`)
rpmsRepoPattern = regexp.MustCompile(`(\S+)\.repo/(\S+)\.(\S+)\/([a-zA-Z0-9_-]+)-([\d.]+-[a-zA-Z0-9_-]+)\.(\S+)\.rpm`)
)
r.Methods("HEAD,GET,PUT,DELETE", "*", func(ctx *context.Context) {
path := ctx.Params("*")
isGetHead := ctx.Req.Method == "HEAD" || ctx.Req.Method == "GET"
isPut := ctx.Req.Method == "PUT"
isDelete := ctx.Req.Method == "DELETE"
m := repoPattern.FindStringSubmatch(path)
if len(m) == 2 && isGetHead {
ctx.SetParams("group", strings.Trim(m[1], "/"))
alt.GetRepositoryConfig(ctx)
return
}
m = baseRepoPattern.FindStringSubmatch(path)
if len(m) == 4 {
if strings.Trim(m[1], "/") != "alt" {
ctx.SetParams("group", strings.Trim(m[1], "/"))
}
ctx.SetParams("filename", m[3])
if isGetHead {
alt.GetRepositoryFile(ctx, m[2])
}
}
m = uploadPattern.FindStringSubmatch(path)
if len(m) == 2 && isPut {
reqPackageAccess(perm.AccessModeWrite)(ctx)
if ctx.Written() {
return
}
ctx.SetParams("group", strings.Trim(m[1], "/"))
alt.UploadPackageFile(ctx)
return
}
m = rpmsRepoPattern.FindStringSubmatch(path)
if len(m) == 7 && (isGetHead || isDelete) {
if strings.Trim(m[1], "/") != "alt" {
ctx.SetParams("group", strings.Trim(m[1], "/"))
}
ctx.SetParams("name", m[4])
ctx.SetParams("version", m[5])
ctx.SetParams("architecture", m[6])
if isGetHead {
alt.DownloadPackageFile(ctx)
} else {
reqPackageAccess(perm.AccessModeWrite)(ctx)
if ctx.Written() {
return
}
alt.DeletePackageFile(ctx)
}
return
}
ctx.Status(http.StatusNotFound)
})
}, reqPackageAccess(perm.AccessModeRead))
r.Group("/rubygems", func() {
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest)

View File

@ -193,7 +193,7 @@ func deleteBlob(ctx context.Context, ownerID int64, image, digest string) error
}
func digestFromHashSummer(h packages_module.HashSummer) string {
_, _, hashSHA256, _ := h.Sums()
_, _, hashSHA256, _, _ := h.Sums()
return "sha256:" + hex.EncodeToString(hashSHA256)
}

View File

@ -120,7 +120,7 @@ func UploadPackageFile(ctx *context.Context) {
}
defer buf.Close()
_, _, hashSHA256, _ := buf.Sums()
_, _, hashSHA256, _, _ := buf.Sums()
if !strings.EqualFold(ctx.Req.FormValue("sha256_digest"), hex.EncodeToString(hashSHA256)) {
apiError(ctx, http.StatusBadRequest, "hash mismatch")

View File

@ -22,6 +22,7 @@ import (
arch_model "code.gitea.io/gitea/modules/packages/arch"
debian_module "code.gitea.io/gitea/modules/packages/debian"
rpm_module "code.gitea.io/gitea/modules/packages/rpm"
alt_module "code.gitea.io/gitea/modules/packages/alt"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
@ -250,6 +251,23 @@ func ViewPackageVersion(ctx *context.Context) {
}
}
ctx.Data["Groups"] = util.Sorted(groups.Values())
ctx.Data["Architectures"] = util.Sorted(architectures.Values())
case packages_model.TypeAlt:
groups := make(container.Set[string])
architectures := make(container.Set[string])
for _, f := range pd.Files {
for _, pp := range f.Properties {
switch pp.Name {
case alt_module.PropertyGroup:
groups.Add(pp.Value)
case alt_module.PropertyArchitecture:
architectures.Add(pp.Value)
}
}
}
ctx.Data["Groups"] = util.Sorted(groups.Values())
ctx.Data["Architectures"] = util.Sorted(architectures.Values())
}

View File

@ -15,7 +15,7 @@ import (
type PackageCleanupRuleForm struct {
ID int64
Enabled bool
Type string `binding:"Required;In(alpine,arch,cargo,chef,composer,conan,conda,container,cran,debian,generic,go,helm,maven,npm,nuget,pub,pypi,rpm,rubygems,swift,vagrant)"`
Type string `binding:"Required;In(alpine,arch,cargo,chef,composer,conan,conda,container,cran,debian,generic,go,helm,maven,npm,nuget,pub,pypi,rpm,alt,rubygems,swift,vagrant)"`
KeepCount int `binding:"In(0,1,5,10,25,50,100)"`
KeepPattern string `binding:"RegexPattern"`
RemoveDays int `binding:"In(0,7,14,30,60,90,180)"`

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
package alt
import (
"bytes"
"io"
"strings"
packages_module "code.gitea.io/gitea/modules/packages"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/sassoftware/go-rpmutils"
)
func SignPackage(buf *packages_module.HashedBuffer, privateKey string) (*packages_module.HashedBuffer, error) {
keyring, err := openpgp.ReadArmoredKeyRing(strings.NewReader(privateKey))
if err != nil {
return nil, err
}
h, err := rpmutils.SignRpmStream(buf, keyring[0].PrivateKey, nil)
if err != nil {
return nil, err
}
signBlob, err := h.DumpSignatureHeader(false)
if err != nil {
return nil, err
}
if _, err := buf.Seek(int64(h.OriginalSignatureHeaderSize()), io.SeekStart); err != nil {
return nil, err
}
// create new buf with signature prefix
return packages_module.CreateHashedBufferFromReader(io.MultiReader(bytes.NewReader(signBlob), buf))
}

View File

@ -21,6 +21,7 @@ import (
container_service "code.gitea.io/gitea/services/packages/container"
debian_service "code.gitea.io/gitea/services/packages/debian"
rpm_service "code.gitea.io/gitea/services/packages/rpm"
alt_service "code.gitea.io/gitea/services/packages/alt"
)
// Task method to execute cleanup rules and cleanup expired package data
@ -137,6 +138,10 @@ func ExecuteCleanupRules(outerCtx context.Context) error {
if err := arch_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
return fmt.Errorf("CleanupRule [%d]: arch.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
}
} else if pcr.Type == packages_model.TypeAlt {
if err := alt_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil {
return fmt.Errorf("CleanupRule [%d]: alt.BuildAllRepositoryFiles failed: %w", pcr.ID, err)
}
}
}
return nil

View File

@ -244,7 +244,7 @@ func addFileToPackageWrapper(ctx context.Context, fn func(ctx context.Context) (
// NewPackageBlob creates a package blob instance
func NewPackageBlob(hsr packages_module.HashedSizeReader) *packages_model.PackageBlob {
hashMD5, hashSHA1, hashSHA256, hashSHA512 := hsr.Sums()
hashMD5, hashSHA1, hashSHA256, hashSHA512, hashBlake2b := hsr.Sums()
return &packages_model.PackageBlob{
Size: hsr.Size(),
@ -252,6 +252,7 @@ func NewPackageBlob(hsr packages_module.HashedSizeReader) *packages_model.Packag
HashSHA1: hex.EncodeToString(hashSHA1),
HashSHA256: hex.EncodeToString(hashSHA256),
HashSHA512: hex.EncodeToString(hashSHA512),
HashBlake2b:hex.EncodeToString(hashBlake2b),
}
}
@ -395,6 +396,8 @@ func CheckSizeQuotaExceeded(ctx context.Context, doer, owner *user_model.User, p
typeSpecificSize = setting.Packages.LimitSizePyPI
case packages_model.TypeRpm:
typeSpecificSize = setting.Packages.LimitSizeRpm
case packages_model.TypeAlt:
typeSpecificSize = setting.Packages.LimitSizeAlt
case packages_model.TypeRubyGems:
typeSpecificSize = setting.Packages.LimitSizeRubyGems
case packages_model.TypeSwift:

View File

@ -622,7 +622,7 @@ func addDataAsFileToRepo(ctx context.Context, pv *packages_model.PackageVersion,
return nil, err
}
_, _, hashSHA256, _ := content.Sums()
_, _, hashSHA256, _, _ := content.Sums()
return &repoData{
Type: filetype,

View File

@ -0,0 +1,49 @@
{{if eq .PackageDescriptor.Package.Type "alt"}}
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.installation"}}</h4>
<div class="ui attached segment">
<div class="ui form">
<div class="field">
<label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.alt.registry"}}</label>
<div class="markup"><pre class="code-block"><code>{{- if gt (len .Groups) 1 -}}
# {{ctx.Locale.Tr "packages.alt.repository.multiple_groups"}}
{{end -}}
# {{ctx.Locale.Tr "packages.alt.setup"}}
{{- range $group := .Groups}}
{{- if $group}}{{$group = print "/" $group}}{{end}}
apt-repo add rpm <origin-url data-url="{{AppSubUrl}}/api/packages/{{$.PackageDescriptor.Owner.Name}}/alt{{- if $group}}{{$group}}{{- else}}/alt{{- end}}.repo"></origin-url> _arch_ classic
{{- end}}</code></pre></div>
</div>
<div class="field">
<label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.alt.install"}}</label>
<div class="markup">
<pre class="code-block"><code># {{ctx.Locale.Tr "packages.alt.registry.install"}}
apt-get update
apt-get install {{$.PackageDescriptor.Package.Name}}</code></pre>
</div>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "packages.registry.documentation" "ALT" "https://docs.gitea.com/usage/packages/alt/"}}</label>
</div>
</div>
</div>
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.alt.repository"}}</h4>
<div class="ui attached segment">
<table class="ui single line very basic table">
<tbody>
<tr>
<td class="collapsing"><h5>{{ctx.Locale.Tr "packages.alt.repository.architectures"}}</h5></td>
<td>{{StringUtils.Join .Architectures ", "}}</td>
</tr>
</tbody>
</table>
</div>
{{if or .PackageDescriptor.Metadata.Summary .PackageDescriptor.Metadata.Description}}
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.about"}}</h4>
{{if .PackageDescriptor.Metadata.Summary}}<div class="ui attached segment">{{.PackageDescriptor.Metadata.Summary}}</div>{{end}}
{{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{.PackageDescriptor.Metadata.Description}}</div>{{end}}
{{end}}
{{end}}

View File

@ -0,0 +1,4 @@
{{if eq .PackageDescriptor.Package.Type "alt"}}
{{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "gt-mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}}
{{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{.locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}}
{{end}}

View File

@ -37,6 +37,7 @@
{{template "package/content/pub" .}}
{{template "package/content/pypi" .}}
{{template "package/content/rpm" .}}
{{template "package/content/alt" .}}
{{template "package/content/rubygems" .}}
{{template "package/content/swift" .}}
{{template "package/content/vagrant" .}}
@ -68,6 +69,7 @@
{{template "package/metadata/pub" .}}
{{template "package/metadata/pypi" .}}
{{template "package/metadata/rpm" .}}
{{template "package/metadata/alt" .}}
{{template "package/metadata/rubygems" .}}
{{template "package/metadata/swift" .}}
{{template "package/metadata/vagrant" .}}

View File

@ -4016,6 +4016,7 @@
"pub",
"pypi",
"rpm",
"alt",
"rubygems",
"swift",
"vagrant"

255
web_src/svg/gitea-alt.svg Normal file
View File

@ -0,0 +1,255 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FDC811;}
.st1{fill-rule:evenodd;clip-rule:evenodd;}
.st2{fill:#FFFFFF;}
.st3{fill:#FFC629;}
</style>
<g>
<path class="st0" d="M59.4301758,53.243782l4.1563721-5.9726563l-0.1383057-1.7323608
c-0.2910767-1.5082397-1.3920898-2.1431885-2.1206665-2.5633545l-0.0183716-0.0105591
c-0.0826416-0.0476685-0.1665039-0.0960083-0.2504883-0.1466064c-0.9364014-0.5628052-1.376709-1.0727539-1.4223633-1.1831665
l-0.046875-0.1455688c-0.3235474-0.8590088-0.0525513-2.2307129,0.2094727-3.557373l0.0053711-0.0269775
c0.117981-0.5983276,0.2293701-1.1635132,0.302002-1.7321167c0.901001-6.1165161,0.5557861-9.4356689-0.5584717-14.5588989
c-0.0568848-0.2745972-0.1121216-0.5448608-0.1668091-0.8113403l-0.0526733-0.2573242
c-0.4945679-2.4212036-0.9215698-4.512207-1.7373657-6.7763672c-0.1715698-0.473877-0.4075317-0.9042969-0.6196899-1.2913208
c-0.1323853-0.2420044-0.2692261-0.4922485-0.3555908-0.6970825l-0.1502075-0.2786865l-0.0116577-0.0185547
c-0.0631714-0.0996094-0.1762085-0.5687256-0.2133789-0.7232056l-0.013916-0.0565796
c-0.0991211-0.4724121-0.1328125-0.9918213-0.1655884-1.4961548c-0.0496826-0.7700195-0.1060181-1.6428828-0.3894043-2.5153193
c-0.3701172-1.1920166-1.2028809-2.3480225-2.2277222-3.0922854c-0.5145874-0.3737793-1.0682373-0.5390625-1.5135498-0.671936
L51.8637085,2.90815c-0.1146851-0.0344238-0.2159424-0.0574951-0.3165283-0.0803833
c-0.2425537-0.0551758-0.4714966-0.1072998-0.9468384-0.4008789c-0.1668091-0.1030884-0.3479614-0.2166138-0.5403442-0.3372192
c-0.7921753-0.4962157-1.7772217-1.1132813-2.7288208-1.6102294c-0.9656982-0.6225586-2.3862915-0.7607422-3.3961182,0.3307495
l-1.050293,1.1351929l1.1959229,0.9803466c1.859436,1.5242312,2.5354004,3.225281,2.6345215,3.4965212
c0.0152588,0.0749512,0.0307007,0.1482544,0.0456543,0.2197266c0.0094604,0.0443115,0.0215454,0.102478,0.0343018,0.1652222
c-1.7796021,1.2429194-2.6052856,2.6298213-2.9859009,3.5904536c-0.1320801,0.2521362-0.3170776,0.649292-0.5948486,1.2453613
l-0.0080566,0.017334c-0.3079834,0.6616211-0.7207642,1.5484619-1.1865845,2.4963989
c-1.1564331-0.3937378-2.2368164-0.5854492-3.2950439-0.5854492c-1.3127441,0-2.5141602,0.293396-3.6641235,0.8926392
c-2.2763062,1.0950317-4.0001831,2.9336548-5.1401978,5.4265747H9.8925171v10.9262695
c-0.5941162-0.1156616-1.1992188-0.1606445-1.7796021-0.1756592c-0.8712158-0.0236816-1.9017334-0.0324707-2.9365845,0.0844116
c-0.7351685,0.0834961-2.97229,0.3377686-4.0450439,2.1537476c-0.2897339,0.4960327-0.5153809,1.0943604-0.7098389,1.8831177
L0,36.4700394l1.2049561,0.1270142c-0.43573,0.444519-0.7553711,0.9666138-0.9402466,1.5411987
C0.1213989,38.5687332,0.048645,39.038826,0.048645,39.535347c0,1.1737671,0.4656372,2.1719971,1.4296875,3.057373
l0.0446167,0.0391846c0.973938,0.8185425,2.1550903,1.2164307,3.6106567,1.2164307
c0.756897,0,1.4492798-0.0865479,2.0899048-0.2609253h2.6690063v20.4125366h44.109436v-4.6799927l0.913208-1.3365479
l1.0172119,1.7949219H64L59.4301758,53.243782z"/>
<g>
<path class="st1" d="M5.6372313,35.1268883c-0.0615196,0.112545-0.1214924,0.2396698-0.18014,0.3768463l-3.5473537-0.3739777
c0.1528593-0.6188354,0.3238322-1.097847,0.5453898-1.4770126c0.5801809-0.9821014,1.8402765-1.2840652,2.8942773-1.4039001
c0.8974967-0.1011696,1.8176351-0.0997353,2.7217669-0.0751038c1.2615232,0.0325813,2.6965675,0.1908531,3.534091,1.2579956
c0.493701,0.6335297,0.7313929,1.5369911,0.7313929,2.3298912v4.2216377c0,0.1843376,0,0.355751,0.0052919,0.5128059
c0.010603,0.5258408,0.1717463,1.1058006,0.4377041,1.5581932H9.3358974
c-0.2635279-0.3405075-0.3951807-0.7159195-0.3951807-1.1467743c-0.1756115,0.1608124-0.3485727,0.299202-0.515008,0.4216881
c-0.98001,0.7304993-2.0744343,0.9859657-3.2921185,0.9859657c-1.1494272,0-1.9731448-0.3098068-2.6243434-0.8569641
c-0.5995091-0.5506897-0.9275383-1.1515236-0.9275383-1.9227791c0-0.3299065,0.0458357-0.6371727,0.1376264-0.9128494
c0.2673846-0.8308983,0.9712666-1.4104156,1.7725636-1.7050896c0.3366439-0.1243629,0.7330403-0.232048,1.1946001-0.3146629
c0.5418558-0.0917854,1.0792933-0.1983643,1.6155162-0.3127899c0.6294403-0.135849,1.262857-0.2966614,1.8843455-0.4908295
c0.2627544-0.083828,0.4975653-0.1652298,0.4975653-0.1904106c0-0.1743965-0.0067368-0.323391-0.0258446-0.4519501
c-0.0973043-0.6231461-0.8308964-0.802845-1.3772802-0.802845c-0.2123909,0-0.4258857,0.0252953-0.6328645,0.0772057
C6.1996298,34.5442772,5.8708277,34.71315,5.6372313,35.1268883z M8.6839256,37.4609756
c-0.1994677,0.0823936-0.3966169,0.1584892-0.5946493,0.2263069c-0.3727603,0.1302147-0.7679415,0.255352-1.1619077,0.3444824
c-0.3671188,0.0826149-0.7411027,0.1531906-1.0724454,0.3434944c-0.3178682,0.1827888-0.5506916,0.4470901-0.5506916,0.8528748
c0,0.6694221,0.6439095,1.0125847,1.2360182,1.0125847c0.7200074,0,1.5661459-0.3459244,1.9180317-1.0125847
c0.1737337-0.3248253,0.2256441-0.7212219,0.2256441-1.1668777V37.4609756z"/>
<rect x="14.3783417" y="28.7292137" class="st1" width="3.7205353" height="13.3250523"/>
<path class="st1" d="M24.7167435,28.7292137v3.6048965h2.075861v2.7956467h-2.075861v3.3771553
c0,0.3963966,0.0504742,0.7212219,0.1551781,0.8782768c0.0685883,0.1502113,0.2946835,0.304615,0.5995197,0.304615
c0.3264732,0,0.7023258-0.083828,1.1668682-0.2394485l0.3072643,2.5549812
c-0.9212418,0.1569481-1.7789688,0.3098068-2.6213608,0.3098068c-0.9247684,0-1.6298733-0.1528587-2.0091515-0.3777313
c-0.4444408-0.2570114-0.7718067-0.6362877-0.9925919-1.0792923c-0.2588882-0.4816628-0.3260403-1.2521439-0.3260403-2.3512077
v-3.3771553h-1.3731842v-2.7956467h1.3731842v-1.7486057L24.7167435,28.7292137z"/>
</g>
<g>
<rect x="14.5349178" y="44.961422" class="st1" width="3.7205353" height="13.3603954"/>
<path class="st1" d="M20.2926922,44.961422h3.7044106v2.4856224h-3.7044106V44.961422L20.2926922,44.961422z
M20.2926922,48.6153564h3.7044106v9.706459h-3.7044106V48.6153564z"/>
<path class="st1" d="M26.0343571,48.6153564h3.5009651v1.5954132c0.237463-0.3105774,0.4824352-0.5748787,0.7333717-0.8023987
c0.79953-0.7171364,1.684988-0.9652023,2.745615-0.9652023c0.9761353,0,1.8336487,0.3265953,2.3838997,0.9246674
c0.5988464,0.5509109,0.9255486,1.5463753,0.9255486,2.7978554v6.1561241h-3.7897835v-5.3341713
c0-0.3656921,0.0074005-0.7747917-0.1696396-1.1029282c-0.3026276-0.5627327-1.0557766-0.7265244-1.6147423-0.4763603
c-0.4485378,0.2024498-0.7572384,0.6706352-0.876852,1.1324158c-0.0799637,0.3060493-0.1184006,0.6891937-0.1184006,1.1525192
v4.6285248h-3.7199821V48.6153564z"/>
<path class="st1" d="M48.1863098,58.3218155h-3.4289665v-1.526825c-0.2003403,0.2207832-0.4000397,0.4222412-0.5935326,0.5985146
c-0.5867081,0.5323563-1.2222252,0.9002571-2.0041924,1.0591927c-0.2951164,0.0599709-0.6158562,0.0905647-0.95261,0.0905647
c-1.0270424,0-1.7821808-0.3233871-2.4002457-0.9234505c-0.5471573-0.6188354-0.8570709-1.524395-0.8570709-2.8328667v-6.1715889
h3.704628v5.401104c0,0.4129639,0.0903473,0.9127388,0.3945198,1.2161407
c0.5661545,0.5710106,1.4642029,0.4771309,2.0072746-0.0663795c0.4304123-0.4319611,0.4444389-1.2852783,0.4444389-1.8505478
v-4.7003174h3.6857567V58.3218155z"/>
<polygon class="st1" points="49.0792999,48.6153564 53.5573959,48.6153564 55.0304337,51.2908363 56.7809181,48.6153564
60.8766441,48.6153564 57.654335,53.2458687 61.2041206,58.3218155 56.7809181,58.3218155 55.0304337,55.2326012
52.920002,58.3218155 48.9075546,58.3218155 52.4591064,53.2458687 "/>
</g>
<g>
<g>
<path class="st2" d="M54.8775864,37.2458992c-0.4417419-0.9824486-0.928688-1.5612144-1.5432358-2.6015053
c-1.1544266-1.639679-0.9232979-4.588686-0.3833885-6.5147324c0.441124-1.8112373,1.098175-3.0455437,1.4552307-3.6656818
c0.357029-0.6201382-0.216114-0.8550396-0.4979973-0.4322166c-0.6305008,0.9457531-1.0098763,1.8198357-1.3552856,2.7800083
c-0.5591278,1.5642052-0.852951,3.3891621-0.7728996,5.0280304c0.0374641,0.9827881,0.1879196,2.3677902,0.1080589,2.8469772
c-0.0798645,0.4791946-0.4013977,0.5730209-0.6555176,0.4011917c-2.0794258-1.6592178-2.5048103-5.0322628-2.4234619-7.4789619
c0-2.5241375,0.9607658-5.223608,1.3034019-7.748579c0.1949272-0.7926693-0.1705627-3.0480099-0.1151466-4.3953466
c0.0221825-0.6126175,0.0156059-1.2263813-0.0004311-1.8390942c-0.026226-1.0058594,0.0336113-1.9613962,0.1155777-2.9612722
c0.0831223-0.8852139,0.7342987-1.1200619,1.3131027-0.7343006c0.2706451,0.1959944,0.3490791,0.445734,0.7147598,0.6759729
c1.0795784,0.5787106,1.910347-0.385829,1.6966019-1.3877802c-0.1728249-0.8285313-0.7127914-1.8497677-1.6174126-1.5014849
c-0.5391006,0.1899967-0.4622536,0.9824228-0.9071808,1.3280373c-0.6181297,0.3677969-1.2745895,0.0957804-1.8310204-0.2901974
c-0.3970032-0.2754536-0.5853806-0.5622816-1.1023293-0.3243341c-0.6526299,0.3003454-1.2847748,0.9363184-1.7352257,1.4860001
c-0.4319763,0.5271883-0.7538414,1.1364775-1.0013199,1.7693777c-0.7104568,1.717021-1.5416565,3.1989508-2.3485184,4.8783026
c1.1163292,0.692482,2.0791855,1.4835606,3.063591,2.3688164c1.6179504,1.4477386,2.5053749,3.7408409,2.0036583,5.8987846
c-0.172802,0.9452686-0.7894745,1.7349052-1.1357231,2.5827065c-0.0917778,0.5446548-0.1365471,1.287447-0.3454514,2.1325798
c-0.1357536,0.5491829-0.3476486,1.1181087-0.6765137,1.399992c-0.1867752,0.1600914-0.6599083,0.2370167-0.5355682-0.1315479
c0.1243477-0.3685532,0.2537003-1.4751644,0.0093918-2.5557003c-0.2442818-1.080534-0.9014931-2.1482525-1.7323837-3.3710766
c-0.5696259-0.838316-0.5884132-1.3926735-0.5884132-1.3926735s0.3505173-0.0961838,0.4189682-0.5350037
c0.038826-0.248703,0.2907753-0.7864571,0.8845139-0.8285179c0.538372,0,0.5366745,0.472271,1.1206818,0.6588306
c0.6764984,0.2161007,0.9221153-0.079258,1.0410614-0.4060993c0.2885666-0.7147713-0.1764374-2.1587105-0.7158623-2.5819244
c-0.3251305-0.3052521-0.8749466-0.462944-1.2937927-0.3434315c-0.325779,0.0929508-0.6381187,0.428093-0.5723228,0.8429508
c0.0657921,0.4148445,0.0842705,0.6324291-0.2067375,0.4589825c-0.1598625-0.095295-0.3085098-0.3068542-0.4138336-0.4589691
c-0.3002625-0.4336586-0.5667953-0.7106876-1.0237427-1.0256023c-0.5760002-0.3969479-1.0711403-0.5731583-1.748085-0.6325092
c-0.6769409-0.0593529-1.2068253,0.0157814-1.796196,0.2507763c-1.5036812,0.5966492-2.4664688,1.8880424-2.8726349,3.3162537
c-0.6154747,2.0802479,0.2061806,4.3003616,0,7.1108799c-0.1914673,2.6099854-0.4327431,3.0149803-0.8085861,4.236454
c-0.3426361,1.1135674-0.5825386,1.540947-0.9208069,1.954361c-0.2688217,0.3285789-0.5261116,0.3588486-0.7469673,0.0563736
c-0.2161179-0.2959633-0.1926117-2.0246429-0.1926117-2.5744858c0-0.9959793-0.0657806-1.7053757-0.1503487-2.1845589
c-0.0845528-0.4791965-0.5214729-0.4510174-0.5941544-0.0029373c-0.0726814,0.4480782-0.1410751,1.0856152-0.1411972,1.7458878
c-0.0003242,1.5904465,0.2643471,3.5357513,0.7408218,5.1168442c0.6953011,2.1996803,1.3123093,3.8146667,1.3123093,5.1853065
c-0.0396194,1.2930107-1.4469147,1.9852638-1.1596947,2.0248833c1.371109,0.0200958,3.008667,0.0755119,4.4938583,0.0200958
c0.4224472,0,0.3662224-0.0759964,0.7916183-0.5038185c0.4196854-0.5011406,0.1536636-1.0416145,0.0779114-1.4819717
c-0.3700485-2.6995392,0.9610062-5.8593941,1.7496223-8.3105125c0.7560501-2.2550697,1.4678726-4.6059475,1.9675941-7.382803
c0.0168343-0.1129627,0.1343651-0.1721268,0.2337685-0.137167c0.072506,0.0242043,0.1132355,0.1205769,0.1332588,0.1968422
c0.3289986,1.2756405,0.7511482,3.1794643,1.1760483,4.8378868c0.3081055,1.1963425,0.6363106,2.5615768,1.5014687,3.4888496
c0.8494339-0.079216,1.5628471,0.7140045,1.6765633,1.5612564c0.118084,0.8285179-0.1137161,1.6762924,0.3290787,2.5249329
c0.4634171,0.7883415,0.984539,1.4865532,1.2940483,2.1587372c0.4232559,0.8467102,0.7919273,1.8291473,0.519886,3.0868759
c1.4841003,0,2.5045662-0.1555634,3.6398849-0.8105774C55.2435112,42.8560524,56.284008,40.2936668,54.8775864,37.2458992z"/>
<path class="st2" d="M47.4192467,43.0482864c-0.5817566-0.9607544-1.0424767-2.6804848-1.0424767-3.7582283
c0-0.6159744-0.4424744-0.4058571-0.7715378-0.4058571c-0.3468666,0-0.6954727,0.0201225-0.964756-0.0936623
c-0.2117729-0.097168-0.1913719-0.2331657,0.040432-0.2719765c0.6922798-0.1735039,1.0991745,0.2719765,1.5799866-0.6743584
c0.1517639-0.3097229,0.1930428-0.6566734,0.0966454-0.9648094c-0.1132355-0.3648453-0.4994812-0.5570526-0.9422646-0.5787354
c-0.3111267,0-0.8689728,0.0762367-0.9091644,0.4622574c-0.0216827,0.2136765,0.0955391,0.5038452,0.4036484,0.5421448
c0,0.133812,0.0401878,0.307045-0.1113205,0.3656845c-0.2350616,0.0776253-0.7142067-0.1154022-0.6409187,0
c0.3269081,0.4620132,0.1734467,1.0033379,0.0412941,1.4849205c-0.1520729,0.3661919,0.0409698,0.9248772,0.3072968,1.4624023
c0.2489471,0.5250053,0.5592117,1.0228958,0.6555443,1.3489532c0.1735001,0.5817413,0.0367355,1.195507,0.1547661,1.6795807
c0.037468,0.2494698,0.2888107,0.3466263,0.3455887,0.6368217c0.0784492,0.1930428,0.3678246,0.1525879,0.443821,0.1525879
c0.3481064-0.073288,0.4243469-0.4427834,0.6792488-0.4427834c0.4403152-0.056736,1.0378265-0.056736,1.3264465,0.2901955
c0.1554947,0,0.2496719,0,0.2496719-0.0955238C48.3611984,43.9739418,47.7650909,43.6286659,47.4192467,43.0482864z"/>
</g>
<g>
<path class="st3" d="M46.1058998,19.8034763c-0.3251305-0.3052521-0.8749466-0.462944-1.2937927-0.3434315
c-0.325779,0.0929508-0.6381187,0.428093-0.5723228,0.8429508c0.0657921,0.4148445,0.0842705,0.6324291-0.2067375,0.4589825
c-0.1598625-0.095295-0.3085098-0.3068542-0.4138336-0.4589691c-0.3002625-0.4336586-0.5667953-0.7106876-1.0237427-1.0256023
c-0.5760002-0.3969479-1.0711403-0.5731583-1.748085-0.6325092c-0.0204582-0.0017929-0.0403481-0.0030727-0.0605392-0.0046101
c1.2716904,0.3518276,1.829525,1.1305199,2.1665955,2.1566772c0.2802811,0.7037621,0.1656456,1.571846,0.327919,2.5113068
c0.0173721,0.1005898,0.0459175,0.1866398,0.082859,0.2604523c-0.0067673-0.0473309-0.0076828-0.0725327-0.0076828-0.0725327
s0.3505173-0.0961838,0.4189682-0.5350037c0.038826-0.248703,0.2907753-0.7864571,0.8845139-0.8285179
c0.538372,0,0.5366745,0.472271,1.1206818,0.6588306c0.6764984,0.2161007,0.9221153-0.079258,1.0410614-0.4060993
C47.1103287,21.6706295,46.6453247,20.2266903,46.1058998,19.8034763z"/>
<path class="st3" d="M53.836956,9.2186089c-0.1728249-0.8285313-0.7127914-1.8497677-1.6174126-1.5014849
c-0.5391006,0.1899967-0.4622536,0.9824228-0.9071808,1.3280373c-0.6181297,0.3677969-1.2745895,0.0957804-1.8310204-0.2901974
c-0.3970032-0.2754536-0.5853806-0.5622816-1.1023293-0.3243341c-0.2152519,0.0990553-0.4280777,0.2350769-0.6330872,0.3920012
c0.0061989,0.0167923,0.0187607,0.032156,0.0394325,0.0455923c0.4212074,0.3639975,1.126503,0.2423143,1.5674133,1.1283112
c0.3878632,0.7674131,0.5869713,1.7535563,0.6470261,2.5209694c0-0.0027761,0.0005112-0.0056334,0.0005913-0.0084372
c0.018383-0.6128597,0.0609169-1.2199526,0.1121025-1.8443499c0.0831223-0.8852139,0.7342987-1.1200619,1.3131027-0.7343006
c0.2706451,0.1959944,0.3490791,0.445734,0.7147598,0.6759729C53.2199326,11.1850996,54.0507011,10.2205601,53.836956,9.2186089z
"/>
<path class="st3" d="M49.8489647,5.3000607c-1.0492287-1.5967526-2.8186073-2.9165828-3.917942-3.4380569
c1.5503502,1.2684563,2.6079369,2.6365471,3.2941246,3.6919208C49.594223,6.0575285,50.0221405,5.6309853,49.8489647,5.3000607z"
/>
<path class="st3" d="M45.6973457,26.4065361c0.2925148,1.3435078,0.7974396,2.5205231,0.2925148,4.3240395
c0.627079-0.9860764,0.6804237-2.650362,0.2547531-4.383028C46.166935,25.8002911,45.5606918,25.9757195,45.6973457,26.4065361z"
/>
</g>
<g>
<path class="st1" d="M60.3082008,44.0666504c-0.762413-0.458416-1.8714523-1.2809753-2.0747108-2.0412331l-0.0073051-0.0227089
c-0.4695053-1.2471504-0.1578407-2.8256683,0.1437187-4.3523293c0.1158447-0.5869141,0.2254677-1.1412048,0.2937965-1.6786652
c0.869957-5.8982582,0.5357857-9.1058865-0.5421486-14.0628014c-0.0753632-0.3637943-0.1481934-0.7201366-0.2198067-1.0701714
c-0.4837151-2.3678722-0.9016571-4.4128246-1.6804581-6.5739889c-0.1328278-0.3669348-0.331398-0.7295704-0.5235786-1.0802917
c-0.1513176-0.2764921-0.307972-0.5624847-0.4241447-0.838006l-0.0229073-0.042263
c-0.2103767-0.3080816-0.3247681-0.7820759-0.4255486-1.2001553l-0.0200806-0.0824518
c-0.1234741-0.5798149-0.1611557-1.1595497-0.1974602-1.7202005c-0.0465508-0.718668-0.094799-1.4618363-0.3270302-2.1774039
c-0.2501869-0.8055124-0.8374252-1.7244058-1.6927757-2.3456898c-0.332531-0.2415323-0.7466736-0.3564901-1.1420021-0.4749384
c-0.3284607-0.0983944-0.748642-0.104877-1.6107025-0.637321c-0.8620872-0.5324576-2.0999947-1.3389268-3.2460785-1.9308039
c0,0-0.9257278-0.6483991-1.5848389,0.0641364c2.4490433,2.0075953,3.1210785,4.2460766,3.1210785,4.2460766
c0.0200272,0.1019254,0.0407562,0.2008996,0.0610237,0.2968011c0.1833115,0.8723307,0.2343903,1.2377019-0.3541451,1.5232635
l-0.0828819,0.0532875c-1.6040573,1.083271-2.3067818,2.2738714-2.6141624,3.0881701
c-0.10923,0.1965332-0.319416,0.6480494-0.6071854,1.2658825c-0.4401436,0.9452-1.0946999,2.3509855-1.8043671,3.7265434
c-1.4607964-0.6534262-2.7709503-0.9715748-3.9988556-0.9715748c-1.0785255,0-2.0676193,0.2436352-3.0158386,0.740716
c-4.6369476,2.2190065-5.7889633,7.6167078-5.9388123,11.75385c-0.0805378,2.1570129,0.3133373,4.5337009,0.6941261,6.8321133
c0.4347897,2.6235161,0.8843918,5.3362885,0.6004219,7.6333237c-0.260252,1.7098122-1.2106819,2.6940956-2.9901009,3.0960274
c-0.2547932,0.0049057-0.4296951,0.0777779-0.520071,0.2167511c-0.0407944,0.0627594-0.0974388,0.1943893-0.0188675,0.3711128
l0.0634747,0.1429062h10.6231689c0.4728355,0,0.6843567-0.2222595,0.9293785-0.4795189
c0.1040688-0.1094208,0.222084-0.2333412,0.3817749-0.3647919l0.014431-0.0130196
c0.4712868-0.456974,0.3630409-1.0108986,0.2377052-1.6523705c-0.0770988-0.3949814-0.1645927-0.8427353-0.1645927-1.39328
c0-1.8929596,0.6549911-3.7649918,1.2885895-5.5753403c0.0807266-0.2308578,0.1609688-0.4603844,0.2393646-0.688076
c0.7574005-2.189167,1.2928886-3.9913216,1.6871414-5.6911182c0.019043,0.0888386,0.0384483,0.1788368,0.0581818,0.2697659
c0.1588097,0.7376556,0.3390083,1.5736675,0.5508537,2.3774261c0.2355461,0.8893127,0.6090851,2.1349792,1.1992493,3.0402069
c-0.0484085,0.0247574-0.0960197,0.0500679-0.1430817,0.0750275c-0.1051216,0.0557785-0.2044029,0.1085548-0.2920723,0.1451035
c-0.25877,0.097168-0.5205688,0.2052383-0.7736931,0.3097649c-0.2016258,0.0832214-0.3965683,0.1637993-0.5773773,0.2333412
c-0.1978378,0.076683-0.2868652,0.26548-0.2218552,0.4699783l0.0061455,0.0172348
c0.0450935,0.1113205,0.1412392,0.1998482,0.2657089,0.2457809c0.0887184,0.045578,0.225296,0.1166954,0.2530022,0.1531639
l0.024395,0.033329c0.3502502,0.4037247,0.4827118,0.5961189,0.4827118,1.0178642
c0,0.2429504-0.0535698,0.40905-0.1100922,0.5849533c-0.0753899,0.2341576-0.1533508,0.4763527-0.0782166,0.8293114
c0.0800514,0.4265289,0.2879601,0.8466988,0.5079155,1.2914734c0.2672462,0.5401001,0.5434151,1.0985146,0.5488853,1.6329803
c-0.0637741,0.4379959-0.288269,0.5852051-0.5974312,0.7879372c-0.1873131,0.1228561-0.3997078,0.2621002-0.5815659,0.4819183
l-0.0260506,0.038559c-0.0960922,0.1769791-0.2050095,0.3776207-0.2050095,0.733223
c0,0.0474129-0.0048523,0.1015358-0.0094604,0.1575584c-0.0178871,0.2170982-0.0381546,0.4631195,0.1145096,0.6288719
c0.05093,0.0553894,0.1406326,0.1207924,0.2829895,0.1285019c0.0285568,0.0052261,0.0570755,0.0077744,0.0852127,0.0077744
h0.0001907c0.2746849,0,0.4328232-0.2281761,0.5481987-0.3948059c0.0275307-0.0399323,0.0653763-0.0943909,0.0949821-0.1287041
c0.0713348,0.0464172,0.127182,0.1329498,0.200592,0.2554016c0.1102791,0.1833267,0.2613029,0.4345741,0.5823746,0.4345741
h16.9920425C61.8289833,44.8344154,61.0706139,44.5250664,60.3082008,44.0666504z M45.9310226,1.8620039
c1.0993347,0.521474,2.8687134,1.8413042,3.917942,3.4380569c0.1731758,0.3309245-0.2547417,0.7574677-0.6238174,0.2538638
C48.5389595,4.4985509,47.4813728,3.1304603,45.9310226,1.8620039z M48.1115265,44.2834244
c-0.28862-0.3469315-0.8861313-0.3469315-1.3264465-0.2901955c-0.2549019,0-0.3311424,0.3694954-0.6792488,0.4427834
c-0.0759964,0-0.3653717,0.0404549-0.443821-0.1525879c-0.056778-0.2901955-0.3081207-0.387352-0.3455887-0.6368217
c-0.1180305-0.4840736,0.018734-1.0978394-0.1547661-1.6795807c-0.0963326-0.3260574-0.4065971-0.8239479-0.6555443-1.3489532
c-0.2663269-0.5375252-0.4593697-1.0962105-0.3072968-1.4624023c0.1321526-0.4815826,0.285614-1.0229073-0.0412941-1.4849205
c-0.073288-0.1154022,0.4058571,0.0776253,0.6409187,0c0.1515083-0.0586395,0.1113205-0.2318726,0.1113205-0.3656845
c-0.3081093-0.0382996-0.4253311-0.3284683-0.4036484-0.5421448c0.0401917-0.3860207,0.5980377-0.4622574,0.9091644-0.4622574
c0.4427834,0.0216827,0.8290291,0.2138901,0.9422646,0.5787354c0.0963974,0.308136,0.0551186,0.6550865-0.0966454,0.9648094
c-0.4808121,0.9463348-0.8877068,0.5008545-1.5799866,0.6743584c-0.2318039,0.0388107-0.2522049,0.1748085-0.040432,0.2719765
c0.2692833,0.1137848,0.6178894,0.0936623,0.964756,0.0936623c0.3290634,0,0.7715378-0.2101173,0.7715378,0.4058571
c0,1.0777435,0.4607201,2.7974739,1.0424767,3.7582283c0.3458443,0.5803795,0.9419518,0.9256554,0.9419518,1.1396141
C48.3611984,44.2834244,48.2670212,44.2834244,48.1115265,44.2834244z M53.1019287,44.1295624
c-1.1353188,0.655014-2.1557846,0.8105774-3.6398849,0.8105774c0.2720413-1.2577286-0.0966301-2.2401657-0.519886-3.0868759
c-0.3095093-0.672184-0.8306313-1.3703957-1.2940483-2.1587372c-0.4427948-0.8486404-0.2109947-1.6964149-0.3290787-2.5249329
c-0.1137161-0.8472519-0.8271294-1.6404724-1.6765633-1.5612564c-0.8651581-0.9272728-1.1933632-2.2925072-1.5014687-3.4888496
c-0.4249001-1.6584225-0.8470497-3.5622463-1.1760483-4.8378868c-0.0200233-0.0762653-0.0607529-0.1726379-0.1332588-0.1968422
c-0.0994034-0.0349598-0.2169342,0.0242043-0.2337685,0.137167c-0.4997215,2.7768555-1.211544,5.1277332-1.9675941,7.382803
c-0.7886162,2.4511185-2.1196709,5.6109734-1.7496223,8.3105125c0.0757523,0.4403572,0.341774,0.9808311-0.0779114,1.4819717
c-0.425396,0.4278221-0.3691711,0.5038185-0.7916183,0.5038185c-1.4851913,0.0554161-3.1227493,0-4.4938583-0.0200958
c-0.28722-0.0396194,1.1200752-0.7318726,1.1596947-2.0248833c0-1.3706398-0.6170082-2.9856262-1.3123093-5.1853065
c-0.4764748-1.5810928-0.7411461-3.5263977-0.7408218-5.1168442c0.0001221-0.6602726,0.0685158-1.2978096,0.1411972-1.7458878
c0.0726814-0.4480801,0.5096016-0.4762592,0.5941544,0.0029373c0.084568,0.4791832,0.1503487,1.1885796,0.1503487,2.1845589
c0,0.5498428-0.0235062,2.2785225,0.1926117,2.5744858c0.2208557,0.302475,0.4781456,0.2722054,0.7469673-0.0563736
c0.3382683-0.413414,0.5781708-0.8407936,0.9208069-1.954361c0.375843-1.2214737,0.6171188-1.6264687,0.8085861-4.236454
c0.2061806-2.8105183-0.6154747-5.030632,0-7.1108799c0.4061661-1.4282112,1.3689537-2.7196045,2.8726349-3.3162537
c0.5893707-0.2349949,1.1192551-0.3101292,1.796196-0.2507763c0.6769447,0.059351,1.1720848,0.2355614,1.748085,0.6325092
c0.4569473,0.3149147,0.7234802,0.5919437,1.0237427,1.0256023c0.1053238,0.1521149,0.2539711,0.3636742,0.4138336,0.4589691
c0.291008,0.1734467,0.2725296-0.044138,0.2067375-0.4589825c-0.0657959-0.4148579,0.2465439-0.75,0.5723228-0.8429508
c0.4188461-0.1195126,0.9686623,0.0381794,1.2937927,0.3434315c0.5394249,0.423214,1.0044289,1.8671532,0.7158623,2.5819244
c-0.1189461,0.3268414-0.364563,0.6222-1.0410614,0.4060993c-0.5840073-0.1865597-0.5823097-0.6588306-1.1206818-0.6588306
c-0.5937386,0.0420609-0.8456879,0.5798149-0.8845139,0.8285179c-0.0684509,0.4388199-0.4189682,0.5350037-0.4189682,0.5350037
s0.0187874,0.5543575,0.5884132,1.3926735c0.8308907,1.2228241,1.488102,2.2905426,1.7323837,3.3710766
c0.2443085,1.0805359,0.1149559,2.1871471-0.0093918,2.5557003c-0.1243401,0.3685646,0.348793,0.2916393,0.5355682,0.1315479
c0.3288651-0.2818832,0.54076-0.8508091,0.6765137-1.399992c0.2089043-0.8451328,0.2536736-1.587925,0.3454514-2.1325798
c0.3462486-0.8478012,0.9629211-1.6374378,1.1357231-2.5827065c0.5017166-2.1579437-0.3857079-4.451046-2.0036583-5.8987846
c-0.9844055-0.8852558-1.9472618-1.6763344-3.063591-2.3688164c0.8068619-1.6793518,1.6380615-3.1612816,2.3485184-4.8783026
c0.2474785-0.6329002,0.5693436-1.2421894,1.0013199-1.7693777c0.4504509-0.5496817,1.0825958-1.1856546,1.7352257-1.4860001
c0.5169487-0.2379475,0.7053261,0.0488806,1.1023293,0.3243341c0.5564308,0.3859777,1.2128906,0.6579943,1.8310204,0.2901974
c0.4449272-0.3456144,0.3680801-1.1380405,0.9071808-1.3280373c0.9046211-0.3482828,1.4445877,0.6729536,1.6174126,1.5014849
c0.2137451,1.0019512-0.6170235,1.9664907-1.6966019,1.3877802c-0.3656807-0.2302389-0.4441147-0.4799786-0.7147598-0.6759729
c-0.578804-0.3857613-1.2299805-0.1509132-1.3131027,0.7343006c-0.0819664,0.999876-0.1418037,1.9554129-0.1155777,2.9612722
c0.016037,0.6127129,0.0226135,1.2264767,0.0004311,1.8390942c-0.0554161,1.3473368,0.3100739,3.6026773,0.1151466,4.3953466
c-0.3426361,2.524971-1.3034019,5.2244415-1.3034019,7.748579c-0.0813484,2.4466991,0.3440361,5.8197441,2.4234619,7.4789619
c0.2541199,0.1718292,0.5756531,0.0780029,0.6555176-0.4011917c0.0798607-0.479187-0.0705948-1.8641891-0.1080589-2.8469772
c-0.0800514-1.6388683,0.2137718-3.4638252,0.7728996-5.0280304c0.3454094-0.9601727,0.7247849-1.8342552,1.3552856-2.7800083
c0.2818832-0.422823,0.8550262-0.1879215,0.4979973,0.4322166c-0.3570557,0.6201382-1.0141068,1.8544445-1.4552307,3.6656818
c-0.5399094,1.9260464-0.7710381,4.8750534,0.3833885,6.5147324c0.6145477,1.0402908,1.1014938,1.6190567,1.5432358,2.6015053
C56.284008,40.2936668,55.2435112,42.8560524,53.1019287,44.1295624z M45.6973457,26.4065361
c-0.1366539-0.4308167,0.4695892-0.606245,0.5472679-0.0589886c0.4256706,1.732666,0.3723259,3.3969517-0.2547531,4.383028
C46.4947853,28.9270592,45.9898605,27.7500439,45.6973457,26.4065361z"/>
<path class="st1" d="M45.3441582,36.9398956c0.2541199,0,0.3114624,0.1784439,0.3907738,0.1197128
c0.1188393-0.1197128-0.1366539-0.3696823-0.2955322-0.3696823c-0.252491,0-0.4265442,0.1155624-0.4265442,0.2499695
C45.0128555,37.0596085,45.1869087,36.9398956,45.3441582,36.9398956z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 26 KiB