Compare commits

...

12 Commits

Author SHA1 Message Date
crystal
5d9c64b3fe
Fix line spacing for plaintext previews (#22699)
Adding `<br>` between each line is not necessary since the entire file
is rendered inside a `<pre>`

fixes https://codeberg.org/Codeberg/Community/issues/915
2023-02-01 22:51:02 -06:00
Lunny Xiao
4e946e5a7d
Small refactor for loading PRs (#22652) 2023-02-01 21:49:28 -06:00
Lukas
3f2e721372
Allow setting access token scope by CLI (#22648)
Followup for #20908 to allow setting the scopes when creating new access
token via CLI.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: John Olheiser <john.olheiser@gmail.com>
2023-02-02 11:10:37 +08:00
Felipe Leopoldo Sologuren Gutiérrez
15c035775a
Add main landmark to templates and adjust titles (#22670)
* Add main aria landmark to templates
 * Adjust some titles to improve understanding of location in navigation

Contributed by @Forgejo
2023-02-01 22:56:10 +00:00
Brecht Van Lommel
1e0e79dcbf
Fix cache-control header clearing comment text when editing issue (#22604)
The `no-store` cache control added in #20432 is causing form input to be
cleared unnecessarily on page reload. Instead use
`max-age=0,private,must-revalidate` which avoids this.

This was particularly a problem when typing a long comment for an issue
and then for example changing the label. The page would be reloaded and
lose the unsubmitted comment.

Fixes #22603
2023-02-01 15:28:06 -06:00
delvh
b6b8feb3de
Enable @<user>- completion popup on the release description textarea (#22359)
For some unknown reason, this was previously disabled.
Additionally removed an unused return value.
2023-02-01 13:14:40 -06:00
KN4CK3R
6ba9ff7b48
Add Conda package registry (#22262)
This PR adds a [Conda](https://conda.io/) package registry.
2023-02-01 12:30:39 -06:00
KN4CK3R
5882e179a9
Add user secrets (#22191)
Fixes #22183
Replaces #22187

This PR adds secrets for users. I refactored the files for organizations
and repos to use the same logic and templates. I splitted the secrets
from deploy keys again and reverted the fix from #22187.

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-02-01 20:53:04 +08:00
zeripath
9f9a1ce922
Add missing close bracket in imagediff (#22710)
There was a missing `]` in imagediff.js:

```
const $range = $container.find("input[type='range'"); 
```

This PR simply adds this.

Fix #22702
2023-02-01 19:48:35 +08:00
yp05327
72a83dcc82
Explain that the no-access team unit does not affect public repositories (#22661)
Fixes https://github.com/go-gitea/gitea/issues/22600

Add explanations to team unit access control.

---------

Co-authored-by: Jason Song <i@wolfogre.com>
2023-02-01 16:14:40 +08:00
zeripath
19d5b2f922
Fix bugs with WebAuthn preventing sign in and registration. (#22651)
This PR fixes two bugs with Webauthn support:

* There was a longstanding bug within webauthn due to the backend using
URLEncodedBase64 but the javascript using decoding using plain base64.
This causes intermittent issues with users reporting decoding errors.
* Following the recent upgrade to webauthn there was a change in the way
the library expects RPOrigins to be configured. This leads to the
Relying Party Origin not being configured and prevents registration.

Fix #22507

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2023-02-01 07:24:10 +00:00
Lunny Xiao
2871ea0809
Add more events details supports for actions (#22680)
#21937 implemented only basic events based on name because of `act`'s
limitation. So I sent a PR to parse all possible events details in
https://gitea.com/gitea/act/pulls/11 and it merged. The ref
documentation is
https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows

This PR depends on that and make more detail responses for `push` events
and `pull_request` events. And it lefts more events there for future
PRs.

---------

Co-authored-by: Jason Song <i@wolfogre.com>
2023-02-01 13:32:46 +08:00
221 changed files with 1948 additions and 476 deletions

View File

@ -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 {

View File

@ -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`)

View File

@ -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`)

View 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) |

View File

@ -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` |

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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
} }

View 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)
}

View File

@ -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:

View File

@ -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:

View File

@ -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
}

View File

@ -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",
}, },

View File

@ -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)
} }

View File

@ -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
} }

View File

@ -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)

View 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)
}

View 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)
})
}

View File

@ -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")

View File

@ -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.

View 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

View File

@ -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)

View 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(),
})
}

View File

@ -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

View File

@ -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

View File

@ -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",
})
}

View 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",
)
}

View File

@ -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)
} }

View File

@ -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 {

View File

@ -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

View 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",
)
}

View File

@ -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)

View 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,
})
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View 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",
)
}

View File

@ -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") != "" {

View File

@ -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() {

View File

@ -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)
} }

View File

@ -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)"`

View File

@ -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:

View File

@ -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)
} }
} }

View File

@ -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">

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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" .}}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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" .}}

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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" .}}

View File

@ -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">

View File

@ -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" .}}

View File

@ -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">

View File

@ -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" .}}

View File

@ -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" .}}

View 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:
&#32;&#32;- {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/conda
default_channels:
&#32;&#32;- {{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}}

View 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}}

View File

@ -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" .}}

View File

@ -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" .}}

View File

@ -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