Skip to content

Commit fcb84f1

Browse files
fix anything ...
1 parent 2fc347b commit fcb84f1

File tree

18 files changed

+1906
-0
lines changed

18 files changed

+1906
-0
lines changed

models/packages/descriptor.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
user_model "code.gitea.io/gitea/models/user"
1414
"code.gitea.io/gitea/modules/json"
1515
"code.gitea.io/gitea/modules/packages/alpine"
16+
"code.gitea.io/gitea/modules/packages/arch"
1617
"code.gitea.io/gitea/modules/packages/cargo"
1718
"code.gitea.io/gitea/modules/packages/chef"
1819
"code.gitea.io/gitea/modules/packages/composer"
@@ -150,6 +151,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
150151
switch p.Type {
151152
case TypeAlpine:
152153
metadata = &alpine.VersionMetadata{}
154+
case TypeArch:
155+
metadata = &arch.VersionMetadata{}
153156
case TypeCargo:
154157
metadata = &cargo.Metadata{}
155158
case TypeChef:

models/packages/package.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type Type string
3131
// List of supported packages
3232
const (
3333
TypeAlpine Type = "alpine"
34+
TypeArch Type = "arch"
3435
TypeCargo Type = "cargo"
3536
TypeChef Type = "chef"
3637
TypeComposer Type = "composer"
@@ -55,6 +56,7 @@ const (
5556

5657
var TypeList = []Type{
5758
TypeAlpine,
59+
TypeArch,
5860
TypeCargo,
5961
TypeChef,
6062
TypeComposer,
@@ -82,6 +84,8 @@ func (pt Type) Name() string {
8284
switch pt {
8385
case TypeAlpine:
8486
return "Alpine"
87+
case TypeArch:
88+
return "Arch"
8589
case TypeCargo:
8690
return "Cargo"
8791
case TypeChef:
@@ -131,6 +135,8 @@ func (pt Type) SVGName() string {
131135
switch pt {
132136
case TypeAlpine:
133137
return "gitea-alpine"
138+
case TypeArch:
139+
return "gitea-arch"
134140
case TypeCargo:
135141
return "gitea-cargo"
136142
case TypeChef:

modules/packages/arch/metadata.go

Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package arch
5+
6+
import (
7+
"bufio"
8+
"bytes"
9+
"encoding/hex"
10+
"errors"
11+
"fmt"
12+
"io"
13+
"regexp"
14+
"strconv"
15+
"strings"
16+
17+
"code.gitea.io/gitea/modules/packages"
18+
"code.gitea.io/gitea/modules/util"
19+
"code.gitea.io/gitea/modules/validation"
20+
21+
"github.com/mholt/archiver/v3"
22+
)
23+
24+
// Arch Linux Packages
25+
// https://man.archlinux.org/man/PKGBUILD.5
26+
27+
const (
28+
PropertyDescription = "arch.description"
29+
PropertyArch = "arch.architecture"
30+
PropertyDistribution = "arch.distribution"
31+
32+
SettingKeyPrivate = "arch.key.private"
33+
SettingKeyPublic = "arch.key.public"
34+
35+
RepositoryPackage = "_arch"
36+
RepositoryVersion = "_repository"
37+
)
38+
39+
var (
40+
reName = regexp.MustCompile(`^[a-zA-Z0-9@._+-]+$`)
41+
reVer = regexp.MustCompile(`^[a-zA-Z0-9:_.+]+-+[0-9]+$`)
42+
reOptDep = regexp.MustCompile(`^[a-zA-Z0-9@._+-]+$|^[a-zA-Z0-9@._+-]+(:.*)`)
43+
rePkgVer = regexp.MustCompile(`^[a-zA-Z0-9@._+-]+$|^[a-zA-Z0-9@._+-]+(>.*)|^[a-zA-Z0-9@._+-]+(<.*)|^[a-zA-Z0-9@._+-]+(=.*)`)
44+
)
45+
46+
type Package struct {
47+
Name string `json:"name"`
48+
Version string `json:"version"` // Includes version, release and epoch
49+
VersionMetadata VersionMetadata
50+
FileMetadata FileMetadata
51+
}
52+
53+
// Arch package metadata related to specific version.
54+
// Version metadata the same across different architectures and distributions.
55+
type VersionMetadata struct {
56+
Base string `json:"base"`
57+
Description string `json:"description"`
58+
ProjectURL string `json:"project_url"`
59+
Groups []string `json:"groups,omitempty"`
60+
Provides []string `json:"provides,omitempty"`
61+
License []string `json:"license,omitempty"`
62+
Depends []string `json:"depends,omitempty"`
63+
OptDepends []string `json:"opt_depends,omitempty"`
64+
MakeDepends []string `json:"make_depends,omitempty"`
65+
CheckDepends []string `json:"check_depends,omitempty"`
66+
Conflicts []string `json:"conflicts,omitempty"`
67+
Replaces []string `json:"replaces,omitempty"`
68+
Backup []string `json:"backup,omitempty"`
69+
Xdata []string `json:"xdata,omitempty"`
70+
}
71+
72+
// FileMetadata Metadata related to specific package file.
73+
// This metadata might vary for different architecture and distribution.
74+
type FileMetadata struct {
75+
CompressedSize int64 `json:"compressed_size"`
76+
InstalledSize int64 `json:"installed_size"`
77+
MD5 string `json:"md5"`
78+
SHA256 string `json:"sha256"`
79+
BuildDate int64 `json:"build_date"`
80+
Packager string `json:"packager"`
81+
Arch string `json:"arch"`
82+
PgpSigned string `json:"pgp"`
83+
}
84+
85+
// ParsePackage Function that receives arch package archive data and returns it's metadata.
86+
func ParsePackage(r *packages.HashedBuffer) (*Package, error) {
87+
md5, _, sha256, _ := r.Sums()
88+
_, err := r.Seek(0, io.SeekStart)
89+
if err != nil {
90+
return nil, err
91+
}
92+
zstd := archiver.NewTarZstd()
93+
err = zstd.Open(r, 0)
94+
if err != nil {
95+
return nil, err
96+
}
97+
defer zstd.Close()
98+
99+
var pkg *Package
100+
var mtree bool
101+
102+
for {
103+
f, err := zstd.Read()
104+
if err == io.EOF {
105+
break
106+
}
107+
if err != nil {
108+
return nil, err
109+
}
110+
defer f.Close()
111+
112+
switch f.Name() {
113+
case ".PKGINFO":
114+
pkg, err = ParsePackageInfo(f)
115+
if err != nil {
116+
return nil, err
117+
}
118+
case ".MTREE":
119+
mtree = true
120+
}
121+
}
122+
123+
if pkg == nil {
124+
return nil, util.NewInvalidArgumentErrorf(".PKGINFO file not found")
125+
}
126+
127+
if !mtree {
128+
return nil, util.NewInvalidArgumentErrorf(".MTREE file not found")
129+
}
130+
131+
pkg.FileMetadata.CompressedSize = r.Size()
132+
pkg.FileMetadata.MD5 = hex.EncodeToString(md5)
133+
pkg.FileMetadata.SHA256 = hex.EncodeToString(sha256)
134+
135+
return pkg, nil
136+
}
137+
138+
// ParsePackageInfo Function that accepts reader for .PKGINFO file from package archive,
139+
// validates all field according to PKGBUILD spec and returns package.
140+
func ParsePackageInfo(r io.Reader) (*Package, error) {
141+
p := &Package{}
142+
143+
scanner := bufio.NewScanner(r)
144+
for scanner.Scan() {
145+
line := scanner.Text()
146+
147+
if strings.HasPrefix(line, "#") {
148+
continue
149+
}
150+
151+
key, value, find := strings.Cut(line, "=")
152+
if !find {
153+
continue
154+
}
155+
key = strings.TrimSpace(key)
156+
value = strings.TrimSpace(value)
157+
switch key {
158+
case "pkgname":
159+
p.Name = value
160+
case "pkgbase":
161+
p.VersionMetadata.Base = value
162+
case "pkgver":
163+
p.Version = value
164+
case "pkgdesc":
165+
p.VersionMetadata.Description = value
166+
case "url":
167+
p.VersionMetadata.ProjectURL = value
168+
case "packager":
169+
p.FileMetadata.Packager = value
170+
case "arch":
171+
p.FileMetadata.Arch = value
172+
case "provides":
173+
p.VersionMetadata.Provides = append(p.VersionMetadata.Provides, value)
174+
case "license":
175+
p.VersionMetadata.License = append(p.VersionMetadata.License, value)
176+
case "depend":
177+
p.VersionMetadata.Depends = append(p.VersionMetadata.Depends, value)
178+
case "optdepend":
179+
p.VersionMetadata.OptDepends = append(p.VersionMetadata.OptDepends, value)
180+
case "makedepend":
181+
p.VersionMetadata.MakeDepends = append(p.VersionMetadata.MakeDepends, value)
182+
case "checkdepend":
183+
p.VersionMetadata.CheckDepends = append(p.VersionMetadata.CheckDepends, value)
184+
case "backup":
185+
p.VersionMetadata.Backup = append(p.VersionMetadata.Backup, value)
186+
case "group":
187+
p.VersionMetadata.Groups = append(p.VersionMetadata.Groups, value)
188+
case "conflict":
189+
p.VersionMetadata.Conflicts = append(p.VersionMetadata.Conflicts, value)
190+
case "replaces":
191+
p.VersionMetadata.Replaces = append(p.VersionMetadata.Replaces, value)
192+
case "xdata":
193+
p.VersionMetadata.Xdata = append(p.VersionMetadata.Xdata, value)
194+
case "builddate":
195+
bd, err := strconv.ParseInt(value, 10, 64)
196+
if err != nil {
197+
return nil, err
198+
}
199+
p.FileMetadata.BuildDate = bd
200+
case "size":
201+
is, err := strconv.ParseInt(value, 10, 64)
202+
if err != nil {
203+
return nil, err
204+
}
205+
p.FileMetadata.InstalledSize = is
206+
default:
207+
return nil, util.NewInvalidArgumentErrorf("property is not supported %s", key)
208+
}
209+
}
210+
211+
return p, errors.Join(scanner.Err(), ValidatePackageSpec(p))
212+
}
213+
214+
// ValidatePackageSpec Arch package validation according to PKGBUILD specification.
215+
func ValidatePackageSpec(p *Package) error {
216+
if !reName.MatchString(p.Name) {
217+
return util.NewInvalidArgumentErrorf("invalid package name")
218+
}
219+
if !reName.MatchString(p.VersionMetadata.Base) {
220+
return util.NewInvalidArgumentErrorf("invalid package base")
221+
}
222+
if !reVer.MatchString(p.Version) {
223+
return util.NewInvalidArgumentErrorf("invalid package version")
224+
}
225+
if p.FileMetadata.Arch == "" {
226+
return util.NewInvalidArgumentErrorf("architecture should be specified")
227+
}
228+
if p.VersionMetadata.ProjectURL != "" {
229+
if !validation.IsValidURL(p.VersionMetadata.ProjectURL) {
230+
return util.NewInvalidArgumentErrorf("invalid project URL")
231+
}
232+
}
233+
for _, cd := range p.VersionMetadata.CheckDepends {
234+
if !rePkgVer.MatchString(cd) {
235+
return util.NewInvalidArgumentErrorf("invalid check dependency: " + cd)
236+
}
237+
}
238+
for _, d := range p.VersionMetadata.Depends {
239+
if !rePkgVer.MatchString(d) {
240+
return util.NewInvalidArgumentErrorf("invalid dependency: " + d)
241+
}
242+
}
243+
for _, md := range p.VersionMetadata.MakeDepends {
244+
if !rePkgVer.MatchString(md) {
245+
return util.NewInvalidArgumentErrorf("invalid make dependency: " + md)
246+
}
247+
}
248+
for _, p := range p.VersionMetadata.Provides {
249+
if !rePkgVer.MatchString(p) {
250+
return util.NewInvalidArgumentErrorf("invalid provides: " + p)
251+
}
252+
}
253+
for _, p := range p.VersionMetadata.Conflicts {
254+
if !rePkgVer.MatchString(p) {
255+
return util.NewInvalidArgumentErrorf("invalid conflicts: " + p)
256+
}
257+
}
258+
for _, p := range p.VersionMetadata.Replaces {
259+
if !rePkgVer.MatchString(p) {
260+
return util.NewInvalidArgumentErrorf("invalid replaces: " + p)
261+
}
262+
}
263+
for _, p := range p.VersionMetadata.Replaces {
264+
if !rePkgVer.MatchString(p) {
265+
return util.NewInvalidArgumentErrorf("invalid xdata: " + p)
266+
}
267+
}
268+
for _, od := range p.VersionMetadata.OptDepends {
269+
if !reOptDep.MatchString(od) {
270+
return util.NewInvalidArgumentErrorf("invalid optional dependency: " + od)
271+
}
272+
}
273+
for _, bf := range p.VersionMetadata.Backup {
274+
if strings.HasPrefix(bf, "/") {
275+
return util.NewInvalidArgumentErrorf("backup file contains leading forward slash")
276+
}
277+
}
278+
return nil
279+
}
280+
281+
// Desc Create pacman package description file.
282+
func (p *Package) Desc() string {
283+
entries := []string{
284+
"FILENAME", fmt.Sprintf("%s-%s-%s.pkg.tar.zst", p.Name, p.Version, p.FileMetadata.Arch),
285+
"NAME", p.Name,
286+
"BASE", p.VersionMetadata.Base,
287+
"VERSION", p.Version,
288+
"DESC", p.VersionMetadata.Description,
289+
"GROUPS", strings.Join(p.VersionMetadata.Groups, "\n"),
290+
"CSIZE", fmt.Sprintf("%d", p.FileMetadata.CompressedSize),
291+
"ISIZE", fmt.Sprintf("%d", p.FileMetadata.InstalledSize),
292+
"MD5SUM", p.FileMetadata.MD5,
293+
"SHA256SUM", p.FileMetadata.SHA256,
294+
"PGPSIG", p.FileMetadata.PgpSigned,
295+
"URL", p.VersionMetadata.ProjectURL,
296+
"LICENSE", strings.Join(p.VersionMetadata.License, "\n"),
297+
"ARCH", p.FileMetadata.Arch,
298+
"BUILDDATE", fmt.Sprintf("%d", p.FileMetadata.BuildDate),
299+
"PACKAGER", p.FileMetadata.Packager,
300+
"REPLACES", strings.Join(p.VersionMetadata.Replaces, "\n"),
301+
"CONFLICTS", strings.Join(p.VersionMetadata.Conflicts, "\n"),
302+
"PROVIDES", strings.Join(p.VersionMetadata.Provides, "\n"),
303+
"DEPENDS", strings.Join(p.VersionMetadata.Depends, "\n"),
304+
"OPTDEPENDS", strings.Join(p.VersionMetadata.OptDepends, "\n"),
305+
"MAKEDEPENDS", strings.Join(p.VersionMetadata.MakeDepends, "\n"),
306+
"CHECKDEPENDS", strings.Join(p.VersionMetadata.CheckDepends, "\n"),
307+
}
308+
309+
var buf bytes.Buffer
310+
for i := 0; i < len(entries); i += 2 {
311+
if entries[i+1] != "" {
312+
_, _ = fmt.Fprintf(&buf, "%%%s%%\n%s\n\n", entries[i], entries[i+1])
313+
}
314+
}
315+
return buf.String()
316+
}

0 commit comments

Comments
 (0)