forgejo/models/packages/descriptor.go
Exploding Dragon f17194ca91
Some checks are pending
/ release (push) Waiting to run
testing / backend-checks (push) Waiting to run
testing / frontend-checks (push) Waiting to run
testing / test-unit (push) Blocked by required conditions
testing / test-remote-cacher (map[image:docker.io/valkey/valkey:7.2.5-alpine3.19 port:6379]) (push) Blocked by required conditions
testing / test-remote-cacher (map[image:ghcr.io/microsoft/garnet-alpine:1.0.14 port:6379]) (push) Blocked by required conditions
testing / test-remote-cacher (map[image:redis:7.2 port:6379]) (push) Blocked by required conditions
testing / test-remote-cacher (map[image:registry.redict.io/redict:7.3.0-scratch port:6379]) (push) Blocked by required conditions
testing / test-mysql (push) Blocked by required conditions
testing / test-pgsql (push) Blocked by required conditions
testing / test-sqlite (push) Blocked by required conditions
testing / security-check (push) Blocked by required conditions
Arch packages implementation (#4785)
This PR is from https://github.com/go-gitea/gitea/pull/31037

This PR was originally created by @d1nch8g , and the original source code comes from https://ion.lc/core/gitea.

This PR adds a package registry for [Arch Linux](https://archlinux.org/) packages with support for package files, [signatures](https://wiki.archlinux.org/title/Pacman/Package_signing), and automatic [pacman-database](https://archlinux.org/pacman/repo-add.8.html) management.

Features:

1. Push any ` tar.zst ` package and Gitea sign it.
2. Delete endpoint for specific package version and all related files
3. Supports trust levels with `SigLevel = Required`.
4. Package UI with instructions to connect to the new pacman database and visualised package metadata

![](/attachments/810ca6df-bd20-44c2-bdf7-95e94886d750)

You can follow [this tutorial](https://wiki.archlinux.org/title/Creating_packages) to build a *.pkg.tar.zst package for testing

docs pr: https://codeberg.org/forgejo/docs/pulls/791

Co-authored-by: d1nch8g@ion.lc
Co-authored-by: @KN4CK3R
Co-authored-by: @mahlzahn
Co-authored-by: @silverwind
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4785
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Exploding Dragon <explodingfkl@gmail.com>
Co-committed-by: Exploding Dragon <explodingfkl@gmail.com>
2024-08-04 06:16:29 +00:00

260 lines
7.2 KiB
Go

// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package packages
import (
"context"
"errors"
"fmt"
"net/url"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/packages/alpine"
"code.gitea.io/gitea/modules/packages/arch"
"code.gitea.io/gitea/modules/packages/cargo"
"code.gitea.io/gitea/modules/packages/chef"
"code.gitea.io/gitea/modules/packages/composer"
"code.gitea.io/gitea/modules/packages/conan"
"code.gitea.io/gitea/modules/packages/conda"
"code.gitea.io/gitea/modules/packages/container"
"code.gitea.io/gitea/modules/packages/cran"
"code.gitea.io/gitea/modules/packages/debian"
"code.gitea.io/gitea/modules/packages/helm"
"code.gitea.io/gitea/modules/packages/maven"
"code.gitea.io/gitea/modules/packages/npm"
"code.gitea.io/gitea/modules/packages/nuget"
"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/rubygems"
"code.gitea.io/gitea/modules/packages/swift"
"code.gitea.io/gitea/modules/packages/vagrant"
"code.gitea.io/gitea/modules/util"
"github.com/hashicorp/go-version"
)
// PackagePropertyList is a list of package properties
type PackagePropertyList []*PackageProperty
// GetByName gets the first property value with the specific name
func (l PackagePropertyList) GetByName(name string) string {
for _, pp := range l {
if pp.Name == name {
return pp.Value
}
}
return ""
}
// PackageDescriptor describes a package
type PackageDescriptor struct {
Package *Package
Owner *user_model.User
Repository *repo_model.Repository
Version *PackageVersion
SemVer *version.Version
Creator *user_model.User
PackageProperties PackagePropertyList
VersionProperties PackagePropertyList
Metadata any
Files []*PackageFileDescriptor
}
// PackageFileDescriptor describes a package file
type PackageFileDescriptor struct {
File *PackageFile
Blob *PackageBlob
Properties PackagePropertyList
}
// PackageWebLink returns the relative package web link
func (pd *PackageDescriptor) PackageWebLink() string {
return fmt.Sprintf("%s/-/packages/%s/%s", pd.Owner.HomeLink(), string(pd.Package.Type), url.PathEscape(pd.Package.LowerName))
}
// VersionWebLink returns the relative package version web link
func (pd *PackageDescriptor) VersionWebLink() string {
return fmt.Sprintf("%s/%s", pd.PackageWebLink(), url.PathEscape(pd.Version.LowerVersion))
}
// PackageHTMLURL returns the absolute package HTML URL
func (pd *PackageDescriptor) PackageHTMLURL() string {
return fmt.Sprintf("%s/-/packages/%s/%s", pd.Owner.HTMLURL(), string(pd.Package.Type), url.PathEscape(pd.Package.LowerName))
}
// VersionHTMLURL returns the absolute package version HTML URL
func (pd *PackageDescriptor) VersionHTMLURL() string {
return fmt.Sprintf("%s/%s", pd.PackageHTMLURL(), url.PathEscape(pd.Version.LowerVersion))
}
// CalculateBlobSize returns the total blobs size in bytes
func (pd *PackageDescriptor) CalculateBlobSize() int64 {
size := int64(0)
for _, f := range pd.Files {
size += f.Blob.Size
}
return size
}
// GetPackageDescriptor gets the package description for a version
func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDescriptor, error) {
p, err := GetPackageByID(ctx, pv.PackageID)
if err != nil {
return nil, err
}
o, err := user_model.GetUserByID(ctx, p.OwnerID)
if err != nil {
return nil, err
}
repository, err := repo_model.GetRepositoryByID(ctx, p.RepoID)
if err != nil && !repo_model.IsErrRepoNotExist(err) {
return nil, err
}
creator, err := user_model.GetUserByID(ctx, pv.CreatorID)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
creator = user_model.NewGhostUser()
} else {
return nil, err
}
}
var semVer *version.Version
if p.SemverCompatible {
semVer, err = version.NewVersion(pv.Version)
if err != nil {
return nil, err
}
}
pps, err := GetProperties(ctx, PropertyTypePackage, p.ID)
if err != nil {
return nil, err
}
pvps, err := GetProperties(ctx, PropertyTypeVersion, pv.ID)
if err != nil {
return nil, err
}
pfs, err := GetFilesByVersionID(ctx, pv.ID)
if err != nil {
return nil, err
}
pfds, err := GetPackageFileDescriptors(ctx, pfs)
if err != nil {
return nil, err
}
var metadata any
switch p.Type {
case TypeAlpine:
metadata = &alpine.VersionMetadata{}
case TypeArch:
metadata = &arch.VersionMetadata{}
case TypeCargo:
metadata = &cargo.Metadata{}
case TypeChef:
metadata = &chef.Metadata{}
case TypeComposer:
metadata = &composer.Metadata{}
case TypeConan:
metadata = &conan.Metadata{}
case TypeConda:
metadata = &conda.VersionMetadata{}
case TypeContainer:
metadata = &container.Metadata{}
case TypeCran:
metadata = &cran.Metadata{}
case TypeDebian:
metadata = &debian.Metadata{}
case TypeGeneric:
// generic packages have no metadata
case TypeGo:
// go packages have no metadata
case TypeHelm:
metadata = &helm.Metadata{}
case TypeNuGet:
metadata = &nuget.Metadata{}
case TypeNpm:
metadata = &npm.Metadata{}
case TypeMaven:
metadata = &maven.Metadata{}
case TypePub:
metadata = &pub.Metadata{}
case TypePyPI:
metadata = &pypi.Metadata{}
case TypeRpm:
metadata = &rpm.VersionMetadata{}
case TypeRubyGems:
metadata = &rubygems.Metadata{}
case TypeSwift:
metadata = &swift.Metadata{}
case TypeVagrant:
metadata = &vagrant.Metadata{}
default:
panic(fmt.Sprintf("unknown package type: %s", string(p.Type)))
}
if metadata != nil {
if err := json.Unmarshal([]byte(pv.MetadataJSON), &metadata); err != nil {
return nil, err
}
}
return &PackageDescriptor{
Package: p,
Owner: o,
Repository: repository,
Version: pv,
SemVer: semVer,
Creator: creator,
PackageProperties: PackagePropertyList(pps),
VersionProperties: PackagePropertyList(pvps),
Metadata: metadata,
Files: pfds,
}, nil
}
// GetPackageFileDescriptor gets a package file descriptor for a package file
func GetPackageFileDescriptor(ctx context.Context, pf *PackageFile) (*PackageFileDescriptor, error) {
pb, err := GetBlobByID(ctx, pf.BlobID)
if err != nil {
return nil, err
}
pfps, err := GetProperties(ctx, PropertyTypeFile, pf.ID)
if err != nil {
return nil, err
}
return &PackageFileDescriptor{
pf,
pb,
PackagePropertyList(pfps),
}, nil
}
// GetPackageFileDescriptors gets the package file descriptors for the package files
func GetPackageFileDescriptors(ctx context.Context, pfs []*PackageFile) ([]*PackageFileDescriptor, error) {
pfds := make([]*PackageFileDescriptor, 0, len(pfs))
for _, pf := range pfs {
pfd, err := GetPackageFileDescriptor(ctx, pf)
if err != nil {
return nil, err
}
pfds = append(pfds, pfd)
}
return pfds, nil
}
// GetPackageDescriptors gets the package descriptions for the versions
func GetPackageDescriptors(ctx context.Context, pvs []*PackageVersion) ([]*PackageDescriptor, error) {
pds := make([]*PackageDescriptor, 0, len(pvs))
for _, pv := range pvs {
pd, err := GetPackageDescriptor(ctx, pv)
if err != nil {
return nil, err
}
pds = append(pds, pd)
}
return pds, nil
}