Skip to content

Commit ade0372

Browse files
committed
Merge remote-tracking branch 'upstream/main'
* upstream/main: Add CRAN package registry (go-gitea#22331)
2 parents 6791aa3 + cdb088c commit ade0372

File tree

23 files changed

+1212
-2
lines changed

23 files changed

+1212
-2
lines changed

custom/conf/app.example.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2420,6 +2420,8 @@ LEVEL = Info
24202420
;LIMIT_SIZE_CONDA = -1
24212421
;; Maximum size of a Container upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
24222422
;LIMIT_SIZE_CONTAINER = -1
2423+
;; Maximum size of a CRAN upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
2424+
;LIMIT_SIZE_CRAN = -1
24232425
;; Maximum size of a Debian upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
24242426
;LIMIT_SIZE_DEBIAN = -1
24252427
;; Maximum size of a Generic upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)

docs/content/doc/administration/config-cheat-sheet.en-us.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,7 @@ Task queue configuration has been moved to `queue.task`. However, the below conf
12071207
- `LIMIT_SIZE_CONAN`: **-1**: Maximum size of a Conan upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
12081208
- `LIMIT_SIZE_CONDA`: **-1**: Maximum size of a Conda upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
12091209
- `LIMIT_SIZE_CONTAINER`: **-1**: Maximum size of a Container upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
1210+
- `LIMIT_SIZE_CRAN`: **-1**: Maximum size of a CRAN upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
12101211
- `LIMIT_SIZE_DEBIAN`: **-1**: Maximum size of a Debian upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
12111212
- `LIMIT_SIZE_GENERIC`: **-1**: Maximum size of a Generic upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
12121213
- `LIMIT_SIZE_GO`: **-1**: Maximum size of a Go upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
date: "2023-01-01T00:00:00+00:00"
3+
title: "CRAN Packages Repository"
4+
slug: "cran"
5+
draft: false
6+
toc: false
7+
menu:
8+
sidebar:
9+
parent: "packages"
10+
name: "CRAN"
11+
weight: 35
12+
identifier: "cran"
13+
---
14+
15+
# CRAN Packages Repository
16+
17+
Publish [R](https://www.r-project.org/) packages to a [CRAN](https://cran.r-project.org/)-like registry for your user or organization.
18+
19+
**Table of Contents**
20+
21+
{{< toc >}}
22+
23+
## Requirements
24+
25+
To work with the CRAN package registry, you need to install [R](https://cran.r-project.org/).
26+
27+
## Configuring the package registry
28+
29+
To register the package registry you need to add it to `Rprofile.site`, either on the system-level, user-level (`~/.Rprofile`) or project-level:
30+
31+
```
32+
options("repos" = c(getOption("repos"), c(gitea="https://gitea.example.com/api/packages/{owner}/cran")))
33+
```
34+
35+
| Parameter | Description |
36+
| --------- | ----------- |
37+
| `owner` | The owner of the package. |
38+
39+
If you need to provide credentials, you may embed them as part of the url (`https://user:[email protected]/...`).
40+
41+
## Publish a package
42+
43+
To publish a R package, perform a HTTP `PUT` operation with the package content in the request body.
44+
45+
Source packages:
46+
47+
```
48+
PUT https://gitea.example.com/api/packages/{owner}/cran/src
49+
```
50+
51+
| Parameter | Description |
52+
| --------- | ----------- |
53+
| `owner` | The owner of the package. |
54+
55+
Binary packages:
56+
57+
```
58+
PUT https://gitea.example.com/api/packages/{owner}/cran/bin?platform={platform}&rversion={rversion}
59+
```
60+
61+
| Parameter | Description |
62+
| ---------- | ----------- |
63+
| `owner` | The owner of the package. |
64+
| `platform` | The name of the platform. |
65+
| `rversion` | The R version of the binary. |
66+
67+
For example:
68+
69+
```shell
70+
curl --user your_username:your_password_or_token \
71+
--upload-file path/to/package.zip \
72+
https://gitea.example.com/api/packages/testuser/cran/bin?platform=windows&rversion=4.2
73+
```
74+
75+
You cannot publish a package if a package of the same name and version already exists. You must delete the existing package first.
76+
77+
## Install a package
78+
79+
To install a R package from the package registry, execute the following command:
80+
81+
```shell
82+
install.packages("{package_name}")
83+
```
84+
85+
| Parameter | Description |
86+
| -------------- | ----------- |
87+
| `package_name` | The package name. |
88+
89+
For example:
90+
91+
```shell
92+
install.packages("testpackage")
93+
```

docs/content/doc/usage/packages/overview.en-us.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ The following package managers are currently supported:
3434
| [Conan]({{< relref "doc/usage/packages/conan.en-us.md" >}}) | C++ | `conan` |
3535
| [Conda]({{< relref "doc/usage/packages/conda.en-us.md" >}}) | - | `conda` |
3636
| [Container]({{< relref "doc/usage/packages/container.en-us.md" >}}) | - | any OCI compliant client |
37+
| [CRAN]({{< relref "doc/usage/packages/cran.en-us.md" >}}) | R | - |
3738
| [Debian]({{< relref "doc/usage/packages/debian.en-us.md" >}}) | - | `apt` |
3839
| [Generic]({{< relref "doc/usage/packages/generic.en-us.md" >}}) | - | any HTTP client |
3940
| [Go]({{< relref "doc/usage/packages/go.en-us.md" >}}) | Go | `go` |

models/packages/cran/search.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package cran
5+
6+
import (
7+
"context"
8+
"strconv"
9+
"strings"
10+
11+
"code.gitea.io/gitea/models/db"
12+
"code.gitea.io/gitea/models/packages"
13+
cran_module "code.gitea.io/gitea/modules/packages/cran"
14+
15+
"xorm.io/builder"
16+
)
17+
18+
type SearchOptions struct {
19+
OwnerID int64
20+
FileType string
21+
Platform string
22+
RVersion string
23+
Filename string
24+
}
25+
26+
func (opts *SearchOptions) toConds() builder.Cond {
27+
var cond builder.Cond = builder.Eq{
28+
"package.type": packages.TypeCran,
29+
"package.owner_id": opts.OwnerID,
30+
"package_version.is_internal": false,
31+
}
32+
33+
if opts.Filename != "" {
34+
cond = cond.And(builder.Eq{"package_file.lower_name": strings.ToLower(opts.Filename)})
35+
}
36+
37+
var propsCond builder.Cond = builder.Eq{
38+
"package_property.ref_type": packages.PropertyTypeFile,
39+
}
40+
propsCond = propsCond.And(builder.Expr("package_property.ref_id = package_file.id"))
41+
42+
count := 1
43+
propsCondBlock := builder.Eq{"package_property.name": cran_module.PropertyType}.And(builder.Eq{"package_property.value": opts.FileType})
44+
45+
if opts.Platform != "" {
46+
count += 2
47+
propsCondBlock = propsCondBlock.
48+
Or(builder.Eq{"package_property.name": cran_module.PropertyPlatform}.And(builder.Eq{"package_property.value": opts.Platform})).
49+
Or(builder.Eq{"package_property.name": cran_module.PropertyRVersion}.And(builder.Eq{"package_property.value": opts.RVersion}))
50+
}
51+
52+
propsCond = propsCond.And(propsCondBlock)
53+
54+
cond = cond.And(builder.Eq{
55+
strconv.Itoa(count): builder.Select("COUNT(*)").Where(propsCond).From("package_property"),
56+
})
57+
58+
return cond
59+
}
60+
61+
func SearchLatestVersions(ctx context.Context, opts *SearchOptions) ([]*packages.PackageVersion, error) {
62+
sess := db.GetEngine(ctx).
63+
Table("package_version").
64+
Select("package_version.*").
65+
Join("LEFT", "package_version pv2", builder.Expr("package_version.package_id = pv2.package_id AND pv2.is_internal = ? AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))", false)).
66+
Join("INNER", "package", "package.id = package_version.package_id").
67+
Join("INNER", "package_file", "package_file.version_id = package_version.id").
68+
Where(opts.toConds().And(builder.Expr("pv2.id IS NULL"))).
69+
Asc("package.name")
70+
71+
pvs := make([]*packages.PackageVersion, 0, 10)
72+
return pvs, sess.Find(&pvs)
73+
}
74+
75+
func SearchFile(ctx context.Context, opts *SearchOptions) (*packages.PackageFile, error) {
76+
sess := db.GetEngine(ctx).
77+
Table("package_version").
78+
Select("package_file.*").
79+
Join("INNER", "package", "package.id = package_version.package_id").
80+
Join("INNER", "package_file", "package_file.version_id = package_version.id").
81+
Where(opts.toConds())
82+
83+
pf := &packages.PackageFile{}
84+
if has, err := sess.Get(pf); err != nil {
85+
return nil, err
86+
} else if !has {
87+
return nil, packages.ErrPackageFileNotExist
88+
}
89+
return pf, nil
90+
}

models/packages/descriptor.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"code.gitea.io/gitea/modules/packages/conan"
2020
"code.gitea.io/gitea/modules/packages/conda"
2121
"code.gitea.io/gitea/modules/packages/container"
22+
"code.gitea.io/gitea/modules/packages/cran"
2223
"code.gitea.io/gitea/modules/packages/debian"
2324
"code.gitea.io/gitea/modules/packages/helm"
2425
"code.gitea.io/gitea/modules/packages/maven"
@@ -151,6 +152,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
151152
metadata = &conda.VersionMetadata{}
152153
case TypeContainer:
153154
metadata = &container.Metadata{}
155+
case TypeCran:
156+
metadata = &cran.Metadata{}
154157
case TypeDebian:
155158
metadata = &debian.Metadata{}
156159
case TypeGeneric:

models/packages/package.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const (
3737
TypeConan Type = "conan"
3838
TypeConda Type = "conda"
3939
TypeContainer Type = "container"
40+
TypeCran Type = "cran"
4041
TypeDebian Type = "debian"
4142
TypeGeneric Type = "generic"
4243
TypeGo Type = "go"
@@ -60,6 +61,7 @@ var TypeList = []Type{
6061
TypeConan,
6162
TypeConda,
6263
TypeContainer,
64+
TypeCran,
6365
TypeDebian,
6466
TypeGeneric,
6567
TypeGo,
@@ -92,6 +94,8 @@ func (pt Type) Name() string {
9294
return "Conda"
9395
case TypeContainer:
9496
return "Container"
97+
case TypeCran:
98+
return "CRAN"
9599
case TypeDebian:
96100
return "Debian"
97101
case TypeGeneric:
@@ -139,6 +143,8 @@ func (pt Type) SVGName() string {
139143
return "gitea-conda"
140144
case TypeContainer:
141145
return "octicon-container"
146+
case TypeCran:
147+
return "gitea-cran"
142148
case TypeDebian:
143149
return "gitea-debian"
144150
case TypeGeneric:

0 commit comments

Comments
 (0)