Skip to content

Commit

Permalink
Support for grouping RPMs using paths (go-gitea#26984)
Browse files Browse the repository at this point in the history
The current rpm repository places all packages in the same repository,
and different systems (el7,f34) may hit packages that do not belong to
this distribution ( go-gitea#25304 ) , which now supports grouping of rpm.

![图片](https://github.com/go-gitea/gitea/assets/33776693/d1e1d99f-7799-4b2b-a19b-cb2a5c692914)

Fixes go-gitea#25304 .
Fixes go-gitea#27056 .

Refactor: [go-gitea#25866](go-gitea#25866)
  • Loading branch information
ExplodingDragon authored and silverwind committed Feb 20, 2024
1 parent c4d84ad commit 6b9d02b
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 101 deletions.
33 changes: 18 additions & 15 deletions docs/content/usage/packages/rpm.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,18 @@ The following examples use `dnf`.
To register the RPM registry add the url to the list of known apt sources:

```shell
dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm.repo
dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm/{group}.repo
```

| Placeholder | Description |
| ----------- | ----------- |
| `owner` | The owner of the package. |
| Placeholder | Description |
| ----------- |----------------------------------------------------|
| `owner` | The owner of the package. |
| `group` | Everything, e.g. `el7`, `rocky/el9` , `test/fc38`.|

If the registry is private, provide credentials in the url. You can use a password or a [personal access token](development/api-usage.md#authentication):

```shell
dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm.repo
dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm/{group}.repo
```

You have to add the credentials to the urls in the `rpm.repo` file in `/etc/yum.repos.d` too.
Expand All @@ -47,19 +48,20 @@ You have to add the credentials to the urls in the `rpm.repo` file in `/etc/yum.
To publish a RPM package (`*.rpm`), perform a HTTP PUT operation with the package content in the request body.

```
PUT https://gitea.example.com/api/packages/{owner}/rpm/upload
PUT https://gitea.example.com/api/packages/{owner}/rpm/{group}/upload
```

| Parameter | Description |
| --------- | ----------- |
| `owner` | The owner of the package. |
| `group` | Everything, e.g. `el7`, `rocky/el9` , `test/fc38`.|

Example request using HTTP Basic authentication:

```shell
curl --user your_username:your_password_or_token \
--upload-file path/to/file.rpm \
https://gitea.example.com/api/packages/testuser/rpm/upload
https://gitea.example.com/api/packages/testuser/rpm/centos/el7/upload
```

If you are using 2FA or OAuth use a [personal access token](development/api-usage.md#authentication) instead of the password.
Expand All @@ -78,21 +80,22 @@ The server responds with the following HTTP Status codes.
To delete an RPM package perform a HTTP DELETE operation. This will delete the package version too if there is no file left.

```
DELETE https://gitea.example.com/api/packages/{owner}/rpm/{package_name}/{package_version}/{architecture}
DELETE https://gitea.example.com/api/packages/{owner}/rpm/{group}/package/{package_name}/{package_version}/{architecture}
```

| Parameter | Description |
| ----------------- | ----------- |
| `owner` | The owner of the package. |
| `package_name` | The package name. |
| `package_version` | The package version. |
| `architecture` | The package architecture. |
| Parameter | Description |
|-------------------|----------------------------|
| `owner` | The owner of the package. |
| `group` | The package group . |
| `package_name` | The package name. |
| `package_version` | The package version. |
| `architecture` | The package architecture. |

Example request using HTTP Basic authentication:

```shell
curl --user your_username:your_token_or_password -X DELETE \
https://gitea.example.com/api/packages/testuser/rpm/test-package/1.0.0/x86_64
https://gitea.example.com/api/packages/testuser/rpm/centos/el7/package/test-package/1.0.0/x86_64
```

The server responds with the following HTTP Status codes.
Expand Down
25 changes: 14 additions & 11 deletions docs/content/usage/packages/rpm.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,18 @@ menu:
要注册RPM注册表,请将 URL 添加到已知 `apt` 源列表中:

```shell
dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm.repo
dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm/{group}.repo
```

| 占位符 | 描述 |
| ------- | -------------- |
| `owner` | 软件包的所有者 |
| 占位符 | 描述 |
| ------- |--------------------------------------|
| `owner` | 软件包的所有者 |
| `group` | 任何名称,例如 `centos/7``el-7``fc38` |

如果注册表是私有的,请在URL中提供凭据。您可以使用密码或[个人访问令牌](development/api-usage.md#通过-api-认证)

```shell
dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm.repo
dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm/{group}.repo
```

您还必须将凭据添加到 `/etc/yum.repos.d` 中的 `rpm.repo` 文件中的URL中。
Expand All @@ -47,19 +48,20 @@ dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.
要发布RPM软件包(`*.rpm`),请执行带有软件包内容的 HTTP `PUT` 操作。

```
PUT https://gitea.example.com/api/packages/{owner}/rpm/upload
PUT https://gitea.example.com/api/packages/{owner}/rpm/{group}/upload
```

| 参数 | 描述 |
| ------- | -------------- |
| `owner` | 软件包的所有者 |
| ------- |--------------|
| `owner` | 软件包的所有者 |
| `group` | 软件包自定义分组名称 |

使用HTTP基本身份验证的示例请求:

```shell
curl --user your_username:your_password_or_token \
--upload-file path/to/file.rpm \
https://gitea.example.com/api/packages/testuser/rpm/upload
https://gitea.example.com/api/packages/testuser/rpm/centos/el7/version/upload
```

如果您使用 2FA 或 OAuth,请使用[个人访问令牌](development/api-usage.md#通过-api-认证)替代密码。您无法将具有相同名称的文件两次发布到软件包中。您必须先删除现有的软件包版本。
Expand All @@ -77,12 +79,13 @@ curl --user your_username:your_password_or_token \
要删除 RPM 软件包,请执行 HTTP `DELETE` 操作。如果没有文件剩余,这也将删除软件包版本。

```
DELETE https://gitea.example.com/api/packages/{owner}/rpm/{package_name}/{package_version}/{architecture}
DELETE https://gitea.example.com/api/packages/{owner}/rpm/{group}/package/{package_name}/{package_version}/{architecture}
```

| 参数 | 描述 |
| ----------------- | -------------- |
| `owner` | 软件包的所有者 |
| `group` | 软件包自定义分组 |
| `package_name` | 软件包名称 |
| `package_version` | 软件包版本 |
| `architecture` | 软件包架构 |
Expand All @@ -91,7 +94,7 @@ DELETE https://gitea.example.com/api/packages/{owner}/rpm/{package_name}/{packag

```shell
curl --user your_username:your_token_or_password -X DELETE \
https://gitea.example.com/api/packages/testuser/rpm/test-package/1.0.0/x86_64
https://gitea.example.com/api/packages/testuser/rpm/centos/el7/package/test-package/1.0.0/x86_64
```

服务器将以以下HTTP状态码响应:
Expand Down
3 changes: 1 addition & 2 deletions modules/packages/rpm/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import (
)

const (
PropertyMetadata = "rpm.metadata"

PropertyMetadata = "rpm.metadata"
SettingKeyPrivate = "rpm.key.private"
SettingKeyPublic = "rpm.key.public"

Expand Down
5 changes: 5 additions & 0 deletions modules/templates/util_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package templates

import (
"regexp"
"strings"

"code.gitea.io/gitea/modules/base"
Expand All @@ -25,6 +26,10 @@ func (su *StringUtils) Contains(s, substr string) bool {
return strings.Contains(s, substr)
}

func (su *StringUtils) ReplaceAllStringRegex(s, regex, new string) string {
return regexp.MustCompile(regex).ReplaceAllString(s, new)
}

func (su *StringUtils) Split(s, sep string) []string {
return strings.Split(s, sep)
}
Expand Down
90 changes: 77 additions & 13 deletions routers/api/packages/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,19 +512,7 @@ func CommonRoutes() *web.Route {
r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile)
r.Get("/simple/{id}", pypi.PackageMetadata)
}, reqPackageAccess(perm.AccessModeRead))
r.Group("/rpm", func() {
r.Get(".repo", rpm.GetRepositoryConfig)
r.Get("/repository.key", rpm.GetRepositoryKey)
r.Put("/upload", reqPackageAccess(perm.AccessModeWrite), rpm.UploadPackageFile)
r.Group("/package/{name}/{version}/{architecture}", func() {
r.Get("", rpm.DownloadPackageFile)
r.Delete("", reqPackageAccess(perm.AccessModeWrite), rpm.DeletePackageFile)
})
r.Group("/repodata/{filename}", func() {
r.Head("", rpm.CheckRepositoryFileExistence)
r.Get("", rpm.GetRepositoryFile)
})
}, reqPackageAccess(perm.AccessModeRead))
r.Group("/rpm", RpmRoutes(r), reqPackageAccess(perm.AccessModeRead))
r.Group("/rubygems", func() {
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest)
Expand Down Expand Up @@ -589,6 +577,82 @@ func CommonRoutes() *web.Route {
return r
}

// Support for uploading rpm packages with arbitrary depth paths
func RpmRoutes(r *web.Route) func() {
var (
groupRepoInfo = regexp.MustCompile(`\A((?:/(?:[^/]+))*|)\.repo\z`)
groupUpload = regexp.MustCompile(`\A((?:/(?:[^/]+))*|)/upload\z`)
groupRpm = regexp.MustCompile(`\A((?:/(?:[^/]+))*|)/package/([^/]+)/([^/]+)/([^/]+)(?:/([^/]+\.rpm)|)\z`)
groupMetadata = regexp.MustCompile(`\A((?:/(?:[^/]+))*|)/repodata/([^/]+)\z`)
)

return func() {
r.Methods("HEAD,GET,POST,PUT,PATCH,DELETE", "*", func(ctx *context.Context) {
path := ctx.Params("*")
isHead := ctx.Req.Method == "HEAD"
isGetHead := ctx.Req.Method == "HEAD" || ctx.Req.Method == "GET"
isPut := ctx.Req.Method == "PUT"
isDelete := ctx.Req.Method == "DELETE"

if path == "/repository.key" && isGetHead {
rpm.GetRepositoryKey(ctx)
return
}

// get repo
m := groupRepoInfo.FindStringSubmatch(path)
if len(m) == 2 && isGetHead {
ctx.SetParams("group", strings.Trim(m[1], "/"))
rpm.GetRepositoryConfig(ctx)
return
}
// get meta
m = groupMetadata.FindStringSubmatch(path)
if len(m) == 3 && isGetHead {
ctx.SetParams("group", strings.Trim(m[1], "/"))
ctx.SetParams("filename", m[2])
if isHead {
rpm.CheckRepositoryFileExistence(ctx)
} else {
rpm.GetRepositoryFile(ctx)
}
return
}
// upload
m = groupUpload.FindStringSubmatch(path)
if len(m) == 2 && isPut {
reqPackageAccess(perm.AccessModeWrite)(ctx)
if ctx.Written() {
return
}
ctx.SetParams("group", strings.Trim(m[1], "/"))
rpm.UploadPackageFile(ctx)
return
}
// rpm down/delete
m = groupRpm.FindStringSubmatch(path)
if len(m) == 6 {
ctx.SetParams("group", strings.Trim(m[1], "/"))
ctx.SetParams("name", m[2])
ctx.SetParams("version", m[3])
ctx.SetParams("architecture", m[4])
if isGetHead {
rpm.DownloadPackageFile(ctx)
return
} else if isDelete {
reqPackageAccess(perm.AccessModeWrite)(ctx)
if ctx.Written() {
return
}
rpm.DeletePackageFile(ctx)
}
}
// default
ctx.Status(http.StatusNotFound)
})
}
}

// ContainerRoutes provides endpoints that implement the OCI API to serve containers
// These have to be mounted on `/v2/...` to comply with the OCI spec:
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md
Expand Down
Loading

0 comments on commit 6b9d02b

Please sign in to comment.