mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-14 00:01:44 -04:00
Compare commits
12 Commits
7fd5d38860
...
5d9c64b3fe
Author | SHA1 | Date | |
---|---|---|---|
|
5d9c64b3fe | ||
|
4e946e5a7d | ||
|
3f2e721372 | ||
|
15c035775a | ||
|
1e0e79dcbf | ||
|
b6b8feb3de | ||
|
6ba9ff7b48 | ||
|
5882e179a9 | ||
|
9f9a1ce922 | ||
|
72a83dcc82 | ||
|
19d5b2f922 | ||
|
2871ea0809 |
15
cmd/admin.go
15
cmd/admin.go
@ -180,6 +180,11 @@ var (
|
|||||||
Name: "raw",
|
Name: "raw",
|
||||||
Usage: "Display only the token value",
|
Usage: "Display only the token value",
|
||||||
},
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "scopes",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Comma separated list of scopes to apply to access token",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action: runGenerateAccessToken,
|
Action: runGenerateAccessToken,
|
||||||
}
|
}
|
||||||
@ -698,9 +703,15 @@ func runGenerateAccessToken(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
accessTokenScope, err := auth_model.AccessTokenScope(c.String("scopes")).Normalize()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
t := &auth_model.AccessToken{
|
t := &auth_model.AccessToken{
|
||||||
Name: c.String("token-name"),
|
Name: c.String("token-name"),
|
||||||
UID: user.ID,
|
UID: user.ID,
|
||||||
|
Scope: accessTokenScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := auth_model.NewAccessToken(t); err != nil {
|
if err := auth_model.NewAccessToken(t); err != nil {
|
||||||
|
@ -2458,6 +2458,8 @@ ROUTER = console
|
|||||||
;LIMIT_SIZE_COMPOSER = -1
|
;LIMIT_SIZE_COMPOSER = -1
|
||||||
;; Maximum size of a Conan upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
;; Maximum size of a Conan upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
;LIMIT_SIZE_CONAN = -1
|
;LIMIT_SIZE_CONAN = -1
|
||||||
|
;; Maximum size of a Conda upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
|
;LIMIT_SIZE_CONDA = -1
|
||||||
;; Maximum size of a Container upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
;; Maximum size of a Container upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
;LIMIT_SIZE_CONTAINER = -1
|
;LIMIT_SIZE_CONTAINER = -1
|
||||||
;; Maximum size of a Generic upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
;; Maximum size of a Generic upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
|
@ -1214,6 +1214,7 @@ Task queue configuration has been moved to `queue.task`. However, the below conf
|
|||||||
- `LIMIT_TOTAL_OWNER_SIZE`: **-1**: Maximum size of packages a single owner can use (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
- `LIMIT_TOTAL_OWNER_SIZE`: **-1**: Maximum size of packages a single owner can use (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
- `LIMIT_SIZE_COMPOSER`: **-1**: Maximum size of a Composer upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
- `LIMIT_SIZE_COMPOSER`: **-1**: Maximum size of a Composer upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
- `LIMIT_SIZE_CONAN`: **-1**: Maximum size of a Conan upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
- `LIMIT_SIZE_CONAN`: **-1**: Maximum size of a Conan upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
|
- `LIMIT_SIZE_CONDA`: **-1**: Maximum size of a Conda upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
- `LIMIT_SIZE_CONTAINER`: **-1**: Maximum size of a Container upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
- `LIMIT_SIZE_CONTAINER`: **-1**: Maximum size of a Container upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
- `LIMIT_SIZE_GENERIC`: **-1**: Maximum size of a Generic upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
- `LIMIT_SIZE_GENERIC`: **-1**: Maximum size of a Generic upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
- `LIMIT_SIZE_HELM`: **-1**: Maximum size of a Helm upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
- `LIMIT_SIZE_HELM`: **-1**: Maximum size of a Helm upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
|
||||||
|
85
docs/content/doc/packages/conda.en-us.md
Normal file
85
docs/content/doc/packages/conda.en-us.md
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
---
|
||||||
|
date: "2022-12-28T00:00:00+00:00"
|
||||||
|
title: "Conda Packages Repository"
|
||||||
|
slug: "packages/conda"
|
||||||
|
draft: false
|
||||||
|
toc: false
|
||||||
|
menu:
|
||||||
|
sidebar:
|
||||||
|
parent: "packages"
|
||||||
|
name: "Conda"
|
||||||
|
weight: 25
|
||||||
|
identifier: "conda"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Conda Packages Repository
|
||||||
|
|
||||||
|
Publish [Conda](https://docs.conda.io/en/latest/) packages for your user or organization.
|
||||||
|
|
||||||
|
**Table of Contents**
|
||||||
|
|
||||||
|
{{< toc >}}
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
To work with the Conda package registry, you need to use [conda](https://docs.conda.io/projects/conda/en/stable/user-guide/install/index.html).
|
||||||
|
|
||||||
|
## Configuring the package registry
|
||||||
|
|
||||||
|
To register the package registry and provide credentials, edit your `.condarc` file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
channel_alias: https://gitea.example.com/api/packages/{owner}/conda
|
||||||
|
channels:
|
||||||
|
- https://gitea.example.com/api/packages/{owner}/conda
|
||||||
|
default_channels:
|
||||||
|
- https://gitea.example.com/api/packages/{owner}/conda
|
||||||
|
```
|
||||||
|
|
||||||
|
| Placeholder | Description |
|
||||||
|
| ------------ | ----------- |
|
||||||
|
| `owner` | The owner of the package. |
|
||||||
|
|
||||||
|
See the [official documentation](https://conda.io/projects/conda/en/latest/user-guide/configuration/use-condarc.html) for explanations of the individual settings.
|
||||||
|
|
||||||
|
If you need to provide credentials, you may embed them as part of the channel url (`https://user:password@gitea.example.com/...`).
|
||||||
|
|
||||||
|
## Publish a package
|
||||||
|
|
||||||
|
To publish a package, perform a HTTP PUT operation with the package content in the request body.
|
||||||
|
|
||||||
|
```
|
||||||
|
PUT https://gitea.example.com/api/packages/{owner}/conda/{channel}/{filename}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Placeholder | Description |
|
||||||
|
| ------------ | ----------- |
|
||||||
|
| `owner` | The owner of the package. |
|
||||||
|
| `channel` | The [channel](https://conda.io/projects/conda/en/latest/user-guide/concepts/channels.html) of the package. (optional) |
|
||||||
|
| `filename` | The name of the file. |
|
||||||
|
|
||||||
|
Example request using HTTP Basic authentication:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl --user your_username:your_password_or_token \
|
||||||
|
--upload-file path/to/package-1.0.conda \
|
||||||
|
https://gitea.example.com/api/packages/testuser/conda/package-1.0.conda
|
||||||
|
```
|
||||||
|
|
||||||
|
You cannot publish a package if a package of the same name and version already exists. You must delete the existing package first.
|
||||||
|
|
||||||
|
## Install a package
|
||||||
|
|
||||||
|
To install a package from the package registry, execute one of the following commands:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
conda install {package_name}
|
||||||
|
conda install {package_name}={package_version}
|
||||||
|
conda install -c {channel} {package_name}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Parameter | Description |
|
||||||
|
| ----------------- | ----------- |
|
||||||
|
| `package_name` | The package name. |
|
||||||
|
| `package_version` | The package version. |
|
||||||
|
| `channel` | The channel of the package. (optional) |
|
@ -28,6 +28,7 @@ The following package managers are currently supported:
|
|||||||
| ---- | -------- | -------------- |
|
| ---- | -------- | -------------- |
|
||||||
| [Composer]({{< relref "doc/packages/composer.en-us.md" >}}) | PHP | `composer` |
|
| [Composer]({{< relref "doc/packages/composer.en-us.md" >}}) | PHP | `composer` |
|
||||||
| [Conan]({{< relref "doc/packages/conan.en-us.md" >}}) | C++ | `conan` |
|
| [Conan]({{< relref "doc/packages/conan.en-us.md" >}}) | C++ | `conan` |
|
||||||
|
| [Conda]({{< relref "doc/packages/conda.en-us.md" >}}) | - | `conda` |
|
||||||
| [Container]({{< relref "doc/packages/container.en-us.md" >}}) | - | any OCI compliant client |
|
| [Container]({{< relref "doc/packages/container.en-us.md" >}}) | - | any OCI compliant client |
|
||||||
| [Generic]({{< relref "doc/packages/generic.en-us.md" >}}) | - | any HTTP client |
|
| [Generic]({{< relref "doc/packages/generic.en-us.md" >}}) | - | any HTTP client |
|
||||||
| [Helm]({{< relref "doc/packages/helm.en-us.md" >}}) | - | any HTTP client, `cm-push` |
|
| [Helm]({{< relref "doc/packages/helm.en-us.md" >}}) | - | any HTTP client, `cm-push` |
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
date: "2022-12-19T21:26:00+08:00"
|
date: "2022-12-19T21:26:00+08:00"
|
||||||
title: "Encrypted secrets"
|
title: "Secrets"
|
||||||
slug: "secrets/overview"
|
slug: "secrets/overview"
|
||||||
draft: false
|
draft: false
|
||||||
toc: false
|
toc: false
|
||||||
@ -12,24 +12,24 @@ menu:
|
|||||||
identifier: "overview"
|
identifier: "overview"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Encrypted secrets
|
# Secrets
|
||||||
|
|
||||||
Encrypted secrets allow you to store sensitive information in your organization or repository.
|
Secrets allow you to store sensitive information in your user, organization or repository.
|
||||||
Secrets are available on Gitea 1.19+.
|
Secrets are available on Gitea 1.19+.
|
||||||
|
|
||||||
# Naming your secrets
|
# Naming your secrets
|
||||||
|
|
||||||
The following rules apply to secret names:
|
The following rules apply to secret names:
|
||||||
|
|
||||||
Secret names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]`) or underscores (`_`). Spaces are not allowed.
|
- Secret names can only contain alphanumeric characters (`[a-z]`, `[A-Z]`, `[0-9]`) or underscores (`_`). Spaces are not allowed.
|
||||||
|
|
||||||
Secret names must not start with the `GITHUB_` and `GITEA_` prefix.
|
- Secret names must not start with the `GITHUB_` and `GITEA_` prefix.
|
||||||
|
|
||||||
Secret names must not start with a number.
|
- Secret names must not start with a number.
|
||||||
|
|
||||||
Secret names are not case-sensitive.
|
- Secret names are not case-sensitive.
|
||||||
|
|
||||||
Secret names must be unique at the level they are created at.
|
- Secret names must be unique at the level they are created at.
|
||||||
|
|
||||||
For example, a secret created at the repository level must have a unique name in that repository, and a secret created at the organization level must have a unique name at that level.
|
For example, a secret created at the repository level must have a unique name in that repository, and a secret created at the organization level must have a unique name at that level.
|
||||||
|
|
||||||
|
4
go.mod
4
go.mod
@ -26,6 +26,7 @@ require (
|
|||||||
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
|
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
|
||||||
github.com/djherbis/buffer v1.2.0
|
github.com/djherbis/buffer v1.2.0
|
||||||
github.com/djherbis/nio/v3 v3.0.1
|
github.com/djherbis/nio/v3 v3.0.1
|
||||||
|
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/editorconfig/editorconfig-core-go/v2 v2.5.1
|
github.com/editorconfig/editorconfig-core-go/v2 v2.5.1
|
||||||
github.com/emersion/go-imap v1.2.1
|
github.com/emersion/go-imap v1.2.1
|
||||||
@ -161,7 +162,6 @@ require (
|
|||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/dlclark/regexp2 v1.7.0 // indirect
|
github.com/dlclark/regexp2 v1.7.0 // indirect
|
||||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
|
||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect
|
||||||
github.com/fatih/color v1.13.0 // indirect
|
github.com/fatih/color v1.13.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||||
@ -284,7 +284,7 @@ replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142
|
|||||||
|
|
||||||
replace github.com/blevesearch/zapx/v15 v15.3.6 => github.com/zeripath/zapx/v15 v15.3.6-alignment-fix
|
replace github.com/blevesearch/zapx/v15 v15.3.6 => github.com/zeripath/zapx/v15 v15.3.6-alignment-fix
|
||||||
|
|
||||||
replace github.com/nektos/act => gitea.com/gitea/act v0.234.0
|
replace github.com/nektos/act => gitea.com/gitea/act v0.234.2-0.20230131074955-e46ede1b1744
|
||||||
|
|
||||||
exclude github.com/gofrs/uuid v3.2.0+incompatible
|
exclude github.com/gofrs/uuid v3.2.0+incompatible
|
||||||
|
|
||||||
|
4
go.sum
4
go.sum
@ -70,8 +70,8 @@ codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsi
|
|||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
|
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
|
||||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
|
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
|
||||||
gitea.com/gitea/act v0.234.0 h1:gWgMPMKdNcMrp/o2CF/SyVKiiJLBFl+xmzfvoHCpykU=
|
gitea.com/gitea/act v0.234.2-0.20230131074955-e46ede1b1744 h1:cqzKmGlX0wynSXO04NILpL25eBGwogDrKpkkbwmIpj4=
|
||||||
gitea.com/gitea/act v0.234.0/go.mod h1:2C/WbTalu1VPNgbVaZJaZDzlOtAKqkXJhdOClxkMy14=
|
gitea.com/gitea/act v0.234.2-0.20230131074955-e46ede1b1744/go.mod h1:2C/WbTalu1VPNgbVaZJaZDzlOtAKqkXJhdOClxkMy14=
|
||||||
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681 h1:MMSPgnVULVwV9kEBgvyEUhC9v/uviZ55hPJEMjpbNR4=
|
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681 h1:MMSPgnVULVwV9kEBgvyEUhC9v/uviZ55hPJEMjpbNR4=
|
||||||
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
|
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
|
||||||
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
|
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
|
||||||
|
@ -173,8 +173,9 @@ func (prs PullRequestList) loadAttributes(ctx context.Context) error {
|
|||||||
for i := range issues {
|
for i := range issues {
|
||||||
set[issues[i].ID] = issues[i]
|
set[issues[i].ID] = issues[i]
|
||||||
}
|
}
|
||||||
for i := range prs {
|
for _, pr := range prs {
|
||||||
prs[i].Issue = set[prs[i].IssueID]
|
pr.Issue = set[pr.IssueID]
|
||||||
|
pr.Issue.PullRequest = pr // panic here means issueIDs and prs are not in sync
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
63
models/packages/conda/search.go
Normal file
63
models/packages/conda/search.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package conda
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"code.gitea.io/gitea/models/packages"
|
||||||
|
conda_module "code.gitea.io/gitea/modules/packages/conda"
|
||||||
|
|
||||||
|
"xorm.io/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileSearchOptions struct {
|
||||||
|
OwnerID int64
|
||||||
|
Channel string
|
||||||
|
Subdir string
|
||||||
|
Filename string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchFiles gets all files matching the search options
|
||||||
|
func SearchFiles(ctx context.Context, opts *FileSearchOptions) ([]*packages.PackageFile, error) {
|
||||||
|
var cond builder.Cond = builder.Eq{
|
||||||
|
"package.type": packages.TypeConda,
|
||||||
|
"package.owner_id": opts.OwnerID,
|
||||||
|
"package_version.is_internal": false,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Filename != "" {
|
||||||
|
cond = cond.And(builder.Eq{
|
||||||
|
"package_file.lower_name": strings.ToLower(opts.Filename),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var versionPropsCond builder.Cond = builder.Eq{
|
||||||
|
"package_property.ref_type": packages.PropertyTypePackage,
|
||||||
|
"package_property.name": conda_module.PropertyChannel,
|
||||||
|
"package_property.value": opts.Channel,
|
||||||
|
}
|
||||||
|
|
||||||
|
cond = cond.And(builder.In("package.id", builder.Select("package_property.ref_id").Where(versionPropsCond).From("package_property")))
|
||||||
|
|
||||||
|
var filePropsCond builder.Cond = builder.Eq{
|
||||||
|
"package_property.ref_type": packages.PropertyTypeFile,
|
||||||
|
"package_property.name": conda_module.PropertySubdir,
|
||||||
|
"package_property.value": opts.Subdir,
|
||||||
|
}
|
||||||
|
|
||||||
|
cond = cond.And(builder.In("package_file.id", builder.Select("package_property.ref_id").Where(filePropsCond).From("package_property")))
|
||||||
|
|
||||||
|
sess := db.GetEngine(ctx).
|
||||||
|
Select("package_file.*").
|
||||||
|
Table("package_file").
|
||||||
|
Join("INNER", "package_version", "package_version.id = package_file.version_id").
|
||||||
|
Join("INNER", "package", "package.id = package_version.package_id").
|
||||||
|
Where(cond)
|
||||||
|
|
||||||
|
pfs := make([]*packages.PackageFile, 0, 10)
|
||||||
|
return pfs, sess.Find(&pfs)
|
||||||
|
}
|
@ -13,6 +13,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/packages/composer"
|
"code.gitea.io/gitea/modules/packages/composer"
|
||||||
"code.gitea.io/gitea/modules/packages/conan"
|
"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/container"
|
||||||
"code.gitea.io/gitea/modules/packages/helm"
|
"code.gitea.io/gitea/modules/packages/helm"
|
||||||
"code.gitea.io/gitea/modules/packages/maven"
|
"code.gitea.io/gitea/modules/packages/maven"
|
||||||
@ -132,6 +133,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
|
|||||||
metadata = &composer.Metadata{}
|
metadata = &composer.Metadata{}
|
||||||
case TypeConan:
|
case TypeConan:
|
||||||
metadata = &conan.Metadata{}
|
metadata = &conan.Metadata{}
|
||||||
|
case TypeConda:
|
||||||
|
metadata = &conda.VersionMetadata{}
|
||||||
case TypeContainer:
|
case TypeContainer:
|
||||||
metadata = &container.Metadata{}
|
metadata = &container.Metadata{}
|
||||||
case TypeGeneric:
|
case TypeGeneric:
|
||||||
|
@ -32,6 +32,7 @@ type Type string
|
|||||||
const (
|
const (
|
||||||
TypeComposer Type = "composer"
|
TypeComposer Type = "composer"
|
||||||
TypeConan Type = "conan"
|
TypeConan Type = "conan"
|
||||||
|
TypeConda Type = "conda"
|
||||||
TypeContainer Type = "container"
|
TypeContainer Type = "container"
|
||||||
TypeGeneric Type = "generic"
|
TypeGeneric Type = "generic"
|
||||||
TypeHelm Type = "helm"
|
TypeHelm Type = "helm"
|
||||||
@ -47,6 +48,7 @@ const (
|
|||||||
var TypeList = []Type{
|
var TypeList = []Type{
|
||||||
TypeComposer,
|
TypeComposer,
|
||||||
TypeConan,
|
TypeConan,
|
||||||
|
TypeConda,
|
||||||
TypeContainer,
|
TypeContainer,
|
||||||
TypeGeneric,
|
TypeGeneric,
|
||||||
TypeHelm,
|
TypeHelm,
|
||||||
@ -66,6 +68,8 @@ func (pt Type) Name() string {
|
|||||||
return "Composer"
|
return "Composer"
|
||||||
case TypeConan:
|
case TypeConan:
|
||||||
return "Conan"
|
return "Conan"
|
||||||
|
case TypeConda:
|
||||||
|
return "Conda"
|
||||||
case TypeContainer:
|
case TypeContainer:
|
||||||
return "Container"
|
return "Container"
|
||||||
case TypeGeneric:
|
case TypeGeneric:
|
||||||
@ -97,6 +101,8 @@ func (pt Type) SVGName() string {
|
|||||||
return "gitea-composer"
|
return "gitea-composer"
|
||||||
case TypeConan:
|
case TypeConan:
|
||||||
return "gitea-conan"
|
return "gitea-conan"
|
||||||
|
case TypeConda:
|
||||||
|
return "gitea-conda"
|
||||||
case TypeContainer:
|
case TypeContainer:
|
||||||
return "octicon-container"
|
return "octicon-container"
|
||||||
case TypeGeneric:
|
case TypeGeneric:
|
||||||
|
@ -10,8 +10,11 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||||
|
|
||||||
|
"github.com/gobwas/glob"
|
||||||
|
"github.com/nektos/act/pkg/jobparser"
|
||||||
"github.com/nektos/act/pkg/model"
|
"github.com/nektos/act/pkg/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -41,7 +44,7 @@ func ListWorkflows(commit *git.Commit) (git.Entries, error) {
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DetectWorkflows(commit *git.Commit, event webhook_module.HookEventType) (map[string][]byte, error) {
|
func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader) (map[string][]byte, error) {
|
||||||
entries, err := ListWorkflows(commit)
|
entries, err := ListWorkflows(commit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -63,13 +66,156 @@ func DetectWorkflows(commit *git.Commit, event webhook_module.HookEventType) (ma
|
|||||||
log.Warn("ignore invalid workflow %q: %v", entry.Name(), err)
|
log.Warn("ignore invalid workflow %q: %v", entry.Name(), err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, e := range workflow.On() {
|
events, err := jobparser.ParseRawOn(&workflow.RawOn)
|
||||||
if e == event.Event() {
|
if err != nil {
|
||||||
|
log.Warn("ignore invalid workflow %q: %v", entry.Name(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, evt := range events {
|
||||||
|
if evt.Name != triggedEvent.Event() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if detectMatched(commit, triggedEvent, payload, evt) {
|
||||||
workflows[entry.Name()] = content
|
workflows[entry.Name()] = content
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return workflows, nil
|
return workflows, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func detectMatched(commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader, evt *jobparser.Event) bool {
|
||||||
|
if len(evt.Acts) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch triggedEvent {
|
||||||
|
case webhook_module.HookEventCreate:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventDelete:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventFork:
|
||||||
|
log.Warn("unsupported event %q", triggedEvent.Event())
|
||||||
|
return false
|
||||||
|
case webhook_module.HookEventPush:
|
||||||
|
pushPayload := payload.(*api.PushPayload)
|
||||||
|
matchTimes := 0
|
||||||
|
// all acts conditions should be satisfied
|
||||||
|
for cond, vals := range evt.Acts {
|
||||||
|
switch cond {
|
||||||
|
case "branches", "tags":
|
||||||
|
for _, val := range vals {
|
||||||
|
if glob.MustCompile(val, '/').Match(pushPayload.Ref) {
|
||||||
|
matchTimes++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "paths":
|
||||||
|
filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err)
|
||||||
|
} else {
|
||||||
|
for _, val := range vals {
|
||||||
|
matched := false
|
||||||
|
for _, file := range filesChanged {
|
||||||
|
if glob.MustCompile(val, '/').Match(file) {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matched {
|
||||||
|
matchTimes++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Warn("unsupported condition %q", cond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matchTimes == len(evt.Acts)
|
||||||
|
|
||||||
|
case webhook_module.HookEventIssues:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventIssueAssign:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventIssueLabel:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventIssueMilestone:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventIssueComment:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventPullRequest:
|
||||||
|
prPayload := payload.(*api.PullRequestPayload)
|
||||||
|
matchTimes := 0
|
||||||
|
// all acts conditions should be satisfied
|
||||||
|
for cond, vals := range evt.Acts {
|
||||||
|
switch cond {
|
||||||
|
case "types":
|
||||||
|
for _, val := range vals {
|
||||||
|
if glob.MustCompile(val, '/').Match(string(prPayload.Action)) {
|
||||||
|
matchTimes++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "branches":
|
||||||
|
for _, val := range vals {
|
||||||
|
if glob.MustCompile(val, '/').Match(prPayload.PullRequest.Base.Ref) {
|
||||||
|
matchTimes++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "paths":
|
||||||
|
filesChanged, err := commit.GetFilesChangedSinceCommit(prPayload.PullRequest.Base.Ref)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err)
|
||||||
|
} else {
|
||||||
|
for _, val := range vals {
|
||||||
|
matched := false
|
||||||
|
for _, file := range filesChanged {
|
||||||
|
if glob.MustCompile(val, '/').Match(file) {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matched {
|
||||||
|
matchTimes++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Warn("unsupported condition %q", cond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matchTimes == len(evt.Acts)
|
||||||
|
case webhook_module.HookEventPullRequestAssign:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventPullRequestLabel:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventPullRequestMilestone:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventPullRequestComment:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventPullRequestReviewApproved:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventPullRequestReviewRejected:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventPullRequestReviewComment:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventPullRequestSync:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventWiki:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventRepository:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventRelease:
|
||||||
|
fallthrough
|
||||||
|
case webhook_module.HookEventPackage:
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
log.Warn("unsupported event %q", triggedEvent.Event())
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -28,7 +28,7 @@ func Init() {
|
|||||||
Config: &webauthn.Config{
|
Config: &webauthn.Config{
|
||||||
RPDisplayName: setting.AppName,
|
RPDisplayName: setting.AppName,
|
||||||
RPID: setting.Domain,
|
RPID: setting.Domain,
|
||||||
RPOrigin: appURL,
|
RPOrigins: []string{appURL},
|
||||||
AuthenticatorSelection: protocol.AuthenticatorSelection{
|
AuthenticatorSelection: protocol.AuthenticatorSelection{
|
||||||
UserVerification: "discouraged",
|
UserVerification: "discouraged",
|
||||||
},
|
},
|
||||||
|
@ -15,11 +15,11 @@ func TestInit(t *testing.T) {
|
|||||||
setting.Domain = "domain"
|
setting.Domain = "domain"
|
||||||
setting.AppName = "AppName"
|
setting.AppName = "AppName"
|
||||||
setting.AppURL = "https://domain/"
|
setting.AppURL = "https://domain/"
|
||||||
rpOrigin := "https://domain"
|
rpOrigin := []string{"https://domain"}
|
||||||
|
|
||||||
Init()
|
Init()
|
||||||
|
|
||||||
assert.Equal(t, setting.Domain, WebAuthn.Config.RPID)
|
assert.Equal(t, setting.Domain, WebAuthn.Config.RPID)
|
||||||
assert.Equal(t, setting.AppName, WebAuthn.Config.RPDisplayName)
|
assert.Equal(t, setting.AppName, WebAuthn.Config.RPDisplayName)
|
||||||
assert.Equal(t, rpOrigin, WebAuthn.Config.RPOrigin)
|
assert.Equal(t, rpOrigin, WebAuthn.Config.RPOrigins)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ func EscapeControlReader(reader io.Reader, writer io.Writer, locale translation.
|
|||||||
return streamer.escaped, err
|
return streamer.escaped, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// EscapeControlStringReader escapes the unicode control sequences in a provided reader of string content and writer in a locale and returns the findings as an EscapeStatus and the escaped []byte
|
// EscapeControlStringReader escapes the unicode control sequences in a provided reader of string content and writer in a locale and returns the findings as an EscapeStatus and the escaped []byte. HTML line breaks are not inserted after every newline by this method.
|
||||||
func EscapeControlStringReader(reader io.Reader, writer io.Writer, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, err error) {
|
func EscapeControlStringReader(reader io.Reader, writer io.Writer, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, err error) {
|
||||||
bufRd := bufio.NewReader(reader)
|
bufRd := bufio.NewReader(reader)
|
||||||
outputStream := &HTMLStreamerWriter{Writer: writer}
|
outputStream := &HTMLStreamerWriter{Writer: writer}
|
||||||
@ -65,10 +65,6 @@ func EscapeControlStringReader(reader io.Reader, writer io.Writer, locale transl
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err := streamer.SelfClosingTag("br"); err != nil {
|
|
||||||
streamer.escaped.HasError = true
|
|
||||||
return streamer.escaped, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return streamer.escaped, err
|
return streamer.escaped, err
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,12 @@ func AddCacheControlToHeader(h http.Header, maxAge time.Duration, additionalDire
|
|||||||
|
|
||||||
if setting.IsProd {
|
if setting.IsProd {
|
||||||
if maxAge == 0 {
|
if maxAge == 0 {
|
||||||
directives = append(directives, "no-store")
|
directives = append(directives, "max-age=0", "private", "must-revalidate")
|
||||||
} else {
|
} else {
|
||||||
directives = append(directives, "private", "max-age="+strconv.Itoa(int(maxAge.Seconds())))
|
directives = append(directives, "private", "max-age="+strconv.Itoa(int(maxAge.Seconds())))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
directives = append(directives, "no-store")
|
directives = append(directives, "max-age=0", "private", "must-revalidate")
|
||||||
|
|
||||||
// to remind users they are using non-prod setting.
|
// to remind users they are using non-prod setting.
|
||||||
h.Add("X-Gitea-Debug", "RUN_MODE="+setting.RunMode)
|
h.Add("X-Gitea-Debug", "RUN_MODE="+setting.RunMode)
|
||||||
|
243
modules/packages/conda/metadata.go
Normal file
243
modules/packages/conda/metadata.go
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package conda
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"archive/zip"
|
||||||
|
"compress/bzip2"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
"code.gitea.io/gitea/modules/validation"
|
||||||
|
|
||||||
|
"github.com/klauspost/compress/zstd"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidStructure = util.SilentWrap{Message: "package structure is invalid", Err: util.ErrInvalidArgument}
|
||||||
|
ErrInvalidName = util.SilentWrap{Message: "package name is invalid", Err: util.ErrInvalidArgument}
|
||||||
|
ErrInvalidVersion = util.SilentWrap{Message: "package version is invalid", Err: util.ErrInvalidArgument}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PropertyName = "conda.name"
|
||||||
|
PropertyChannel = "conda.channel"
|
||||||
|
PropertySubdir = "conda.subdir"
|
||||||
|
PropertyMetadata = "conda.metdata"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Package represents a Conda package
|
||||||
|
type Package struct {
|
||||||
|
Name string
|
||||||
|
Version string
|
||||||
|
Subdir string
|
||||||
|
VersionMetadata *VersionMetadata
|
||||||
|
FileMetadata *FileMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionMetadata represents the metadata of a Conda package
|
||||||
|
type VersionMetadata struct {
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Summary string `json:"summary,omitempty"`
|
||||||
|
ProjectURL string `json:"project_url,omitempty"`
|
||||||
|
RepositoryURL string `json:"repository_url,omitempty"`
|
||||||
|
DocumentationURL string `json:"documentation_url,omitempty"`
|
||||||
|
License string `json:"license,omitempty"`
|
||||||
|
LicenseFamily string `json:"license_family,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileMetadata represents the metadata of a Conda package file
|
||||||
|
type FileMetadata struct {
|
||||||
|
IsCondaPackage bool `json:"is_conda"`
|
||||||
|
Architecture string `json:"architecture,omitempty"`
|
||||||
|
NoArch string `json:"noarch,omitempty"`
|
||||||
|
Build string `json:"build,omitempty"`
|
||||||
|
BuildNumber int64 `json:"build_number,omitempty"`
|
||||||
|
Dependencies []string `json:"dependencies,omitempty"`
|
||||||
|
Platform string `json:"platform,omitempty"`
|
||||||
|
Timestamp int64 `json:"timestamp,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type index struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Architecture string `json:"arch"`
|
||||||
|
NoArch string `json:"noarch"`
|
||||||
|
Build string `json:"build"`
|
||||||
|
BuildNumber int64 `json:"build_number"`
|
||||||
|
Dependencies []string `json:"depends"`
|
||||||
|
License string `json:"license"`
|
||||||
|
LicenseFamily string `json:"license_family"`
|
||||||
|
Platform string `json:"platform"`
|
||||||
|
Subdir string `json:"subdir"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type about struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
Summary string `json:"summary"`
|
||||||
|
ProjectURL string `json:"home"`
|
||||||
|
RepositoryURL string `json:"dev_url"`
|
||||||
|
DocumentationURL string `json:"doc_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReaderAndReaderAt interface {
|
||||||
|
io.Reader
|
||||||
|
io.ReaderAt
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePackageBZ2 parses the Conda package file compressed with bzip2
|
||||||
|
func ParsePackageBZ2(r io.Reader) (*Package, error) {
|
||||||
|
gzr := bzip2.NewReader(r)
|
||||||
|
|
||||||
|
return parsePackageTar(gzr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePackageConda parses the Conda package file compressed with zip and zstd
|
||||||
|
func ParsePackageConda(r io.ReaderAt, size int64) (*Package, error) {
|
||||||
|
zr, err := zip.NewReader(r, size)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range zr.File {
|
||||||
|
if strings.HasPrefix(file.Name, "info-") && strings.HasSuffix(file.Name, ".tar.zst") {
|
||||||
|
f, err := zr.Open(file.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
dec, err := zstd.NewReader(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer dec.Close()
|
||||||
|
|
||||||
|
p, err := parsePackageTar(dec)
|
||||||
|
if p != nil {
|
||||||
|
p.FileMetadata.IsCondaPackage = true
|
||||||
|
}
|
||||||
|
return p, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrInvalidStructure
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePackageTar(r io.Reader) (*Package, error) {
|
||||||
|
var i *index
|
||||||
|
var a *about
|
||||||
|
|
||||||
|
tr := tar.NewReader(r)
|
||||||
|
for {
|
||||||
|
hdr, err := tr.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if hdr.Typeflag != tar.TypeReg {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if hdr.Name == "info/index.json" {
|
||||||
|
if err := json.NewDecoder(tr).Decode(&i); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !checkName(i.Name) {
|
||||||
|
return nil, ErrInvalidName
|
||||||
|
}
|
||||||
|
|
||||||
|
if !checkVersion(i.Version) {
|
||||||
|
return nil, ErrInvalidVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
if a != nil {
|
||||||
|
break // stop loop if both files were found
|
||||||
|
}
|
||||||
|
} else if hdr.Name == "info/about.json" {
|
||||||
|
if err := json.NewDecoder(tr).Decode(&a); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !validation.IsValidURL(a.ProjectURL) {
|
||||||
|
a.ProjectURL = ""
|
||||||
|
}
|
||||||
|
if !validation.IsValidURL(a.RepositoryURL) {
|
||||||
|
a.RepositoryURL = ""
|
||||||
|
}
|
||||||
|
if !validation.IsValidURL(a.DocumentationURL) {
|
||||||
|
a.DocumentationURL = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if i != nil {
|
||||||
|
break // stop loop if both files were found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == nil {
|
||||||
|
return nil, ErrInvalidStructure
|
||||||
|
}
|
||||||
|
if a == nil {
|
||||||
|
a = &about{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Package{
|
||||||
|
Name: i.Name,
|
||||||
|
Version: i.Version,
|
||||||
|
Subdir: i.Subdir,
|
||||||
|
VersionMetadata: &VersionMetadata{
|
||||||
|
License: i.License,
|
||||||
|
LicenseFamily: i.LicenseFamily,
|
||||||
|
Description: a.Description,
|
||||||
|
Summary: a.Summary,
|
||||||
|
ProjectURL: a.ProjectURL,
|
||||||
|
RepositoryURL: a.RepositoryURL,
|
||||||
|
DocumentationURL: a.DocumentationURL,
|
||||||
|
},
|
||||||
|
FileMetadata: &FileMetadata{
|
||||||
|
Architecture: i.Architecture,
|
||||||
|
NoArch: i.NoArch,
|
||||||
|
Build: i.Build,
|
||||||
|
BuildNumber: i.BuildNumber,
|
||||||
|
Dependencies: i.Dependencies,
|
||||||
|
Platform: i.Platform,
|
||||||
|
Timestamp: i.Timestamp,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/conda/conda-build/blob/db9a728a9e4e6cfc895637ca3221117970fc2663/conda_build/metadata.py#L1393
|
||||||
|
func checkName(name string) bool {
|
||||||
|
if name == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if name != strings.ToLower(name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !checkBadCharacters(name, "!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/conda/conda-build/blob/db9a728a9e4e6cfc895637ca3221117970fc2663/conda_build/metadata.py#L1403
|
||||||
|
func checkVersion(version string) bool {
|
||||||
|
if version == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !checkBadCharacters(version, "-")
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkBadCharacters(s, additional string) bool {
|
||||||
|
if strings.ContainsAny(s, "=@#$%^&*:;\"'\\|<>?/ ") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return strings.ContainsAny(s, additional)
|
||||||
|
}
|
150
modules/packages/conda/metadata_test.go
Normal file
150
modules/packages/conda/metadata_test.go
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package conda
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/dsnet/compress/bzip2"
|
||||||
|
"github.com/klauspost/compress/zstd"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
packageName = "gitea"
|
||||||
|
packageVersion = "1.0.1"
|
||||||
|
description = "Package Description"
|
||||||
|
projectURL = "https://gitea.io"
|
||||||
|
repositoryURL = "https://gitea.io/gitea/gitea"
|
||||||
|
documentationURL = "https://docs.gitea.io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParsePackage(t *testing.T) {
|
||||||
|
createArchive := func(files map[string][]byte) *bytes.Buffer {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
tw := tar.NewWriter(&buf)
|
||||||
|
for filename, content := range files {
|
||||||
|
hdr := &tar.Header{
|
||||||
|
Name: filename,
|
||||||
|
Mode: 0o600,
|
||||||
|
Size: int64(len(content)),
|
||||||
|
}
|
||||||
|
tw.WriteHeader(hdr)
|
||||||
|
tw.Write(content)
|
||||||
|
}
|
||||||
|
tw.Close()
|
||||||
|
return &buf
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("MissingIndexFile", func(t *testing.T) {
|
||||||
|
buf := createArchive(map[string][]byte{"dummy.txt": {}})
|
||||||
|
|
||||||
|
p, err := parsePackageTar(buf)
|
||||||
|
assert.Nil(t, p)
|
||||||
|
assert.ErrorIs(t, err, ErrInvalidStructure)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("MissingAboutFile", func(t *testing.T) {
|
||||||
|
buf := createArchive(map[string][]byte{"info/index.json": []byte(`{"name":"name","version":"1.0"}`)})
|
||||||
|
|
||||||
|
p, err := parsePackageTar(buf)
|
||||||
|
assert.NotNil(t, p)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, "name", p.Name)
|
||||||
|
assert.Equal(t, "1.0", p.Version)
|
||||||
|
assert.Empty(t, p.VersionMetadata.ProjectURL)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("InvalidName", func(t *testing.T) {
|
||||||
|
for _, name := range []string{"", "name!", "nAMe"} {
|
||||||
|
buf := createArchive(map[string][]byte{"info/index.json": []byte(`{"name":"` + name + `","version":"1.0"}`)})
|
||||||
|
|
||||||
|
p, err := parsePackageTar(buf)
|
||||||
|
assert.Nil(t, p)
|
||||||
|
assert.ErrorIs(t, err, ErrInvalidName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("InvalidVersion", func(t *testing.T) {
|
||||||
|
for _, version := range []string{"", "1.0-2"} {
|
||||||
|
buf := createArchive(map[string][]byte{"info/index.json": []byte(`{"name":"name","version":"` + version + `"}`)})
|
||||||
|
|
||||||
|
p, err := parsePackageTar(buf)
|
||||||
|
assert.Nil(t, p)
|
||||||
|
assert.ErrorIs(t, err, ErrInvalidVersion)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Valid", func(t *testing.T) {
|
||||||
|
buf := createArchive(map[string][]byte{
|
||||||
|
"info/index.json": []byte(`{"name":"` + packageName + `","version":"` + packageVersion + `","subdir":"linux-64"}`),
|
||||||
|
"info/about.json": []byte(`{"description":"` + description + `","dev_url":"` + repositoryURL + `","doc_url":"` + documentationURL + `","home":"` + projectURL + `"}`),
|
||||||
|
})
|
||||||
|
|
||||||
|
p, err := parsePackageTar(buf)
|
||||||
|
assert.NotNil(t, p)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, packageName, p.Name)
|
||||||
|
assert.Equal(t, packageVersion, p.Version)
|
||||||
|
assert.Equal(t, "linux-64", p.Subdir)
|
||||||
|
assert.Equal(t, description, p.VersionMetadata.Description)
|
||||||
|
assert.Equal(t, projectURL, p.VersionMetadata.ProjectURL)
|
||||||
|
assert.Equal(t, repositoryURL, p.VersionMetadata.RepositoryURL)
|
||||||
|
assert.Equal(t, documentationURL, p.VersionMetadata.DocumentationURL)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run(".tar.bz2", func(t *testing.T) {
|
||||||
|
tarArchive := createArchive(map[string][]byte{
|
||||||
|
"info/index.json": []byte(`{"name":"` + packageName + `","version":"` + packageVersion + `"}`),
|
||||||
|
})
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
bw, _ := bzip2.NewWriter(&buf, nil)
|
||||||
|
io.Copy(bw, tarArchive)
|
||||||
|
bw.Close()
|
||||||
|
|
||||||
|
br := bytes.NewReader(buf.Bytes())
|
||||||
|
|
||||||
|
p, err := ParsePackageBZ2(br)
|
||||||
|
assert.NotNil(t, p)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, packageName, p.Name)
|
||||||
|
assert.Equal(t, packageVersion, p.Version)
|
||||||
|
assert.False(t, p.FileMetadata.IsCondaPackage)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run(".conda", func(t *testing.T) {
|
||||||
|
tarArchive := createArchive(map[string][]byte{
|
||||||
|
"info/index.json": []byte(`{"name":"` + packageName + `","version":"` + packageVersion + `"}`),
|
||||||
|
})
|
||||||
|
|
||||||
|
var infoBuf bytes.Buffer
|
||||||
|
zsw, _ := zstd.NewWriter(&infoBuf)
|
||||||
|
io.Copy(zsw, tarArchive)
|
||||||
|
zsw.Close()
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
zpw := zip.NewWriter(&buf)
|
||||||
|
w, _ := zpw.Create("info-x.tar.zst")
|
||||||
|
w.Write(infoBuf.Bytes())
|
||||||
|
zpw.Close()
|
||||||
|
|
||||||
|
br := bytes.NewReader(buf.Bytes())
|
||||||
|
|
||||||
|
p, err := ParsePackageConda(br, int64(br.Len()))
|
||||||
|
assert.NotNil(t, p)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, packageName, p.Name)
|
||||||
|
assert.Equal(t, packageVersion, p.Version)
|
||||||
|
assert.True(t, p.FileMetadata.IsCondaPackage)
|
||||||
|
})
|
||||||
|
}
|
@ -27,6 +27,7 @@ var (
|
|||||||
LimitTotalOwnerSize int64
|
LimitTotalOwnerSize int64
|
||||||
LimitSizeComposer int64
|
LimitSizeComposer int64
|
||||||
LimitSizeConan int64
|
LimitSizeConan int64
|
||||||
|
LimitSizeConda int64
|
||||||
LimitSizeContainer int64
|
LimitSizeContainer int64
|
||||||
LimitSizeGeneric int64
|
LimitSizeGeneric int64
|
||||||
LimitSizeHelm int64
|
LimitSizeHelm int64
|
||||||
@ -66,6 +67,7 @@ func newPackages() {
|
|||||||
Packages.LimitTotalOwnerSize = mustBytes(sec, "LIMIT_TOTAL_OWNER_SIZE")
|
Packages.LimitTotalOwnerSize = mustBytes(sec, "LIMIT_TOTAL_OWNER_SIZE")
|
||||||
Packages.LimitSizeComposer = mustBytes(sec, "LIMIT_SIZE_COMPOSER")
|
Packages.LimitSizeComposer = mustBytes(sec, "LIMIT_SIZE_COMPOSER")
|
||||||
Packages.LimitSizeConan = mustBytes(sec, "LIMIT_SIZE_CONAN")
|
Packages.LimitSizeConan = mustBytes(sec, "LIMIT_SIZE_CONAN")
|
||||||
|
Packages.LimitSizeConda = mustBytes(sec, "LIMIT_SIZE_CONDA")
|
||||||
Packages.LimitSizeContainer = mustBytes(sec, "LIMIT_SIZE_CONTAINER")
|
Packages.LimitSizeContainer = mustBytes(sec, "LIMIT_SIZE_CONTAINER")
|
||||||
Packages.LimitSizeGeneric = mustBytes(sec, "LIMIT_SIZE_GENERIC")
|
Packages.LimitSizeGeneric = mustBytes(sec, "LIMIT_SIZE_GENERIC")
|
||||||
Packages.LimitSizeHelm = mustBytes(sec, "LIMIT_SIZE_HELM")
|
Packages.LimitSizeHelm = mustBytes(sec, "LIMIT_SIZE_HELM")
|
||||||
|
@ -2432,7 +2432,7 @@ teams.leave.detail = Leave %s?
|
|||||||
teams.can_create_org_repo = Create repositories
|
teams.can_create_org_repo = Create repositories
|
||||||
teams.can_create_org_repo_helper = Members can create new repositories in organization. Creator will get administrator access to the new repository.
|
teams.can_create_org_repo_helper = Members can create new repositories in organization. Creator will get administrator access to the new repository.
|
||||||
teams.none_access = No Access
|
teams.none_access = No Access
|
||||||
teams.none_access_helper = Members cannot view or do any other action on this unit.
|
teams.none_access_helper = Members cannot view or do any other action on this unit. It has no effect for public repositories.
|
||||||
teams.general_access = General Access
|
teams.general_access = General Access
|
||||||
teams.general_access_helper = Members permissions will be decided by below permission table.
|
teams.general_access_helper = Members permissions will be decided by below permission table.
|
||||||
teams.read_access = Read
|
teams.read_access = Read
|
||||||
@ -3159,6 +3159,11 @@ conan.details.repository = Repository
|
|||||||
conan.registry = Setup this registry from the command line:
|
conan.registry = Setup this registry from the command line:
|
||||||
conan.install = To install the package using Conan, run the following command:
|
conan.install = To install the package using Conan, run the following command:
|
||||||
conan.documentation = For more information on the Conan registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conan/">the documentation</a>.
|
conan.documentation = For more information on the Conan registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conan/">the documentation</a>.
|
||||||
|
conda.registry = Setup this registry as a Conda repository in your <code>.condarc</code> file:
|
||||||
|
conda.install = To install the package using Conda, run the following command:
|
||||||
|
conda.documentation = For more information on the Conda registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conda/">the documentation</a>.
|
||||||
|
conda.details.repository_site = Repository Site
|
||||||
|
conda.details.documentation_site = Documentation Site
|
||||||
container.details.type = Image Type
|
container.details.type = Image Type
|
||||||
container.details.platform = Platform
|
container.details.platform = Platform
|
||||||
container.details.repository_site = Repository Site
|
container.details.repository_site = Repository Site
|
||||||
@ -3254,7 +3259,7 @@ creation.value_placeholder = Input any content. Whitespace at the start and end
|
|||||||
creation.success = The secret '%s' has been added.
|
creation.success = The secret '%s' has been added.
|
||||||
creation.failed = Failed to add secret.
|
creation.failed = Failed to add secret.
|
||||||
deletion = Remove secret
|
deletion = Remove secret
|
||||||
deletion.description = Removing a secret will revoke its access to repositories. Continue?
|
deletion.description = Removing a secret is permanent and cannot be undone. Continue?
|
||||||
deletion.success = The secret has been removed.
|
deletion.success = The secret has been removed.
|
||||||
deletion.failed = Failed to remove secret.
|
deletion.failed = Failed to remove secret.
|
||||||
|
|
||||||
|
1
public/img/svg/gitea-conda.svg
Normal file
1
public/img/svg/gitea-conda.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg viewBox="0 0 32 32" class="svg gitea-conda" width="16" height="16" aria-hidden="true"><path fill="#43b02a" fill-rule="evenodd" stroke="#43b02a" stroke-width=".068" d="M16.559 8.137a7.2 7.2 0 0 0-1.234-1.708 7.586 7.586 0 0 0-.19 2.183 5.161 5.161 0 0 1 1.424-.475ZM13.617 9.466a7.992 7.992 0 0 0-1.993-1.2 8.123 8.123 0 0 0 .885 2.183c0 .063.443-.475 1.108-.981ZM17.445 7.188a9.143 9.143 0 0 1 1.3-2.246A7.585 7.585 0 0 0 17 2.854a8.35 8.35 0 0 0-1.3 2.278 8.451 8.451 0 0 1 1.74 2.056ZM11.592 11.744a10.276 10.276 0 0 0-2.692-.158 7.478 7.478 0 0 0 1.93 1.9 6.858 6.858 0 0 1 .759-1.74zM6.878 15.161a7.44 7.44 0 0 1 2.942-1.139 10.019 10.019 0 0 1-2.056-2.278 7.639 7.639 0 0 0-2.847 1.2 7.11 7.11 0 0 0 1.961 2.215zM10.516 14.876a6.16 6.16 0 0 0-2.815.886 9.936 9.936 0 0 0 2.815 1.2 7.683 7.683 0 0 1 0-2.088zM14.281 5.543A7.839 7.839 0 0 0 11.592 4.4 8.361 8.361 0 0 0 11.4 7a8.875 8.875 0 0 1 2.47 1.264 10.292 10.292 0 0 1 .411-2.721ZM24.025 3.234a20.488 20.488 0 0 1 .917 4.112 6.823 6.823 0 0 0-3.068 1.519 7.443 7.443 0 0 1 1.55 1.044 1.351 1.351 0 0 0 1.645.316 36.938 36.938 0 0 0 2.721-2.72 1.273 1.273 0 0 0-.159-1.835 20.521 20.521 0 0 0-3.606-2.436ZM4.379 12.06a8.67 8.67 0 0 1 2.847-1.26 7.763 7.763 0 0 1-.759-2.974 14.687 14.687 0 0 0-2.088 4.234ZM11.339 10.668a9.991 9.991 0 0 1-.949-2.784 7.928 7.928 0 0 0-2.911-.126 7.312 7.312 0 0 0 .791 2.879 9.664 9.664 0 0 1 3.069.031ZM6.119 15.73a8.894 8.894 0 0 1-2.025-2.373 14.208 14.208 0 0 0-.063 4.9 8.522 8.522 0 0 1 2.088-2.527Z"/><path fill="#43b02a" fill-rule="evenodd" stroke="#43b02a" stroke-width=".068" d="M22.538 3.487A7.581 7.581 0 0 0 20.323 5.1a11.789 11.789 0 0 1 .823 2.5 9.775 9.775 0 0 1 2.309-1.329 6.593 6.593 0 0 0-.917-2.784ZM19.374 6.3a8.608 8.608 0 0 0-.822 1.676 9.645 9.645 0 0 1 1.329.19 7.568 7.568 0 0 0-.507-1.866ZM19.659 3.9a9.577 9.577 0 0 1 2.056-1.487A15.38 15.38 0 0 0 18.046 2a9.709 9.709 0 0 1 1.613 1.9Z"/><path fill="#43b02a" d="M27.378 23.892c-1.993-1.9-2.4-3.132-4.081-1.835a7.837 7.837 0 0 1-12.591-4.144A10.179 10.179 0 0 1 6.878 16.3a9.427 9.427 0 0 0-2.562 3.321h-.032C7.163 30.5 21.178 33.035 27.663 26.233c1.076-1.139.095-1.933-.285-2.341ZM6.309 20.855a7.559 7.559 0 0 1 .917-2.025 6.872 6.872 0 0 0 2.151.538c1.013 2.689 4.556 6.264 8.922 6.264a9.632 9.632 0 0 0 6.3-2.309 12.841 12.841 0 0 1 1.772 1.771c.095.127.095.159.095.159-5.766 5.03-15.538 4.302-20.157-4.398Z"/><path fill="#43b02a" fill-rule="evenodd" stroke="#43b02a" stroke-width=".067" d="M10.67 4.11a19.934 19.934 0 0 0-.214 2.509 10.512 10.512 0 0 0-2.689-.093A18 18 0 0 1 10.67 4.11ZM12.26 3.274a9.107 9.107 0 0 1 2.445 1.053 14.083 14.083 0 0 1 1.253-2.137 12.106 12.106 0 0 0-3.698 1.084z"/></svg>
|
After Width: | Height: | Size: 2.6 KiB |
@ -16,6 +16,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers/api/packages/composer"
|
"code.gitea.io/gitea/routers/api/packages/composer"
|
||||||
"code.gitea.io/gitea/routers/api/packages/conan"
|
"code.gitea.io/gitea/routers/api/packages/conan"
|
||||||
|
"code.gitea.io/gitea/routers/api/packages/conda"
|
||||||
"code.gitea.io/gitea/routers/api/packages/container"
|
"code.gitea.io/gitea/routers/api/packages/container"
|
||||||
"code.gitea.io/gitea/routers/api/packages/generic"
|
"code.gitea.io/gitea/routers/api/packages/generic"
|
||||||
"code.gitea.io/gitea/routers/api/packages/helm"
|
"code.gitea.io/gitea/routers/api/packages/helm"
|
||||||
@ -167,6 +168,43 @@ func CommonRoutes(ctx gocontext.Context) *web.Route {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}, reqPackageAccess(perm.AccessModeRead))
|
}, reqPackageAccess(perm.AccessModeRead))
|
||||||
|
r.Group("/conda", func() {
|
||||||
|
var (
|
||||||
|
downloadPattern = regexp.MustCompile(`\A(.+/)?(.+)/((?:[^/]+(?:\.tar\.bz2|\.conda))|(?:current_)?repodata\.json(?:\.bz2)?)\z`)
|
||||||
|
uploadPattern = regexp.MustCompile(`\A(.+/)?([^/]+(?:\.tar\.bz2|\.conda))\z`)
|
||||||
|
)
|
||||||
|
|
||||||
|
r.Get("/*", func(ctx *context.Context) {
|
||||||
|
m := downloadPattern.FindStringSubmatch(ctx.Params("*"))
|
||||||
|
if len(m) == 0 {
|
||||||
|
ctx.Status(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.SetParams("channel", strings.TrimSuffix(m[1], "/"))
|
||||||
|
ctx.SetParams("architecture", m[2])
|
||||||
|
ctx.SetParams("filename", m[3])
|
||||||
|
|
||||||
|
switch m[3] {
|
||||||
|
case "repodata.json", "repodata.json.bz2", "current_repodata.json", "current_repodata.json.bz2":
|
||||||
|
conda.EnumeratePackages(ctx)
|
||||||
|
default:
|
||||||
|
conda.DownloadPackageFile(ctx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
r.Put("/*", reqPackageAccess(perm.AccessModeWrite), func(ctx *context.Context) {
|
||||||
|
m := uploadPattern.FindStringSubmatch(ctx.Params("*"))
|
||||||
|
if len(m) == 0 {
|
||||||
|
ctx.Status(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.SetParams("channel", strings.TrimSuffix(m[1], "/"))
|
||||||
|
ctx.SetParams("filename", m[2])
|
||||||
|
|
||||||
|
conda.UploadPackageFile(ctx)
|
||||||
|
})
|
||||||
|
}, reqPackageAccess(perm.AccessModeRead))
|
||||||
r.Group("/generic", func() {
|
r.Group("/generic", func() {
|
||||||
r.Group("/{packagename}/{packageversion}", func() {
|
r.Group("/{packagename}/{packageversion}", func() {
|
||||||
r.Delete("", reqPackageAccess(perm.AccessModeWrite), generic.DeletePackage)
|
r.Delete("", reqPackageAccess(perm.AccessModeWrite), generic.DeletePackage)
|
||||||
|
306
routers/api/packages/conda/conda.go
Normal file
306
routers/api/packages/conda/conda.go
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package conda
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
packages_model "code.gitea.io/gitea/models/packages"
|
||||||
|
conda_model "code.gitea.io/gitea/models/packages/conda"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
packages_module "code.gitea.io/gitea/modules/packages"
|
||||||
|
conda_module "code.gitea.io/gitea/modules/packages/conda"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
"code.gitea.io/gitea/routers/api/packages/helper"
|
||||||
|
packages_service "code.gitea.io/gitea/services/packages"
|
||||||
|
|
||||||
|
"github.com/dsnet/compress/bzip2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func apiError(ctx *context.Context, status int, obj interface{}) {
|
||||||
|
helper.LogAndProcessError(ctx, status, obj, func(message string) {
|
||||||
|
ctx.JSON(status, struct {
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}{
|
||||||
|
Reason: http.StatusText(status),
|
||||||
|
Message: message,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnumeratePackages(ctx *context.Context) {
|
||||||
|
type Info struct {
|
||||||
|
Subdir string `json:"subdir"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PackageInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
NoArch string `json:"noarch"`
|
||||||
|
Subdir string `json:"subdir"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
Build string `json:"build"`
|
||||||
|
BuildNumber int64 `json:"build_number"`
|
||||||
|
Dependencies []string `json:"depends"`
|
||||||
|
License string `json:"license"`
|
||||||
|
LicenseFamily string `json:"license_family"`
|
||||||
|
HashMD5 string `json:"md5"`
|
||||||
|
HashSHA256 string `json:"sha256"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RepoData struct {
|
||||||
|
Info Info `json:"info"`
|
||||||
|
Packages map[string]*PackageInfo `json:"packages"`
|
||||||
|
PackagesConda map[string]*PackageInfo `json:"packages.conda"`
|
||||||
|
Removed map[string]*PackageInfo `json:"removed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
repoData := &RepoData{
|
||||||
|
Info: Info{
|
||||||
|
Subdir: ctx.Params("architecture"),
|
||||||
|
},
|
||||||
|
Packages: make(map[string]*PackageInfo),
|
||||||
|
PackagesConda: make(map[string]*PackageInfo),
|
||||||
|
Removed: make(map[string]*PackageInfo),
|
||||||
|
}
|
||||||
|
|
||||||
|
pfs, err := conda_model.SearchFiles(ctx, &conda_model.FileSearchOptions{
|
||||||
|
OwnerID: ctx.Package.Owner.ID,
|
||||||
|
Channel: ctx.Params("channel"),
|
||||||
|
Subdir: repoData.Info.Subdir,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pfs) == 0 {
|
||||||
|
apiError(ctx, http.StatusNotFound, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pds := make(map[int64]*packages_model.PackageDescriptor)
|
||||||
|
|
||||||
|
for _, pf := range pfs {
|
||||||
|
pd, exists := pds[pf.VersionID]
|
||||||
|
if !exists {
|
||||||
|
pv, err := packages_model.GetVersionByID(ctx, pf.VersionID)
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pd, err = packages_model.GetPackageDescriptor(ctx, pv)
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pds[pf.VersionID] = pd
|
||||||
|
}
|
||||||
|
|
||||||
|
var pfd *packages_model.PackageFileDescriptor
|
||||||
|
for _, d := range pd.Files {
|
||||||
|
if d.File.ID == pf.ID {
|
||||||
|
pfd = d
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileMetadata *conda_module.FileMetadata
|
||||||
|
if err := json.Unmarshal([]byte(pfd.Properties.GetByName(conda_module.PropertyMetadata)), &fileMetadata); err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
versionMetadata := pd.Metadata.(*conda_module.VersionMetadata)
|
||||||
|
|
||||||
|
pi := &PackageInfo{
|
||||||
|
Name: pd.PackageProperties.GetByName(conda_module.PropertyName),
|
||||||
|
Version: pd.Version.Version,
|
||||||
|
NoArch: fileMetadata.NoArch,
|
||||||
|
Subdir: repoData.Info.Subdir,
|
||||||
|
Timestamp: fileMetadata.Timestamp,
|
||||||
|
Build: fileMetadata.Build,
|
||||||
|
BuildNumber: fileMetadata.BuildNumber,
|
||||||
|
Dependencies: fileMetadata.Dependencies,
|
||||||
|
License: versionMetadata.License,
|
||||||
|
LicenseFamily: versionMetadata.LicenseFamily,
|
||||||
|
HashMD5: pfd.Blob.HashMD5,
|
||||||
|
HashSHA256: pfd.Blob.HashSHA256,
|
||||||
|
Size: pfd.Blob.Size,
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileMetadata.IsCondaPackage {
|
||||||
|
repoData.PackagesConda[pfd.File.Name] = pi
|
||||||
|
} else {
|
||||||
|
repoData.Packages[pfd.File.Name] = pi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := ctx.Resp
|
||||||
|
|
||||||
|
var w io.Writer = resp
|
||||||
|
|
||||||
|
if strings.HasSuffix(ctx.Params("filename"), ".json") {
|
||||||
|
resp.Header().Set("Content-Type", "application/json")
|
||||||
|
} else {
|
||||||
|
resp.Header().Set("Content-Type", "application/x-bzip2")
|
||||||
|
|
||||||
|
zw, err := bzip2.NewWriter(w, nil)
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer zw.Close()
|
||||||
|
|
||||||
|
w = zw
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(repoData); err != nil {
|
||||||
|
log.Error("JSON encode: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UploadPackageFile(ctx *context.Context) {
|
||||||
|
upload, close, err := ctx.UploadStream()
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if close {
|
||||||
|
defer upload.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := packages_module.CreateHashedBufferFromReader(upload, 32*1024*1024)
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer buf.Close()
|
||||||
|
|
||||||
|
var pck *conda_module.Package
|
||||||
|
if strings.HasSuffix(strings.ToLower(ctx.Params("filename")), ".tar.bz2") {
|
||||||
|
pck, err = conda_module.ParsePackageBZ2(buf)
|
||||||
|
} else {
|
||||||
|
pck, err = conda_module.ParsePackageConda(buf, buf.Size())
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, util.ErrInvalidArgument) {
|
||||||
|
apiError(ctx, http.StatusBadRequest, err)
|
||||||
|
} else {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := buf.Seek(0, io.SeekStart); err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fullName := pck.Name
|
||||||
|
|
||||||
|
channel := ctx.Params("channel")
|
||||||
|
if channel != "" {
|
||||||
|
fullName = channel + "/" + pck.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
extension := ".tar.bz2"
|
||||||
|
if pck.FileMetadata.IsCondaPackage {
|
||||||
|
extension = ".conda"
|
||||||
|
}
|
||||||
|
|
||||||
|
fileMetadataRaw, err := json.Marshal(pck.FileMetadata)
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = packages_service.CreatePackageOrAddFileToExisting(
|
||||||
|
&packages_service.PackageCreationInfo{
|
||||||
|
PackageInfo: packages_service.PackageInfo{
|
||||||
|
Owner: ctx.Package.Owner,
|
||||||
|
PackageType: packages_model.TypeConda,
|
||||||
|
Name: fullName,
|
||||||
|
Version: pck.Version,
|
||||||
|
},
|
||||||
|
SemverCompatible: false,
|
||||||
|
Creator: ctx.Doer,
|
||||||
|
Metadata: pck.VersionMetadata,
|
||||||
|
PackageProperties: map[string]string{
|
||||||
|
conda_module.PropertyName: pck.Name,
|
||||||
|
conda_module.PropertyChannel: channel,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&packages_service.PackageFileCreationInfo{
|
||||||
|
PackageFileInfo: packages_service.PackageFileInfo{
|
||||||
|
Filename: fmt.Sprintf("%s-%s-%s%s", pck.Name, pck.Version, pck.FileMetadata.Build, extension),
|
||||||
|
CompositeKey: pck.Subdir,
|
||||||
|
},
|
||||||
|
Creator: ctx.Doer,
|
||||||
|
Data: buf,
|
||||||
|
IsLead: true,
|
||||||
|
Properties: map[string]string{
|
||||||
|
conda_module.PropertySubdir: pck.Subdir,
|
||||||
|
conda_module.PropertyMetadata: string(fileMetadataRaw),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
switch err {
|
||||||
|
case packages_model.ErrDuplicatePackageFile:
|
||||||
|
apiError(ctx, http.StatusConflict, err)
|
||||||
|
case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
|
||||||
|
apiError(ctx, http.StatusForbidden, err)
|
||||||
|
default:
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusCreated)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DownloadPackageFile(ctx *context.Context) {
|
||||||
|
pfs, err := conda_model.SearchFiles(ctx, &conda_model.FileSearchOptions{
|
||||||
|
OwnerID: ctx.Package.Owner.ID,
|
||||||
|
Channel: ctx.Params("channel"),
|
||||||
|
Subdir: ctx.Params("architecture"),
|
||||||
|
Filename: ctx.Params("filename"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pfs) != 1 {
|
||||||
|
apiError(ctx, http.StatusNotFound, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pf := pfs[0]
|
||||||
|
|
||||||
|
s, _, err := packages_service.GetPackageFileStream(ctx, pf)
|
||||||
|
if err != nil {
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
ctx.ServeContent(s, &context.ServeHeaderOptions{
|
||||||
|
Filename: pf.Name,
|
||||||
|
LastModified: pf.CreatedUnix.AsLocalTime(),
|
||||||
|
})
|
||||||
|
}
|
@ -40,7 +40,7 @@ func ListPackages(ctx *context.APIContext) {
|
|||||||
// in: query
|
// in: query
|
||||||
// description: package type filter
|
// description: package type filter
|
||||||
// type: string
|
// type: string
|
||||||
// enum: [composer, conan, container, generic, helm, maven, npm, nuget, pub, pypi, rubygems, vagrant]
|
// enum: [composer, conan, conda, container, generic, helm, maven, npm, nuget, pub, pypi, rubygems, vagrant]
|
||||||
// - name: q
|
// - name: q
|
||||||
// in: query
|
// in: query
|
||||||
// description: name filter
|
// description: name filter
|
||||||
|
@ -22,6 +22,7 @@ const (
|
|||||||
func DefaultOrSystemWebhooks(ctx *context.Context) {
|
func DefaultOrSystemWebhooks(ctx *context.Context) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
ctx.Data["Title"] = ctx.Tr("admin.hooks")
|
||||||
ctx.Data["PageIsAdminSystemHooks"] = true
|
ctx.Data["PageIsAdminSystemHooks"] = true
|
||||||
ctx.Data["PageIsAdminDefaultHooks"] = true
|
ctx.Data["PageIsAdminDefaultHooks"] = true
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
secret_model "code.gitea.io/gitea/models/secret"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/models/webhook"
|
"code.gitea.io/gitea/models/webhook"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
@ -38,8 +37,6 @@ const (
|
|||||||
tplSettingsHooks base.TplName = "org/settings/hooks"
|
tplSettingsHooks base.TplName = "org/settings/hooks"
|
||||||
// tplSettingsLabels template path for render labels settings
|
// tplSettingsLabels template path for render labels settings
|
||||||
tplSettingsLabels base.TplName = "org/settings/labels"
|
tplSettingsLabels base.TplName = "org/settings/labels"
|
||||||
// tplSettingsSecrets template path for render secrets settings
|
|
||||||
tplSettingsSecrets base.TplName = "org/settings/secrets"
|
|
||||||
// tplSettingsRunners template path for render runners settings
|
// tplSettingsRunners template path for render runners settings
|
||||||
tplSettingsRunners base.TplName = "org/settings/runners"
|
tplSettingsRunners base.TplName = "org/settings/runners"
|
||||||
// tplSettingsRunnersEdit template path for render runners edit settings
|
// tplSettingsRunnersEdit template path for render runners edit settings
|
||||||
@ -253,51 +250,3 @@ func Labels(ctx *context.Context) {
|
|||||||
ctx.Data["LabelTemplates"] = repo_module.LabelTemplates
|
ctx.Data["LabelTemplates"] = repo_module.LabelTemplates
|
||||||
ctx.HTML(http.StatusOK, tplSettingsLabels)
|
ctx.HTML(http.StatusOK, tplSettingsLabels)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secrets render organization secrets page
|
|
||||||
func Secrets(ctx *context.Context) {
|
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.secrets")
|
|
||||||
ctx.Data["PageIsOrgSettings"] = true
|
|
||||||
ctx.Data["PageIsOrgSettingsSecrets"] = true
|
|
||||||
|
|
||||||
secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: ctx.Org.Organization.ID})
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("FindSecrets", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Data["Secrets"] = secrets
|
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplSettingsSecrets)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SecretsPost add secrets
|
|
||||||
func SecretsPost(ctx *context.Context) {
|
|
||||||
form := web.GetForm(ctx).(*forms.AddSecretForm)
|
|
||||||
|
|
||||||
_, err := secret_model.InsertEncryptedSecret(ctx, ctx.Org.Organization.ID, 0, form.Title, form.Content)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Flash.Error(ctx.Tr("secrets.creation.failed"))
|
|
||||||
log.Error("validate secret: %v", err)
|
|
||||||
ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Trace("Org %d: secret added", ctx.Org.Organization.ID)
|
|
||||||
ctx.Flash.Success(ctx.Tr("secrets.creation.success", form.Title))
|
|
||||||
ctx.Redirect(ctx.Org.OrgLink + "/settings/secrets")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SecretsDelete delete secrets
|
|
||||||
func SecretsDelete(ctx *context.Context) {
|
|
||||||
id := ctx.FormInt64("id")
|
|
||||||
if _, err := db.DeleteByBean(ctx, &secret_model.Secret{ID: id}); err != nil {
|
|
||||||
ctx.Flash.Error(ctx.Tr("secrets.deletion.failed"))
|
|
||||||
log.Error("delete secret %d: %v", id, err)
|
|
||||||
} else {
|
|
||||||
ctx.Flash.Success(ctx.Tr("secrets.deletion.success"))
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
|
||||||
"redirect": ctx.Org.OrgLink + "/settings/secrets",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
48
routers/web/org/setting_secrets.go
Normal file
48
routers/web/org/setting_secrets.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package org
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
shared "code.gitea.io/gitea/routers/web/shared/secrets"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tplSettingsSecrets base.TplName = "org/settings/secrets"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Secrets render organization secrets page
|
||||||
|
func Secrets(ctx *context.Context) {
|
||||||
|
ctx.Data["Title"] = ctx.Tr("secrets.secrets")
|
||||||
|
ctx.Data["PageIsOrgSettings"] = true
|
||||||
|
ctx.Data["PageIsOrgSettingsSecrets"] = true
|
||||||
|
|
||||||
|
shared.SetSecretsContext(ctx, ctx.ContextUser.ID, 0)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.HTML(http.StatusOK, tplSettingsSecrets)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretsPost add secrets
|
||||||
|
func SecretsPost(ctx *context.Context) {
|
||||||
|
shared.PerformSecretsPost(
|
||||||
|
ctx,
|
||||||
|
ctx.ContextUser.ID,
|
||||||
|
0,
|
||||||
|
ctx.Org.OrgLink+"/settings/secrets",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretsDelete delete secrets
|
||||||
|
func SecretsDelete(ctx *context.Context) {
|
||||||
|
shared.PerformSecretsDelete(
|
||||||
|
ctx,
|
||||||
|
ctx.Org.OrgLink+"/settings/secrets",
|
||||||
|
)
|
||||||
|
}
|
@ -328,6 +328,14 @@ func NewRelease(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||||
|
var err error
|
||||||
|
// Get assignees.
|
||||||
|
ctx.Data["Assignees"], err = repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetAssignees", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
upload.AddUploadContext(ctx, "release")
|
upload.AddUploadContext(ctx, "release")
|
||||||
ctx.HTML(http.StatusOK, tplReleaseNew)
|
ctx.HTML(http.StatusOK, tplReleaseNew)
|
||||||
}
|
}
|
||||||
@ -484,6 +492,13 @@ func EditRelease(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
ctx.Data["attachments"] = rel.Attachments
|
ctx.Data["attachments"] = rel.Attachments
|
||||||
|
|
||||||
|
// Get assignees.
|
||||||
|
ctx.Data["Assignees"], err = repo_model.GetRepoAssignees(ctx, rel.Repo)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetAssignees", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplReleaseNew)
|
ctx.HTML(http.StatusOK, tplReleaseNew)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
"code.gitea.io/gitea/models/organization"
|
"code.gitea.io/gitea/models/organization"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
secret_model "code.gitea.io/gitea/models/secret"
|
|
||||||
unit_model "code.gitea.io/gitea/models/unit"
|
unit_model "code.gitea.io/gitea/models/unit"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
@ -61,7 +60,7 @@ const (
|
|||||||
// SettingsCtxData is a middleware that sets all the general context data for the
|
// SettingsCtxData is a middleware that sets all the general context data for the
|
||||||
// settings template.
|
// settings template.
|
||||||
func SettingsCtxData(ctx *context.Context) {
|
func SettingsCtxData(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
ctx.Data["Title"] = ctx.Tr("repo.settings.options")
|
||||||
ctx.Data["PageIsSettingsOptions"] = true
|
ctx.Data["PageIsSettingsOptions"] = true
|
||||||
ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate
|
ctx.Data["ForcePrivate"] = setting.Repository.ForcePrivate
|
||||||
ctx.Data["MirrorsEnabled"] = setting.Mirror.Enabled
|
ctx.Data["MirrorsEnabled"] = setting.Mirror.Enabled
|
||||||
@ -880,7 +879,7 @@ func handleSettingRemoteAddrError(ctx *context.Context, err error, form *forms.R
|
|||||||
|
|
||||||
// Collaboration render a repository's collaboration page
|
// Collaboration render a repository's collaboration page
|
||||||
func Collaboration(ctx *context.Context) {
|
func Collaboration(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
ctx.Data["Title"] = ctx.Tr("repo.settings.collaboration")
|
||||||
ctx.Data["PageIsSettingsCollaboration"] = true
|
ctx.Data["PageIsSettingsCollaboration"] = true
|
||||||
|
|
||||||
users, err := repo_model.GetCollaborators(ctx, ctx.Repo.Repository.ID, db.ListOptions{})
|
users, err := repo_model.GetCollaborators(ctx, ctx.Repo.Repository.ID, db.ListOptions{})
|
||||||
@ -1120,7 +1119,7 @@ func GitHooksEditPost(ctx *context.Context) {
|
|||||||
|
|
||||||
// DeployKeys render the deploy keys list of a repository page
|
// DeployKeys render the deploy keys list of a repository page
|
||||||
func DeployKeys(ctx *context.Context) {
|
func DeployKeys(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys")
|
ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") + " / " + ctx.Tr("secrets.secrets")
|
||||||
ctx.Data["PageIsSettingsKeys"] = true
|
ctx.Data["PageIsSettingsKeys"] = true
|
||||||
ctx.Data["DisableSSH"] = setting.SSH.Disabled
|
ctx.Data["DisableSSH"] = setting.SSH.Disabled
|
||||||
|
|
||||||
@ -1131,33 +1130,9 @@ func DeployKeys(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
ctx.Data["Deploykeys"] = keys
|
ctx.Data["Deploykeys"] = keys
|
||||||
|
|
||||||
secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{RepoID: ctx.Repo.Repository.ID})
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("FindSecrets", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Data["Secrets"] = secrets
|
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplDeployKeys)
|
ctx.HTML(http.StatusOK, tplDeployKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecretsPost response for creating a new secret
|
|
||||||
func SecretsPost(ctx *context.Context) {
|
|
||||||
form := web.GetForm(ctx).(*forms.AddSecretForm)
|
|
||||||
|
|
||||||
_, err := secret_model.InsertEncryptedSecret(ctx, 0, ctx.Repo.Repository.ID, form.Title, form.Content)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Flash.Error(ctx.Tr("secrets.creation.failed"))
|
|
||||||
log.Error("validate secret: %v", err)
|
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Trace("Secret added: %d", ctx.Repo.Repository.ID)
|
|
||||||
ctx.Flash.Success(ctx.Tr("secrets.creation.success", form.Title))
|
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeployKeysPost response for adding a deploy key of a repository
|
// DeployKeysPost response for adding a deploy key of a repository
|
||||||
func DeployKeysPost(ctx *context.Context) {
|
func DeployKeysPost(ctx *context.Context) {
|
||||||
form := web.GetForm(ctx).(*forms.AddKeyForm)
|
form := web.GetForm(ctx).(*forms.AddKeyForm)
|
||||||
@ -1219,20 +1194,6 @@ func DeployKeysPost(ctx *context.Context) {
|
|||||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
|
ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys")
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteSecret(ctx *context.Context) {
|
|
||||||
id := ctx.FormInt64("id")
|
|
||||||
if _, err := db.DeleteByBean(ctx, &secret_model.Secret{ID: id}); err != nil {
|
|
||||||
ctx.Flash.Error(ctx.Tr("secrets.deletion.failed"))
|
|
||||||
log.Error("delete secret %d: %v", id, err)
|
|
||||||
} else {
|
|
||||||
ctx.Flash.Success(ctx.Tr("secrets.deletion.success"))
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
|
||||||
"redirect": ctx.Repo.RepoLink + "/settings/keys",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteDeployKey response for deleting a deploy key
|
// DeleteDeployKey response for deleting a deploy key
|
||||||
func DeleteDeployKey(ctx *context.Context) {
|
func DeleteDeployKey(ctx *context.Context) {
|
||||||
if err := asymkey_service.DeleteDeployKey(ctx.Doer, ctx.FormInt64("id")); err != nil {
|
if err := asymkey_service.DeleteDeployKey(ctx.Doer, ctx.FormInt64("id")); err != nil {
|
||||||
|
@ -31,7 +31,7 @@ const (
|
|||||||
|
|
||||||
// ProtectedBranchRules render the page to protect the repository
|
// ProtectedBranchRules render the page to protect the repository
|
||||||
func ProtectedBranchRules(ctx *context.Context) {
|
func ProtectedBranchRules(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
ctx.Data["Title"] = ctx.Tr("repo.settings.branches")
|
||||||
ctx.Data["PageIsSettingsBranches"] = true
|
ctx.Data["PageIsSettingsBranches"] = true
|
||||||
|
|
||||||
rules, err := git_model.FindRepoProtectedBranchRules(ctx, ctx.Repo.Repository.ID)
|
rules, err := git_model.FindRepoProtectedBranchRules(ctx, ctx.Repo.Repository.ID)
|
||||||
@ -46,7 +46,7 @@ func ProtectedBranchRules(ctx *context.Context) {
|
|||||||
|
|
||||||
// SetDefaultBranchPost set default branch
|
// SetDefaultBranchPost set default branch
|
||||||
func SetDefaultBranchPost(ctx *context.Context) {
|
func SetDefaultBranchPost(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
ctx.Data["Title"] = ctx.Tr("repo.settings.branches.update_default_branch")
|
||||||
ctx.Data["PageIsSettingsBranches"] = true
|
ctx.Data["PageIsSettingsBranches"] = true
|
||||||
|
|
||||||
repo := ctx.Repo.Repository
|
repo := ctx.Repo.Repository
|
||||||
|
46
routers/web/repo/setting_secrets.go
Normal file
46
routers/web/repo/setting_secrets.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
shared "code.gitea.io/gitea/routers/web/shared/secrets"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tplSecrets base.TplName = "repo/settings/secrets"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Secrets(ctx *context.Context) {
|
||||||
|
ctx.Data["Title"] = ctx.Tr("secrets.secrets")
|
||||||
|
ctx.Data["PageIsSettingsSecrets"] = true
|
||||||
|
ctx.Data["DisableSSH"] = setting.SSH.Disabled
|
||||||
|
|
||||||
|
shared.SetSecretsContext(ctx, 0, ctx.Repo.Repository.ID)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.HTML(http.StatusOK, tplSecrets)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecretsPost(ctx *context.Context) {
|
||||||
|
shared.PerformSecretsPost(
|
||||||
|
ctx,
|
||||||
|
0,
|
||||||
|
ctx.Repo.Repository.ID,
|
||||||
|
ctx.Repo.RepoLink+"/settings/secrets",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteSecret(ctx *context.Context) {
|
||||||
|
shared.PerformSecretsDelete(
|
||||||
|
ctx,
|
||||||
|
ctx.Repo.RepoLink+"/settings/secrets",
|
||||||
|
)
|
||||||
|
}
|
@ -133,7 +133,7 @@ func DeleteProtectedTagPost(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setTagsContext(ctx *context.Context) error {
|
func setTagsContext(ctx *context.Context) error {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
ctx.Data["Title"] = ctx.Tr("repo.settings.tags")
|
||||||
ctx.Data["PageIsSettingsTags"] = true
|
ctx.Data["PageIsSettingsTags"] = true
|
||||||
|
|
||||||
protectedTags, err := git_model.GetProtectedTags(ctx, ctx.Repo.Repository.ID)
|
protectedTags, err := git_model.GetProtectedTags(ctx, ctx.Repo.Repository.ID)
|
||||||
|
54
routers/web/shared/secrets/secrets.go
Normal file
54
routers/web/shared/secrets/secrets.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package secrets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
secret_model "code.gitea.io/gitea/models/secret"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
"code.gitea.io/gitea/services/forms"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) {
|
||||||
|
secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: ownerID, RepoID: repoID})
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("FindSecrets", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["Secrets"] = secrets
|
||||||
|
}
|
||||||
|
|
||||||
|
func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL string) {
|
||||||
|
form := web.GetForm(ctx).(*forms.AddSecretForm)
|
||||||
|
|
||||||
|
s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, form.Title, form.Content)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("InsertEncryptedSecret: %v", err)
|
||||||
|
ctx.Flash.Error(ctx.Tr("secrets.creation.failed"))
|
||||||
|
} else {
|
||||||
|
ctx.Flash.Success(ctx.Tr("secrets.creation.success", s.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Redirect(redirectURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PerformSecretsDelete(ctx *context.Context, redirectURL string) {
|
||||||
|
id := ctx.FormInt64("id")
|
||||||
|
|
||||||
|
if _, err := db.DeleteByBean(ctx, &secret_model.Secret{ID: id}); err != nil {
|
||||||
|
log.Error("Delete secret %d failed: %v", id, err)
|
||||||
|
ctx.Flash.Error(ctx.Tr("secrets.deletion.failed"))
|
||||||
|
} else {
|
||||||
|
ctx.Flash.Success(ctx.Tr("secrets.deletion.success"))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, map[string]interface{}{
|
||||||
|
"redirect": redirectURL,
|
||||||
|
})
|
||||||
|
}
|
@ -30,7 +30,7 @@ const (
|
|||||||
|
|
||||||
// Account renders change user's password, user's email and user suicide page
|
// Account renders change user's password, user's email and user suicide page
|
||||||
func Account(ctx *context.Context) {
|
func Account(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("settings")
|
ctx.Data["Title"] = ctx.Tr("settings.account")
|
||||||
ctx.Data["PageIsSettingsAccount"] = true
|
ctx.Data["PageIsSettingsAccount"] = true
|
||||||
ctx.Data["Email"] = ctx.Doer.Email
|
ctx.Data["Email"] = ctx.Doer.Email
|
||||||
ctx.Data["EnableNotifyMail"] = setting.Service.EnableNotifyMail
|
ctx.Data["EnableNotifyMail"] = setting.Service.EnableNotifyMail
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
// AdoptOrDeleteRepository adopts or deletes a repository
|
// AdoptOrDeleteRepository adopts or deletes a repository
|
||||||
func AdoptOrDeleteRepository(ctx *context.Context) {
|
func AdoptOrDeleteRepository(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("settings")
|
ctx.Data["Title"] = ctx.Tr("settings.adopt")
|
||||||
ctx.Data["PageIsSettingsRepos"] = true
|
ctx.Data["PageIsSettingsRepos"] = true
|
||||||
allowAdopt := ctx.IsUserSiteAdmin() || setting.Repository.AllowAdoptionOfUnadoptedRepositories
|
allowAdopt := ctx.IsUserSiteAdmin() || setting.Repository.AllowAdoptionOfUnadoptedRepositories
|
||||||
ctx.Data["allowAdopt"] = allowAdopt
|
ctx.Data["allowAdopt"] = allowAdopt
|
||||||
|
@ -21,7 +21,7 @@ const (
|
|||||||
|
|
||||||
// Applications render manage access token page
|
// Applications render manage access token page
|
||||||
func Applications(ctx *context.Context) {
|
func Applications(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("settings")
|
ctx.Data["Title"] = ctx.Tr("settings.applications")
|
||||||
ctx.Data["PageIsSettingsApplications"] = true
|
ctx.Data["PageIsSettingsApplications"] = true
|
||||||
|
|
||||||
loadApplicationsData(ctx)
|
loadApplicationsData(ctx)
|
||||||
|
@ -23,7 +23,7 @@ const (
|
|||||||
|
|
||||||
// Keys render user's SSH/GPG public keys page
|
// Keys render user's SSH/GPG public keys page
|
||||||
func Keys(ctx *context.Context) {
|
func Keys(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("settings")
|
ctx.Data["Title"] = ctx.Tr("settings.ssh_gpg_keys")
|
||||||
ctx.Data["PageIsSettingsKeys"] = true
|
ctx.Data["PageIsSettingsKeys"] = true
|
||||||
ctx.Data["DisableSSH"] = setting.SSH.Disabled
|
ctx.Data["DisableSSH"] = setting.SSH.Disabled
|
||||||
ctx.Data["BuiltinSSH"] = setting.SSH.StartBuiltinServer
|
ctx.Data["BuiltinSSH"] = setting.SSH.StartBuiltinServer
|
||||||
|
@ -42,7 +42,7 @@ const (
|
|||||||
|
|
||||||
// Profile render user's profile page
|
// Profile render user's profile page
|
||||||
func Profile(ctx *context.Context) {
|
func Profile(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("settings")
|
ctx.Data["Title"] = ctx.Tr("settings.profile")
|
||||||
ctx.Data["PageIsSettingsProfile"] = true
|
ctx.Data["PageIsSettingsProfile"] = true
|
||||||
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
|
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
|
||||||
|
|
||||||
@ -219,7 +219,7 @@ func DeleteAvatar(ctx *context.Context) {
|
|||||||
|
|
||||||
// Organization render all the organization of the user
|
// Organization render all the organization of the user
|
||||||
func Organization(ctx *context.Context) {
|
func Organization(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("settings")
|
ctx.Data["Title"] = ctx.Tr("settings.organization")
|
||||||
ctx.Data["PageIsSettingsOrganization"] = true
|
ctx.Data["PageIsSettingsOrganization"] = true
|
||||||
|
|
||||||
opts := organization.FindOrgOptions{
|
opts := organization.FindOrgOptions{
|
||||||
@ -254,7 +254,7 @@ func Organization(ctx *context.Context) {
|
|||||||
|
|
||||||
// Repos display a list of all repositories of the user
|
// Repos display a list of all repositories of the user
|
||||||
func Repos(ctx *context.Context) {
|
func Repos(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("settings")
|
ctx.Data["Title"] = ctx.Tr("settings.repos")
|
||||||
ctx.Data["PageIsSettingsRepos"] = true
|
ctx.Data["PageIsSettingsRepos"] = true
|
||||||
ctx.Data["allowAdopt"] = ctx.IsUserSiteAdmin() || setting.Repository.AllowAdoptionOfUnadoptedRepositories
|
ctx.Data["allowAdopt"] = ctx.IsUserSiteAdmin() || setting.Repository.AllowAdoptionOfUnadoptedRepositories
|
||||||
ctx.Data["allowDelete"] = ctx.IsUserSiteAdmin() || setting.Repository.AllowDeleteOfUnadoptedRepositories
|
ctx.Data["allowDelete"] = ctx.IsUserSiteAdmin() || setting.Repository.AllowDeleteOfUnadoptedRepositories
|
||||||
@ -360,7 +360,7 @@ func Repos(ctx *context.Context) {
|
|||||||
|
|
||||||
// Appearance render user's appearance settings
|
// Appearance render user's appearance settings
|
||||||
func Appearance(ctx *context.Context) {
|
func Appearance(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("settings")
|
ctx.Data["Title"] = ctx.Tr("settings.appearance")
|
||||||
ctx.Data["PageIsSettingsAppearance"] = true
|
ctx.Data["PageIsSettingsAppearance"] = true
|
||||||
|
|
||||||
var hiddenCommentTypes *big.Int
|
var hiddenCommentTypes *big.Int
|
||||||
|
45
routers/web/user/setting/secrets.go
Normal file
45
routers/web/user/setting/secrets.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
shared "code.gitea.io/gitea/routers/web/shared/secrets"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tplSettingsSecrets base.TplName = "user/settings/secrets"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Secrets(ctx *context.Context) {
|
||||||
|
ctx.Data["Title"] = ctx.Tr("secrets.secrets")
|
||||||
|
ctx.Data["PageIsSettingsSecrets"] = true
|
||||||
|
|
||||||
|
shared.SetSecretsContext(ctx, ctx.Doer.ID, 0)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.HTML(http.StatusOK, tplSettingsSecrets)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecretsPost(ctx *context.Context) {
|
||||||
|
shared.PerformSecretsPost(
|
||||||
|
ctx,
|
||||||
|
ctx.Doer.ID,
|
||||||
|
0,
|
||||||
|
setting.AppSubURL+"/user/settings/secrets",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecretsDelete(ctx *context.Context) {
|
||||||
|
shared.PerformSecretsDelete(
|
||||||
|
ctx,
|
||||||
|
setting.AppSubURL+"/user/settings/secrets",
|
||||||
|
)
|
||||||
|
}
|
@ -22,7 +22,7 @@ const (
|
|||||||
|
|
||||||
// Security render change user's password page and 2FA
|
// Security render change user's password page and 2FA
|
||||||
func Security(ctx *context.Context) {
|
func Security(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("settings")
|
ctx.Data["Title"] = ctx.Tr("settings.security")
|
||||||
ctx.Data["PageIsSettingsSecurity"] = true
|
ctx.Data["PageIsSettingsSecurity"] = true
|
||||||
|
|
||||||
if ctx.FormString("openid.return_to") != "" {
|
if ctx.FormString("openid.return_to") != "" {
|
||||||
|
@ -469,6 +469,11 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}, packagesEnabled)
|
}, packagesEnabled)
|
||||||
|
m.Group("/secrets", func() {
|
||||||
|
m.Get("", user_setting.Secrets)
|
||||||
|
m.Post("", web.Bind(forms.AddSecretForm{}), user_setting.SecretsPost)
|
||||||
|
m.Post("/delete", user_setting.SecretsDelete)
|
||||||
|
})
|
||||||
m.Get("/organization", user_setting.Organization)
|
m.Get("/organization", user_setting.Organization)
|
||||||
m.Get("/repos", user_setting.Repos)
|
m.Get("/repos", user_setting.Repos)
|
||||||
m.Post("/repos/unadopted", user_setting.AdoptOrDeleteRepository)
|
m.Post("/repos/unadopted", user_setting.AdoptOrDeleteRepository)
|
||||||
@ -982,10 +987,12 @@ func RegisterRoutes(m *web.Route) {
|
|||||||
m.Combo("").Get(repo.DeployKeys).
|
m.Combo("").Get(repo.DeployKeys).
|
||||||
Post(web.Bind(forms.AddKeyForm{}), repo.DeployKeysPost)
|
Post(web.Bind(forms.AddKeyForm{}), repo.DeployKeysPost)
|
||||||
m.Post("/delete", repo.DeleteDeployKey)
|
m.Post("/delete", repo.DeleteDeployKey)
|
||||||
m.Group("/secrets", func() {
|
})
|
||||||
m.Post("", web.Bind(forms.AddSecretForm{}), repo.SecretsPost)
|
|
||||||
m.Post("/delete", repo.DeleteSecret)
|
m.Group("/secrets", func() {
|
||||||
})
|
m.Get("", repo.Secrets)
|
||||||
|
m.Post("", web.Bind(forms.AddSecretForm{}), repo.SecretsPost)
|
||||||
|
m.Post("/delete", repo.DeleteSecret)
|
||||||
})
|
})
|
||||||
|
|
||||||
m.Group("/lfs", func() {
|
m.Group("/lfs", func() {
|
||||||
|
@ -137,7 +137,7 @@ func notify(ctx context.Context, input *notifyInput) error {
|
|||||||
return fmt.Errorf("gitRepo.GetCommit: %w", err)
|
return fmt.Errorf("gitRepo.GetCommit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
workflows, err := actions_module.DetectWorkflows(commit, input.Event)
|
workflows, err := actions_module.DetectWorkflows(commit, input.Event, input.Payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("DetectWorkflows: %w", err)
|
return fmt.Errorf("DetectWorkflows: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
type PackageCleanupRuleForm struct {
|
type PackageCleanupRuleForm struct {
|
||||||
ID int64
|
ID int64
|
||||||
Enabled bool
|
Enabled bool
|
||||||
Type string `binding:"Required;In(composer,conan,container,generic,helm,maven,npm,nuget,pub,pypi,rubygems,vagrant)"`
|
Type string `binding:"Required;In(composer,conan,conda,container,generic,helm,maven,npm,nuget,pub,pypi,rubygems,vagrant)"`
|
||||||
KeepCount int `binding:"In(0,1,5,10,25,50,100)"`
|
KeepCount int `binding:"In(0,1,5,10,25,50,100)"`
|
||||||
KeepPattern string `binding:"RegexPattern"`
|
KeepPattern string `binding:"RegexPattern"`
|
||||||
RemoveDays int `binding:"In(0,7,14,30,60,90,180)"`
|
RemoveDays int `binding:"In(0,7,14,30,60,90,180)"`
|
||||||
|
@ -339,6 +339,8 @@ func CheckSizeQuotaExceeded(ctx context.Context, doer, owner *user_model.User, p
|
|||||||
typeSpecificSize = setting.Packages.LimitSizeComposer
|
typeSpecificSize = setting.Packages.LimitSizeComposer
|
||||||
case packages_model.TypeConan:
|
case packages_model.TypeConan:
|
||||||
typeSpecificSize = setting.Packages.LimitSizeConan
|
typeSpecificSize = setting.Packages.LimitSizeConan
|
||||||
|
case packages_model.TypeConda:
|
||||||
|
typeSpecificSize = setting.Packages.LimitSizeConda
|
||||||
case packages_model.TypeContainer:
|
case packages_model.TypeContainer:
|
||||||
typeSpecificSize = setting.Packages.LimitSizeContainer
|
typeSpecificSize = setting.Packages.LimitSizeContainer
|
||||||
case packages_model.TypeGeneric:
|
case packages_model.TypeGeneric:
|
||||||
|
@ -298,7 +298,6 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pr.Issue.PullRequest = pr
|
|
||||||
notification.NotifyPullRequestSynchronized(ctx, doer, pr)
|
notification.NotifyPullRequestSynchronized(ctx, doer, pr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin config">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin config">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="twelve wide column content">
|
<div class="twelve wide column content">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin config">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin config">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
|
|
||||||
{{template "user/settings/applications_oauth2_edit_form" .}}
|
{{template "user/settings/applications_oauth2_edit_form" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin edit authentication">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin edit authentication">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin authentication">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin authentication">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin new authentication">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin new authentication">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin config">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin config">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin dashboard">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin dashboard">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin user">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin user">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin settings new webhook">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin settings new webhook">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin hooks">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin hooks">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin monitor">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin monitor">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin notice">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin notice">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin user">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin user">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin user">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin user">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin monitor">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin monitor">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin user">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin user">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin user">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin user">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin monitor">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin monitor">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin edit user">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin edit user">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin user">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin user">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content admin new user">
|
<div role="main" aria-label="{{.Title}}" class="page-content admin new user">
|
||||||
{{template "admin/navbar" .}}
|
{{template "admin/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content explore users">
|
<div role="main" aria-label="{{.Title}}" class="page-content explore users">
|
||||||
{{template "explore/navbar" .}}
|
{{template "explore/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "code/searchform" .}}
|
{{template "code/searchform" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content explore users">
|
<div role="main" aria-label="{{.Title}}" class="page-content explore users">
|
||||||
{{template "explore/navbar" .}}
|
{{template "explore/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "explore/search" .}}
|
{{template "explore/search" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content explore repositories">
|
<div role="main" aria-label="{{.Title}}" class="page-content explore repositories">
|
||||||
{{template "explore/navbar" .}}
|
{{template "explore/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "explore/repo_search" .}}
|
{{template "explore/repo_search" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content explore users">
|
<div role="main" aria-label="{{.Title}}" class="page-content explore users">
|
||||||
{{template "explore/navbar" .}}
|
{{template "explore/navbar" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "explore/search" .}}
|
{{template "explore/search" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content home">
|
<div role="main" aria-label="{{if .IsSigned}}{{.locale.Tr "dashboard"}}{{else}}{{.locale.Tr "home"}}{{end}}" class="page-content home">
|
||||||
<div class="ui stackable middle very relaxed page grid">
|
<div class="ui stackable middle very relaxed page grid">
|
||||||
<div class="sixteen wide center aligned centered column">
|
<div class="sixteen wide center aligned centered column">
|
||||||
<div>
|
<div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content install">
|
<div role="main" aria-label="{{.Title}}" class="page-content install">
|
||||||
<div class="ui middle very relaxed page grid">
|
<div class="ui middle very relaxed page grid">
|
||||||
<div class="sixteen wide center aligned centered column">
|
<div class="sixteen wide center aligned centered column">
|
||||||
<h3 class="ui top attached header">
|
<h3 class="ui top attached header">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization new org">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization new org">
|
||||||
<div class="ui middle very relaxed page grid">
|
<div class="ui middle very relaxed page grid">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<form class="ui form" action="{{.Link}}" method="post">
|
<form class="ui form" action="{{.Link}}" method="post">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization profile">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization profile">
|
||||||
<div class="ui container df">
|
<div class="ui container df">
|
||||||
{{avatar .Org 140 "org-avatar"}}
|
{{avatar .Org 140 "org-avatar"}}
|
||||||
<div id="org-info">
|
<div id="org-info">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization members">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization members">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content repository packages">
|
<div role="main" aria-label="{{.Title}}" class="page-content repository packages">
|
||||||
{{template "user/overview/header" .}}
|
{{template "user/overview/header" .}}
|
||||||
{{template "projects/list" .}}
|
{{template "projects/list" .}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content repository packages">
|
<div role="main" aria-label="{{.Title}}" class="page-content repository packages">
|
||||||
{{template "user/overview/header" .}}
|
{{template "user/overview/header" .}}
|
||||||
{{template "projects/new" .}}
|
{{template "projects/new" .}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content repository packages">
|
<div role="main" aria-label="{{.Title}}" class="page-content repository packages">
|
||||||
{{template "user/overview/header" .}}
|
{{template "user/overview/header" .}}
|
||||||
{{template "projects/view" .}}
|
{{template "projects/view" .}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization settings options">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization settings options">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization settings options">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization settings options">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
|
|
||||||
{{template "user/settings/applications_oauth2_edit_form" .}}
|
{{template "user/settings/applications_oauth2_edit_form" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization settings delete">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization settings delete">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization settings new webhook">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization settings new webhook">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization settings webhooks">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization settings webhooks">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization settings labels">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization settings labels">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization settings options">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization settings options">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization settings packages">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization settings packages">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization settings packages">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization settings packages">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization settings packages admin">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization settings packages admin">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
|
@ -1,83 +1,15 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization settings webhooks">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization settings webhooks">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
{{template "org/settings/navbar" .}}
|
{{template "org/settings/navbar" .}}
|
||||||
<div class="ui twelve wide column content">
|
<div class="ui twelve wide column content">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
<h4 class="ui top attached header">
|
{{template "shared/secrets/add_list" .}}
|
||||||
{{.locale.Tr "secrets.secrets"}}
|
|
||||||
<div class="ui right">
|
|
||||||
<div class="ui primary tiny show-panel button" data-panel="#add-secret-panel">{{.locale.Tr "secrets.creation"}}</div>
|
|
||||||
</div>
|
|
||||||
</h4>
|
|
||||||
<div class="ui attached segment">
|
|
||||||
<div class="{{if not .HasError}}hide {{end}}mb-4" id="add-secret-panel">
|
|
||||||
<form class="ui form" action="{{.Link}}" method="post">
|
|
||||||
{{.CsrfTokenHtml}}
|
|
||||||
<div class="field">
|
|
||||||
{{.locale.Tr "secrets.description"}}
|
|
||||||
</div>
|
|
||||||
<div class="field{{if .Err_Title}} error{{end}}">
|
|
||||||
<label for="secret-title">{{.locale.Tr "secrets.name"}}</label>
|
|
||||||
<input id="secret-title" name="title" value="{{.title}}" autofocus required pattern="^[a-zA-Z_][a-zA-Z0-9_]*$" placeholder="{{.locale.Tr "secrets.creation.name_placeholder"}}">
|
|
||||||
</div>
|
|
||||||
<div class="field{{if .Err_Content}} error{{end}}">
|
|
||||||
<label for="secret-content">{{.locale.Tr "secrets.value"}}</label>
|
|
||||||
<textarea id="secret-content" name="content" required placeholder="{{.locale.Tr "secrets.creation.value_placeholder"}}">{{.content}}</textarea>
|
|
||||||
</div>
|
|
||||||
<button class="ui green button">
|
|
||||||
{{.locale.Tr "secrets.creation"}}
|
|
||||||
</button>
|
|
||||||
<button class="ui hide-panel button" data-panel="#add-secret-panel">
|
|
||||||
{{.locale.Tr "cancel"}}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{{if .Secrets}}
|
|
||||||
<div class="ui key list">
|
|
||||||
{{range .Secrets}}
|
|
||||||
<div class="item">
|
|
||||||
<div class="right floated content">
|
|
||||||
<button class="ui red tiny button delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
|
|
||||||
{{$.locale.Tr "settings.delete_key"}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="left floated content">
|
|
||||||
<i>{{svg "octicon-key" 32}}</i>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<strong>{{.Name}}</strong>
|
|
||||||
<div class="print meta">******</div>
|
|
||||||
<div class="activity meta">
|
|
||||||
<i>
|
|
||||||
{{$.locale.Tr "settings.add_on"}}
|
|
||||||
<span>{{.CreatedUnix.FormatShort}}</span>
|
|
||||||
</i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
{{.locale.Tr "secrets.none"}}
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui small basic delete modal">
|
|
||||||
<div class="ui header">
|
|
||||||
{{svg "octicon-trash" 16 "mr-2"}}
|
|
||||||
{{.locale.Tr "secrets.deletion"}}
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<p>{{.locale.Tr "secrets.deletion.description"}}</p>
|
|
||||||
</div>
|
|
||||||
{{template "base/delete_modal_actions" .}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{template "base/footer" .}}
|
{{template "base/footer" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization invite">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization invite">
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
<div class="ui centered card">
|
<div class="ui centered card">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization teams">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization teams">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization new team">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization new team">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization teams">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization teams">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content organization teams">
|
<div role="main" aria-label="{{.Title}}" class="page-content organization teams">
|
||||||
{{template "org/header" .}}
|
{{template "org/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
30
templates/package/content/conda.tmpl
Normal file
30
templates/package/content/conda.tmpl
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{{if eq .PackageDescriptor.Package.Type "conda"}}
|
||||||
|
<h4 class="ui top attached header">{{.locale.Tr "packages.installation"}}</h4>
|
||||||
|
<div class="ui attached segment">
|
||||||
|
<div class="ui form">
|
||||||
|
<div class="field">
|
||||||
|
<label>{{svg "octicon-code"}} {{.locale.Tr "packages.conda.registry" | Safe}}</label>
|
||||||
|
<div class="markup"><pre class="code-block"><code>channel_alias: {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/conda
|
||||||
|
channels:
|
||||||
|
  - {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/conda
|
||||||
|
default_channels:
|
||||||
|
  - {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/conda</code></pre></div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.conda.install"}}</label>
|
||||||
|
{{$channel := .PackageDescriptor.PackageProperties.GetByName "conda.channel"}}
|
||||||
|
<div class="markup"><pre class="code-block"><code>conda install{{if $channel}} -c {{$channel}}{{end}} {{.PackageDescriptor.PackageProperties.GetByName "conda.name"}}={{.PackageDescriptor.Version.Version}}</code></pre></div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>{{.locale.Tr "packages.conda.documentation" | Safe}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Summary}}
|
||||||
|
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
|
||||||
|
<div class="ui attached segment">
|
||||||
|
{{if .PackageDescriptor.Metadata.Description}}{{.PackageDescriptor.Metadata.Description}}{{else}}{{.PackageDescriptor.Metadata.Summary}}{{end}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
6
templates/package/metadata/conda.tmpl
Normal file
6
templates/package/metadata/conda.tmpl
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{{if eq .PackageDescriptor.Package.Type "conda"}}
|
||||||
|
{{if .PackageDescriptor.Metadata.License}}<div class="item">{{svg "octicon-law" 16 "mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}}
|
||||||
|
{{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.details.project_site"}}</a></div>{{end}}
|
||||||
|
{{if .PackageDescriptor.Metadata.RepositoryURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.RepositoryURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.conda.details.repository_site"}}</a></div>{{end}}
|
||||||
|
{{if .PackageDescriptor.Metadata.DocumentationURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.DocumentationURL}}" target="_blank" rel="noopener noreferrer me">{{.locale.Tr "packages.conda.details.documentation_site"}}</a></div>{{end}}
|
||||||
|
{{end}}
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content repository settings options">
|
<div role="main" aria-label="{{.Title}}" class="page-content repository settings options">
|
||||||
{{template "user/overview/header" .}}
|
{{template "user/overview/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content repository view issue packages">
|
<div role="main" aria-label="{{.Title}}" class="page-content repository view issue packages">
|
||||||
{{template "user/overview/header" .}}
|
{{template "user/overview/header" .}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div>
|
<div>
|
||||||
@ -21,6 +21,7 @@
|
|||||||
<div class="twelve wide column">
|
<div class="twelve wide column">
|
||||||
{{template "package/content/composer" .}}
|
{{template "package/content/composer" .}}
|
||||||
{{template "package/content/conan" .}}
|
{{template "package/content/conan" .}}
|
||||||
|
{{template "package/content/conda" .}}
|
||||||
{{template "package/content/container" .}}
|
{{template "package/content/container" .}}
|
||||||
{{template "package/content/generic" .}}
|
{{template "package/content/generic" .}}
|
||||||
{{template "package/content/helm" .}}
|
{{template "package/content/helm" .}}
|
||||||
@ -44,6 +45,7 @@
|
|||||||
<div class="item">{{svg "octicon-download" 16 "mr-3"}} {{.PackageDescriptor.Version.DownloadCount}}</div>
|
<div class="item">{{svg "octicon-download" 16 "mr-3"}} {{.PackageDescriptor.Version.DownloadCount}}</div>
|
||||||
{{template "package/metadata/composer" .}}
|
{{template "package/metadata/composer" .}}
|
||||||
{{template "package/metadata/conan" .}}
|
{{template "package/metadata/conan" .}}
|
||||||
|
{{template "package/metadata/conda" .}}
|
||||||
{{template "package/metadata/container" .}}
|
{{template "package/metadata/container" .}}
|
||||||
{{template "package/metadata/generic" .}}
|
{{template "package/metadata/generic" .}}
|
||||||
{{template "package/metadata/helm" .}}
|
{{template "package/metadata/helm" .}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content install">
|
<div role="main" aria-label="{{.Title}}" class="page-content install">
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
<div class="sixteen wide column content">
|
<div class="sixteen wide column content">
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user