Skip to content

Commit d987ac6

Browse files
KN4CK3Rlunny
andauthored
Add Chef package registry (#22554)
This PR implements a [Chef registry](https://chef.io/) to manage cookbooks. This package type was a bit complicated because Chef uses RSA signed requests as authentication with the registry. ![grafik](https://user-images.githubusercontent.com/1666336/213747995-46819fd8-c3d6-45a2-afd4-a4c3c8505a4a.png) ![grafik](https://user-images.githubusercontent.com/1666336/213748145-d01c9e81-d4dd-41e3-a3cc-8241862c3166.png) Co-authored-by: Lunny Xiao <[email protected]>
1 parent ff18d17 commit d987ac6

File tree

31 files changed

+1737
-20
lines changed

31 files changed

+1737
-20
lines changed

Diff for: custom/conf/app.example.ini

+2
Original file line numberDiff line numberDiff line change
@@ -2460,6 +2460,8 @@ ROUTER = console
24602460
;LIMIT_TOTAL_OWNER_SIZE = -1
24612461
;; Maximum size of a Cargo upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
24622462
;LIMIT_SIZE_CARGO = -1
2463+
;; Maximum size of a Chef upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
2464+
;LIMIT_SIZE_CHEF = -1
24632465
;; Maximum size of a Composer upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
24642466
;LIMIT_SIZE_COMPOSER = -1
24652467
;; Maximum size of a Conan upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)

Diff for: docs/content/doc/advanced/config-cheat-sheet.en-us.md

+1
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,7 @@ Task queue configuration has been moved to `queue.task`. However, the below conf
12141214
- `LIMIT_TOTAL_OWNER_COUNT`: **-1**: Maximum count of package versions a single owner can have (`-1` means no limits)
12151215
- `LIMIT_TOTAL_OWNER_SIZE`: **-1**: Maximum size of packages a single owner can use (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
12161216
- `LIMIT_SIZE_CARGO`: **-1**: Maximum size of a Cargo upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
1217+
- `LIMIT_SIZE_CHEF`: **-1**: Maximum size of a Chef upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
12171218
- `LIMIT_SIZE_COMPOSER`: **-1**: Maximum size of a Composer upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
12181219
- `LIMIT_SIZE_CONAN`: **-1**: Maximum size of a Conan upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
12191220
- `LIMIT_SIZE_CONDA`: **-1**: Maximum size of a Conda upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)

Diff for: docs/content/doc/packages/chef.en-us.md

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
---
2+
date: "2023-01-20T00:00:00+00:00"
3+
title: "Chef Packages Repository"
4+
slug: "packages/chef"
5+
draft: false
6+
toc: false
7+
menu:
8+
sidebar:
9+
parent: "packages"
10+
name: "Chef"
11+
weight: 5
12+
identifier: "chef"
13+
---
14+
15+
# Chef Packages Repository
16+
17+
Publish [Chef](https://chef.io/) cookbooks for your user or organization.
18+
19+
**Table of Contents**
20+
21+
{{< toc >}}
22+
23+
## Requirements
24+
25+
To work with the Chef package registry, you have to use [`knife`](https://docs.chef.io/workstation/knife/).
26+
27+
## Authentication
28+
29+
The Chef package registry does not use an username:password authentication but signed requests with a private:public key pair.
30+
Visit the package owner settings page to create the necessary key pair.
31+
Only the public key is stored inside Gitea. if you loose access to the private key you must re-generate the key pair.
32+
[Configure `knife`](https://docs.chef.io/workstation/knife_setup/) to use the downloaded private key with your Gitea username as `client_name`.
33+
34+
## Configure the package registry
35+
36+
To [configure `knife`](https://docs.chef.io/workstation/knife_setup/) to use the Gitea package registry add the url to the `~/.chef/config.rb` file.
37+
38+
```
39+
knife[:supermarket_site] = 'https://gitea.example.com/api/packages/{owner}/chef'
40+
```
41+
42+
| Parameter | Description |
43+
| --------- | ----------- |
44+
| `owner` | The owner of the package. |
45+
46+
## Publish a package
47+
48+
To publish a Chef package execute the following command:
49+
50+
```shell
51+
knife supermarket share {package_name}
52+
```
53+
54+
| Parameter | Description |
55+
| -------------- | ----------- |
56+
| `package_name` | The package name. |
57+
58+
You cannot publish a package if a package of the same name and version already exists. You must delete the existing package first.
59+
60+
## Install a package
61+
62+
To install a package from the package registry, execute the following command:
63+
64+
```shell
65+
knife supermarket install {package_name}
66+
```
67+
68+
Optional you can specify the package version:
69+
70+
```shell
71+
knife supermarket install {package_name} {package_version}
72+
```
73+
74+
| Parameter | Description |
75+
| ----------------- | ----------- |
76+
| `package_name` | The package name. |
77+
| `package_version` | The package version. |
78+
79+
## Delete a package
80+
81+
If you want to remove a package from the registry, execute the following command:
82+
83+
```shell
84+
knife supermarket unshare {package_name}
85+
```
86+
87+
Optional you can specify the package version:
88+
89+
```shell
90+
knife supermarket unshare {package_name}/versions/{package_version}
91+
```
92+
93+
| Parameter | Description |
94+
| ----------------- | ----------- |
95+
| `package_name` | The package name. |
96+
| `package_version` | The package version. |

Diff for: docs/content/doc/packages/overview.en-us.md

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ The following package managers are currently supported:
2727
| Name | Language | Package client |
2828
| ---- | -------- | -------------- |
2929
| [Cargo]({{< relref "doc/packages/cargo.en-us.md" >}}) | Rust | `cargo` |
30+
| [Chef]({{< relref "doc/packages/chef.en-us.md" >}}) | - | `knife` |
3031
| [Composer]({{< relref "doc/packages/composer.en-us.md" >}}) | PHP | `composer` |
3132
| [Conan]({{< relref "doc/packages/conan.en-us.md" >}}) | C++ | `conan` |
3233
| [Conda]({{< relref "doc/packages/conda.en-us.md" >}}) | - | `conda` |

Diff for: models/packages/descriptor.go

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
user_model "code.gitea.io/gitea/models/user"
1313
"code.gitea.io/gitea/modules/json"
1414
"code.gitea.io/gitea/modules/packages/cargo"
15+
"code.gitea.io/gitea/modules/packages/chef"
1516
"code.gitea.io/gitea/modules/packages/composer"
1617
"code.gitea.io/gitea/modules/packages/conan"
1718
"code.gitea.io/gitea/modules/packages/conda"
@@ -132,6 +133,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
132133
switch p.Type {
133134
case TypeCargo:
134135
metadata = &cargo.Metadata{}
136+
case TypeChef:
137+
metadata = &chef.Metadata{}
135138
case TypeComposer:
136139
metadata = &composer.Metadata{}
137140
case TypeConan:

Diff for: models/packages/package.go

+6
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type Type string
3131
// List of supported packages
3232
const (
3333
TypeCargo Type = "cargo"
34+
TypeChef Type = "chef"
3435
TypeComposer Type = "composer"
3536
TypeConan Type = "conan"
3637
TypeConda Type = "conda"
@@ -48,6 +49,7 @@ const (
4849

4950
var TypeList = []Type{
5051
TypeCargo,
52+
TypeChef,
5153
TypeComposer,
5254
TypeConan,
5355
TypeConda,
@@ -68,6 +70,8 @@ func (pt Type) Name() string {
6870
switch pt {
6971
case TypeCargo:
7072
return "Cargo"
73+
case TypeChef:
74+
return "Chef"
7175
case TypeComposer:
7276
return "Composer"
7377
case TypeConan:
@@ -103,6 +107,8 @@ func (pt Type) SVGName() string {
103107
switch pt {
104108
case TypeCargo:
105109
return "gitea-cargo"
110+
case TypeChef:
111+
return "gitea-chef"
106112
case TypeComposer:
107113
return "gitea-composer"
108114
case TypeConan:

Diff for: modules/activitypub/user_settings.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@ package activitypub
55

66
import (
77
user_model "code.gitea.io/gitea/models/user"
8+
"code.gitea.io/gitea/modules/util"
89
)
910

11+
const rsaBits = 2048
12+
1013
// GetKeyPair function returns a user's private and public keys
1114
func GetKeyPair(user *user_model.User) (pub, priv string, err error) {
1215
var settings map[string]*user_model.Setting
1316
settings, err = user_model.GetSettings(user.ID, []string{user_model.UserActivityPubPrivPem, user_model.UserActivityPubPubPem})
1417
if err != nil {
1518
return
1619
} else if len(settings) == 0 {
17-
if priv, pub, err = GenerateKeyPair(); err != nil {
20+
if priv, pub, err = util.GenerateKeyPair(rsaBits); err != nil {
1821
return
1922
}
2023
if err = user_model.SetUserSetting(user.ID, user_model.UserActivityPubPrivPem, priv); err != nil {

Diff for: modules/packages/chef/metadata.go

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package chef
5+
6+
import (
7+
"archive/tar"
8+
"compress/gzip"
9+
"io"
10+
"regexp"
11+
"strings"
12+
13+
"code.gitea.io/gitea/modules/json"
14+
"code.gitea.io/gitea/modules/util"
15+
"code.gitea.io/gitea/modules/validation"
16+
)
17+
18+
const (
19+
KeyBits = 4096
20+
SettingPublicPem = "chef.public_pem"
21+
)
22+
23+
var (
24+
ErrMissingMetadataFile = util.NewInvalidArgumentErrorf("metadata.json file is missing")
25+
ErrInvalidName = util.NewInvalidArgumentErrorf("package name is invalid")
26+
ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid")
27+
28+
namePattern = regexp.MustCompile(`\A\S+\z`)
29+
versionPattern = regexp.MustCompile(`\A\d+\.\d+(?:\.\d+)?\z`)
30+
)
31+
32+
// Package represents a Chef package
33+
type Package struct {
34+
Name string
35+
Version string
36+
Metadata *Metadata
37+
}
38+
39+
// Metadata represents the metadata of a Chef package
40+
type Metadata struct {
41+
Description string `json:"description,omitempty"`
42+
LongDescription string `json:"long_description,omitempty"`
43+
Author string `json:"author,omitempty"`
44+
License string `json:"license,omitempty"`
45+
RepositoryURL string `json:"repository_url,omitempty"`
46+
Dependencies map[string]string `json:"dependencies,omitempty"`
47+
}
48+
49+
type chefMetadata struct {
50+
Name string `json:"name"`
51+
Description string `json:"description"`
52+
LongDescription string `json:"long_description"`
53+
Maintainer string `json:"maintainer"`
54+
MaintainerEmail string `json:"maintainer_email"`
55+
License string `json:"license"`
56+
Platforms map[string]string `json:"platforms"`
57+
Dependencies map[string]string `json:"dependencies"`
58+
Providing map[string]string `json:"providing"`
59+
Recipes map[string]string `json:"recipes"`
60+
Version string `json:"version"`
61+
SourceURL string `json:"source_url"`
62+
IssuesURL string `json:"issues_url"`
63+
Privacy bool `json:"privacy"`
64+
ChefVersions [][]string `json:"chef_versions"`
65+
Gems [][]string `json:"gems"`
66+
EagerLoadLibraries bool `json:"eager_load_libraries"`
67+
}
68+
69+
// ParsePackage parses the Chef package file
70+
func ParsePackage(r io.Reader) (*Package, error) {
71+
gzr, err := gzip.NewReader(r)
72+
if err != nil {
73+
return nil, err
74+
}
75+
defer gzr.Close()
76+
77+
tr := tar.NewReader(gzr)
78+
for {
79+
hd, err := tr.Next()
80+
if err == io.EOF {
81+
break
82+
}
83+
if err != nil {
84+
return nil, err
85+
}
86+
87+
if hd.Typeflag != tar.TypeReg {
88+
continue
89+
}
90+
91+
if strings.Count(hd.Name, "/") != 1 {
92+
continue
93+
}
94+
95+
if hd.FileInfo().Name() == "metadata.json" {
96+
return ParseChefMetadata(tr)
97+
}
98+
}
99+
100+
return nil, ErrMissingMetadataFile
101+
}
102+
103+
// ParseChefMetadata parses a metadata.json file to retrieve the metadata of a Chef package
104+
func ParseChefMetadata(r io.Reader) (*Package, error) {
105+
var cm chefMetadata
106+
if err := json.NewDecoder(r).Decode(&cm); err != nil {
107+
return nil, err
108+
}
109+
110+
if !namePattern.MatchString(cm.Name) {
111+
return nil, ErrInvalidName
112+
}
113+
114+
if !versionPattern.MatchString(cm.Version) {
115+
return nil, ErrInvalidVersion
116+
}
117+
118+
if !validation.IsValidURL(cm.SourceURL) {
119+
cm.SourceURL = ""
120+
}
121+
122+
return &Package{
123+
Name: cm.Name,
124+
Version: cm.Version,
125+
Metadata: &Metadata{
126+
Description: cm.Description,
127+
LongDescription: cm.LongDescription,
128+
Author: cm.Maintainer,
129+
License: cm.License,
130+
RepositoryURL: cm.SourceURL,
131+
Dependencies: cm.Dependencies,
132+
},
133+
}, nil
134+
}

0 commit comments

Comments
 (0)