Compare commits

..

12 Commits

Author SHA1 Message Date
LeenHawk
b942838bd4
Update localization.zh-cn.md (#23448)
As title.
2023-03-14 11:38:20 +08:00
delvh
81fe5d6185
Convert <div class="button"> to <button class="button"> (#23337)
This improves a lot of accessibility shortcomings.
Every possible instance of `<div class="button">` matching the command
`ag '<[^ab].*?class=.*?[" ]button[ "]' templates/ | grep -v 'dropdown'`
has been converted when possible.
divs with the `dropdown` class and their children were omitted as
1. more analysis must be conducted whether the dropdowns still work as
intended when they are a `button` instead of a `div`.
2. most dropdowns have `div`s as children. The HTML standard disallows
`div`s inside `button`s.
3. When a dropdown child that's part of the displayed text content is
converted to a `button`, the dropdown can be focused twice

Further changes include that all "gitea-managed" buttons with JS code
received an `e.preventDefault()` so that they don't accidentally submit
an underlying form, which would execute instead of cancel the action.
Lastly, some minor issues were fixed as well during the refactoring.

## Future improvements

As mentioned in
https://github.com/go-gitea/gitea/pull/23337#discussion_r1127277391,
`<a>`s without `href` attribute are not focusable.
They should later on be converted to `<button>`s.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-03-14 11:34:09 +08:00
GiteaBot
605fd15ad6 [skip ci] Updated translations via Crowdin 2023-03-14 00:16:09 +00:00
KN4CK3R
8570593d55
Add package registry architecture overview (#23445)
As announced in #22810 I added a readme file to help understanding how
the package registry archictecture works and how the go packages are
related.
2023-03-13 18:15:09 -04:00
Zettat123
8421b8264f
Handle missing README in create repos API (#23387)
Close #22934 

In `/user/repos` API (and other APIs related to creating repos), user
can specify a readme template for auto init. At present, if the
specified template does not exist, a `500` will be returned . This PR
improved the logic and will return a `400` instead of `500`.
2023-03-13 16:55:30 -05:00
sillyguodong
5eea61dbc8
Fix missing commit status in PR which from forked repo (#23351)
close: #23347

### Reference and Inference
According to Github REST API
[doc](https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#list-commit-statuses-for-a-reference):
1. The `Drone CI` that can create some commit status by
[API](https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#create-a-commit-status)
is enabled in `go-gitea/gitea`. So I tried to call the API to get a
commit status list of a PR which is commited to upstream
repo(`go-gitea/gitea`). As a result, the API returned a array of commit
status.

![image](https://user-images.githubusercontent.com/33891828/223913371-313d047a-5e2e-484c-b13e-dcd38748703e.png)
2. Then I tried to call the API to get commit status list of the
reference which of the `SHA` is the same as step 1 in the repo which is
forked from `go-gitea/gitea`. But I got a empty array.

![image](https://user-images.githubusercontent.com/33891828/223930827-17a64d3c-f466-4980-897c-77fe386c4d3b.png)

So, I believe it that:
1. The commit status is not shared between upstream repo and forked
repo.
2. The coomit status is bound to a repo that performs actions. (Gitea's
logic is the same)

### Cause
During debugging, I found it that commit status are not stored in the DB
as expected.
So, I located the following code:

8cadd51bf2/services/actions/commit_status.go (L18-L26)
When I create a PR, the type of `event` is `pull request`, not `push`.
So the code return function directly.

### Screenshot

![image](https://user-images.githubusercontent.com/33891828/223939339-dadf539c-1fdd-40c4-96e9-2e4fa733f531.png)

![image](https://user-images.githubusercontent.com/33891828/223939519-edb02bf0-2478-4ea5-9366-be85468f02db.png)

![image](https://user-images.githubusercontent.com/33891828/223939557-ec6f1375-5536-400e-8987-fb7d2fd452fa.png)



### Other 
In this PR, I also fix the problem of missing icon which represents
running in PRs list.

![image](https://user-images.githubusercontent.com/33891828/223939898-2a0339e4-713f-4c7b-9d99-2250a43f3457.png)

![image](https://user-images.githubusercontent.com/33891828/223939979-037a975f-5ced-480c-bac7-0ee00ebfff4b.png)
2023-03-13 16:05:19 -05:00
KN4CK3R
c709fa17a7
Add Swift package registry (#22404)
This PR adds a [Swift](https://www.swift.org/) package registry.


![grafik](https://user-images.githubusercontent.com/1666336/211842523-07521cbd-8fb6-400f-820c-ee8048b05ae8.png)
2023-03-13 15:28:39 -05:00
John Olheiser
0a6f6354bb
Purge API comment (#23451)
This PR just adds the `purge` query parameter to the swagger docs for
admin user delete.

Signed-off-by: jolheiser <john.olheiser@gmail.com>
2023-03-13 14:41:38 -05:00
yp05327
d74a7efb60
Use context for RepositoryList.LoadAttributes (#23435) 2023-03-13 11:31:41 +00:00
FuXiaoHei
cdc9e91750
add path prefix to ObjectStorage.Iterator (#23332)
Support to iterator subdirectory in ObjectStorage for
ObjectStorage.Iterator method.

It's required for https://github.com/go-gitea/gitea/pull/22738 to make
artifact files cleanable.

---------

Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-03-13 18:23:51 +08:00
Philip Peterson
757b4c17e9
Support reflogs (#22451)
This PR adds support for reflogs on all repositories. It does this by
adding a global configuration entry.

Implements #14865

---------

Signed-off-by: Philip Peterson <philip.c.peterson@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-03-13 15:51:07 +08:00
Hester Gong
f521e88240
Fix actions frontend bugs (pagination, long name alignment) and small simplify (#23370)
1 Right now on actions page, the action list will not be aligned if
commit message is long. In this PR, the changes are:
- The branch tag is moved to bottom row
- Width percentage is given to make them aligned
- Show "..." if commit is longer than two lines.
- Align the status icon with the commit message with baseline

 Before:
<img width="1068" alt="截屏2023-03-08 12 23 22"
src="https://user-images.githubusercontent.com/17645053/223628534-6b9472cb-29f5-40a3-9714-c5152553049e.png">
  
 After:
<img width="756" alt="截屏2023-03-08 13 34 28"
src="https://user-images.githubusercontent.com/17645053/223628571-da94698b-0e0a-43e3-ae82-34d8c780e5ba.png">


2 Right now the actions list's pagination is not working properly
because Param is not passed to pagination template, in this PR Param
Strings are passed to the pager

Before:
<img width="1176" alt="截屏2023-03-08 12 23 50"
src="https://user-images.githubusercontent.com/17645053/223629207-8b67ce74-2342-4259-bc81-036e37752716.png">

After:
<img width="1343" alt="截屏2023-03-08 13 11 54"
src="https://user-images.githubusercontent.com/17645053/223629321-4f538f8a-45dc-4d6f-ae60-2c82680ae3e7.png">

3 A small simplify in `RepoActionView.vue` .

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-03-13 13:31:06 +08:00
108 changed files with 1913 additions and 347 deletions

View File

@ -250,7 +250,7 @@ func runDump(ctx *cli.Context) error {
if ctx.IsSet("skip-lfs-data") && ctx.Bool("skip-lfs-data") { if ctx.IsSet("skip-lfs-data") && ctx.Bool("skip-lfs-data") {
log.Info("Skip dumping LFS data") log.Info("Skip dumping LFS data")
} else if err := storage.LFS.IterateObjects(func(objPath string, object storage.Object) error { } else if err := storage.LFS.IterateObjects("", func(objPath string, object storage.Object) error {
info, err := object.Stat() info, err := object.Stat()
if err != nil { if err != nil {
return err return err
@ -351,7 +351,7 @@ func runDump(ctx *cli.Context) error {
if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") { if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") {
log.Info("Skip dumping attachment data") log.Info("Skip dumping attachment data")
} else if err := storage.Attachments.IterateObjects(func(objPath string, object storage.Object) error { } else if err := storage.Attachments.IterateObjects("", func(objPath string, object storage.Object) error {
info, err := object.Stat() info, err := object.Stat()
if err != nil { if err != nil {
return err return err
@ -364,7 +364,7 @@ func runDump(ctx *cli.Context) error {
if ctx.IsSet("skip-package-data") && ctx.Bool("skip-package-data") { if ctx.IsSet("skip-package-data") && ctx.Bool("skip-package-data") {
log.Info("Skip dumping package data") log.Info("Skip dumping package data")
} else if err := storage.Packages.IterateObjects(func(objPath string, object storage.Object) error { } else if err := storage.Packages.IterateObjects("", func(objPath string, object storage.Object) error {
info, err := object.Stat() info, err := object.Stat()
if err != nil { if err != nil {
return err return err

View File

@ -2272,6 +2272,17 @@ ROUTER = console
;PULL = 300 ;PULL = 300
;GC = 60 ;GC = 60
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Git Reflog timeout in days
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[git.reflog]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;ENABLED = true
;EXPIRATION = 90
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[mirror] ;[mirror]
@ -2505,6 +2516,8 @@ ROUTER = console
;LIMIT_SIZE_PYPI = -1 ;LIMIT_SIZE_PYPI = -1
;; Maximum size of a RubyGems upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`) ;; Maximum size of a RubyGems upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
;LIMIT_SIZE_RUBYGEMS = -1 ;LIMIT_SIZE_RUBYGEMS = -1
;; Maximum size of a Swift upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
;LIMIT_SIZE_SWIFT = -1
;; Maximum size of a Vagrant upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`) ;; Maximum size of a Vagrant upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
;LIMIT_SIZE_VAGRANT = -1 ;LIMIT_SIZE_VAGRANT = -1

View File

@ -1093,6 +1093,11 @@ Default templates for project boards:
- `DISABLE_CORE_PROTECT_NTFS`: **false** Set to true to forcibly set `core.protectNTFS` to false. - `DISABLE_CORE_PROTECT_NTFS`: **false** Set to true to forcibly set `core.protectNTFS` to false.
- `DISABLE_PARTIAL_CLONE`: **false** Disable the usage of using partial clones for git. - `DISABLE_PARTIAL_CLONE`: **false** Disable the usage of using partial clones for git.
## Git - Reflog settings (`git.reflog`)
- `ENABLED`: **true** Set to true to enable Git to write changes to reflogs in each repo.
- `EXPIRATION`: **90** Reflog entry lifetime, in days. Entries are removed opportunistically by Git.
## Git - Timeout settings (`git.timeout`) ## Git - Timeout settings (`git.timeout`)
- `DEFAULT`: **360**: Git operations default timeout seconds. - `DEFAULT`: **360**: Git operations default timeout seconds.
@ -1249,6 +1254,7 @@ Task queue configuration has been moved to `queue.task`. However, the below conf
- `LIMIT_SIZE_PUB`: **-1**: Maximum size of a Pub upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`) - `LIMIT_SIZE_PUB`: **-1**: Maximum size of a Pub upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_PYPI`: **-1**: Maximum size of a PyPI upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`) - `LIMIT_SIZE_PYPI`: **-1**: Maximum size of a PyPI upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_RUBYGEMS`: **-1**: Maximum size of a RubyGems upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`) - `LIMIT_SIZE_RUBYGEMS`: **-1**: Maximum size of a RubyGems upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_SWIFT`: **-1**: Maximum size of a Swift upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
- `LIMIT_SIZE_VAGRANT`: **-1**: Maximum size of a Vagrant upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`) - `LIMIT_SIZE_VAGRANT`: **-1**: Maximum size of a Vagrant upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
## Mirror (`mirror`) ## Mirror (`mirror`)

View File

@ -14,5 +14,17 @@ menu:
--- ---
# 本地化 # 本地化
Gitea的本地化是通过我们的[Crowdin项目](https://crowdin.com/project/gitea)进行的。
## TBD 对于对**英语翻译**的更改可以发出pull-request来更改[英语语言环境](https://github.com/go-gitea/gitea/blob/master/options/locale/locale_en-US.ini)中合适的关键字。
有关对**非英语**翻译的更改,请参阅上面的 Crowdin 项目。
## 支持的语言
上述 Crowdin 项目中列出的任何语言一旦翻译了 25% 或更多都将得到支持。
翻译被接受后,它将在下一次 Crowdin 同步后反映在主存储库中,这通常是在任何 PR 合并之后。
在撰写本文时,这意味着更改后的翻译可能要到 Gitea 的下一个版本才会出现。
如果使用开发版本,则在同步更改内容后,它应该会在更新后立即显示。

View File

@ -40,6 +40,7 @@ The following package managers are currently supported:
| [Pub]({{< relref "doc/packages/pub.en-us.md" >}}) | Dart | `dart`, `flutter` | | [Pub]({{< relref "doc/packages/pub.en-us.md" >}}) | Dart | `dart`, `flutter` |
| [PyPI]({{< relref "doc/packages/pypi.en-us.md" >}}) | Python | `pip`, `twine` | | [PyPI]({{< relref "doc/packages/pypi.en-us.md" >}}) | Python | `pip`, `twine` |
| [RubyGems]({{< relref "doc/packages/rubygems.en-us.md" >}}) | Ruby | `gem`, `Bundler` | | [RubyGems]({{< relref "doc/packages/rubygems.en-us.md" >}}) | Ruby | `gem`, `Bundler` |
| [Swift]({{< relref "doc/packages/rubygems.en-us.md" >}}) | Swift | `swift` |
| [Vagrant]({{< relref "doc/packages/vagrant.en-us.md" >}}) | - | `vagrant` | | [Vagrant]({{< relref "doc/packages/vagrant.en-us.md" >}}) | - | `vagrant` |
**The following paragraphs only apply if Packages are not globally disabled!** **The following paragraphs only apply if Packages are not globally disabled!**

View File

@ -0,0 +1,93 @@
---
date: "2023-01-10T00:00:00+00:00"
title: "Swift Packages Repository"
slug: "packages/swift"
draft: false
toc: false
menu:
sidebar:
parent: "packages"
name: "Swift"
weight: 95
identifier: "swift"
---
# Swift Packages Repository
Publish [Swift](hhttps://www.swift.org/) packages for your user or organization.
**Table of Contents**
{{< toc >}}
## Requirements
To work with the Swift package registry, you need to use [swift](https://www.swift.org/getting-started/) to consume and a HTTP client (like `curl`) to publish packages.
## Configuring the package registry
To register the package registry and provide credentials, execute:
```shell
swift package-registry set https://gitea.example.com/api/packages/{owner}/swift -login {username} -password {password}
```
| Placeholder | Description |
| ------------ | ----------- |
| `owner` | The owner of the package. |
| `username` | Your Gitea username. |
| `password` | Your Gitea password. If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}) instead of the password. |
The login is optional and only needed if the package registry is private.
## Publish a package
First you have to pack the contents of your package:
```shell
swift package archive-source
```
To publish the package perform a HTTP PUT request with the package content in the request body.
```shell --user your_username:your_password_or_token \
curl -X PUT --user {username}:{password} \
-H "Accept: application/vnd.swift.registry.v1+json" \
-F source-archive=@/path/to/package.zip \
-F metadata={metadata} \
https://gitea.example.com/api/packages/{owner}/swift/{scope}/{name}/{version}
```
| Placeholder | Description |
| ----------- | ----------- |
| `username` | Your Gitea username. |
| `password` | Your Gitea password. If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}) instead of the password. |
| `owner` | The owner of the package. |
| `scope` | The package scope. |
| `name` | The package name. |
| `version` | The package version. |
| `metadata` | (Optional) The metadata of the package. JSON encoded subset of https://schema.org/SoftwareSourceCode |
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 Swift package from the package registry, add it in the `Package.swift` file dependencies list:
```
dependencies: [
.package(id: "{scope}.{name}", from:"{version}")
]
```
| Parameter | Description |
| ----------- | ----------- |
| `scope` | The package scope. |
| `name` | The package name. |
| `version` | The package version. |
Afterwards execute the following command to install it:
```shell
swift package resolve
```

View File

@ -128,6 +128,17 @@ func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
return nil, fmt.Errorf("event %s is not a push event", run.Event) return nil, fmt.Errorf("event %s is not a push event", run.Event)
} }
func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, error) {
if run.Event == webhook_module.HookEventPullRequest {
var payload api.PullRequestPayload
if err := json.Unmarshal([]byte(run.EventPayload), &payload); err != nil {
return nil, err
}
return &payload, nil
}
return nil, fmt.Errorf("event %s is not a pull request event", run.Event)
}
func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error { func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error {
_, err := db.GetEngine(ctx).ID(repo.ID). _, err := db.GetEngine(ctx).ID(repo.ID).
SetExpr("num_action_runs", SetExpr("num_action_runs",

View File

@ -24,6 +24,7 @@ import (
"code.gitea.io/gitea/modules/packages/pub" "code.gitea.io/gitea/modules/packages/pub"
"code.gitea.io/gitea/modules/packages/pypi" "code.gitea.io/gitea/modules/packages/pypi"
"code.gitea.io/gitea/modules/packages/rubygems" "code.gitea.io/gitea/modules/packages/rubygems"
"code.gitea.io/gitea/modules/packages/swift"
"code.gitea.io/gitea/modules/packages/vagrant" "code.gitea.io/gitea/modules/packages/vagrant"
"github.com/hashicorp/go-version" "github.com/hashicorp/go-version"
@ -159,6 +160,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
metadata = &pypi.Metadata{} metadata = &pypi.Metadata{}
case TypeRubyGems: case TypeRubyGems:
metadata = &rubygems.Metadata{} metadata = &rubygems.Metadata{}
case TypeSwift:
metadata = &swift.Metadata{}
case TypeVagrant: case TypeVagrant:
metadata = &vagrant.Metadata{} metadata = &vagrant.Metadata{}
default: default:

View File

@ -44,6 +44,7 @@ const (
TypePub Type = "pub" TypePub Type = "pub"
TypePyPI Type = "pypi" TypePyPI Type = "pypi"
TypeRubyGems Type = "rubygems" TypeRubyGems Type = "rubygems"
TypeSwift Type = "swift"
TypeVagrant Type = "vagrant" TypeVagrant Type = "vagrant"
) )
@ -62,6 +63,7 @@ var TypeList = []Type{
TypePub, TypePub,
TypePyPI, TypePyPI,
TypeRubyGems, TypeRubyGems,
TypeSwift,
TypeVagrant, TypeVagrant,
} }
@ -96,6 +98,8 @@ func (pt Type) Name() string {
return "PyPI" return "PyPI"
case TypeRubyGems: case TypeRubyGems:
return "RubyGems" return "RubyGems"
case TypeSwift:
return "Swift"
case TypeVagrant: case TypeVagrant:
return "Vagrant" return "Vagrant"
} }
@ -133,6 +137,8 @@ func (pt Type) SVGName() string {
return "gitea-python" return "gitea-python"
case TypeRubyGems: case TypeRubyGems:
return "gitea-rubygems" return "gitea-rubygems"
case TypeSwift:
return "gitea-swift"
case TypeVagrant: case TypeVagrant:
return "gitea-vagrant" return "gitea-vagrant"
} }

View File

@ -62,7 +62,8 @@ func RepositoryListOfMap(repoMap map[int64]*Repository) RepositoryList {
return RepositoryList(ValuesRepository(repoMap)) return RepositoryList(ValuesRepository(repoMap))
} }
func (repos RepositoryList) loadAttributes(ctx context.Context) error { // LoadAttributes loads the attributes for the given RepositoryList
func (repos RepositoryList) LoadAttributes(ctx context.Context) error {
if len(repos) == 0 { if len(repos) == 0 {
return nil return nil
} }
@ -107,11 +108,6 @@ func (repos RepositoryList) loadAttributes(ctx context.Context) error {
return nil return nil
} }
// LoadAttributes loads the attributes for the given RepositoryList
func (repos RepositoryList) LoadAttributes() error {
return repos.loadAttributes(db.DefaultContext)
}
// SearchRepoOptions holds the search options // SearchRepoOptions holds the search options
type SearchRepoOptions struct { type SearchRepoOptions struct {
db.ListOptions db.ListOptions
@ -547,7 +543,7 @@ func SearchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, c
} }
if loadAttributes { if loadAttributes {
if err := repos.loadAttributes(ctx); err != nil { if err := repos.LoadAttributes(ctx); err != nil {
return nil, 0, fmt.Errorf("LoadAttributes: %w", err) return nil, 0, fmt.Errorf("LoadAttributes: %w", err)
} }
} }

View File

@ -31,7 +31,7 @@ func commonCheckStorage(ctx context.Context, logger log.Logger, autofix bool, op
totalSize, orphanedSize := int64(0), int64(0) totalSize, orphanedSize := int64(0), int64(0)
var pathsToDelete []string var pathsToDelete []string
if err := opts.storer.IterateObjects(func(p string, obj storage.Object) error { if err := opts.storer.IterateObjects("", func(p string, obj storage.Object) error {
defer obj.Close() defer obj.Close()
totalCount++ totalCount++

View File

@ -201,6 +201,23 @@ func InitFull(ctx context.Context) (err error) {
return syncGitConfig() return syncGitConfig()
} }
func enableReflogs() error {
if err := configSet("core.logAllRefUpdates", "true"); err != nil {
return err
}
err := configSet("gc.reflogExpire", fmt.Sprintf("%d", setting.Git.Reflog.Expiration))
return err
}
func disableReflogs() error {
if err := configUnsetAll("core.logAllRefUpdates", "true"); err != nil {
return err
} else if err := configUnsetAll("gc.reflogExpire", ""); err != nil {
return err
}
return nil
}
// syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem) // syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem)
func syncGitConfig() (err error) { func syncGitConfig() (err error) {
if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil { if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil {
@ -224,6 +241,16 @@ func syncGitConfig() (err error) {
return err return err
} }
if setting.Git.Reflog.Enabled {
if err := enableReflogs(); err != nil {
return err
}
} else {
if err := disableReflogs(); err != nil {
return err
}
}
if CheckGitVersionAtLeast("2.10") == nil { if CheckGitVersionAtLeast("2.10") == nil {
if err := configSet("receive.advertisePushOptions", "true"); err != nil { if err := configSet("receive.advertisePushOptions", "true"); err != nil {
return err return err

View File

@ -0,0 +1,214 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package swift
import (
"archive/zip"
"fmt"
"io"
"path"
"regexp"
"strings"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation"
"github.com/hashicorp/go-version"
)
var (
ErrMissingManifestFile = util.NewInvalidArgumentErrorf("Package.swift file is missing")
ErrManifestFileTooLarge = util.NewInvalidArgumentErrorf("Package.swift file is too large")
ErrInvalidManifestVersion = util.NewInvalidArgumentErrorf("manifest version is invalid")
manifestPattern = regexp.MustCompile(`\APackage(?:@swift-(\d+(?:\.\d+)?(?:\.\d+)?))?\.swift\z`)
toolsVersionPattern = regexp.MustCompile(`\A// swift-tools-version:(\d+(?:\.\d+)?(?:\.\d+)?)`)
)
const (
maxManifestFileSize = 128 * 1024
PropertyScope = "swift.scope"
PropertyName = "swift.name"
PropertyRepositoryURL = "swift.repository_url"
)
// Package represents a Swift package
type Package struct {
RepositoryURLs []string
Metadata *Metadata
}
// Metadata represents the metadata of a Swift package
type Metadata struct {
Description string `json:"description,omitempty"`
Keywords []string `json:"keywords,omitempty"`
RepositoryURL string `json:"repository_url,omitempty"`
License string `json:"license,omitempty"`
Author Person `json:"author,omitempty"`
Manifests map[string]*Manifest `json:"manifests,omitempty"`
}
// Manifest represents a Package.swift file
type Manifest struct {
Content string `json:"content"`
ToolsVersion string `json:"tools_version,omitempty"`
}
// https://schema.org/SoftwareSourceCode
type SoftwareSourceCode struct {
Context []string `json:"@context"`
Type string `json:"@type"`
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description,omitempty"`
Keywords []string `json:"keywords,omitempty"`
CodeRepository string `json:"codeRepository,omitempty"`
License string `json:"license,omitempty"`
Author Person `json:"author"`
ProgrammingLanguage ProgrammingLanguage `json:"programmingLanguage"`
RepositoryURLs []string `json:"repositoryURLs,omitempty"`
}
// https://schema.org/ProgrammingLanguage
type ProgrammingLanguage struct {
Type string `json:"@type"`
Name string `json:"name"`
URL string `json:"url"`
}
// https://schema.org/Person
type Person struct {
Type string `json:"@type,omitempty"`
GivenName string `json:"givenName,omitempty"`
MiddleName string `json:"middleName,omitempty"`
FamilyName string `json:"familyName,omitempty"`
}
func (p Person) String() string {
var sb strings.Builder
if p.GivenName != "" {
sb.WriteString(p.GivenName)
}
if p.MiddleName != "" {
if sb.Len() > 0 {
sb.WriteRune(' ')
}
sb.WriteString(p.MiddleName)
}
if p.FamilyName != "" {
if sb.Len() > 0 {
sb.WriteRune(' ')
}
sb.WriteString(p.FamilyName)
}
return sb.String()
}
// ParsePackage parses the Swift package upload
func ParsePackage(sr io.ReaderAt, size int64, mr io.Reader) (*Package, error) {
zr, err := zip.NewReader(sr, size)
if err != nil {
return nil, err
}
p := &Package{
Metadata: &Metadata{
Manifests: make(map[string]*Manifest),
},
}
for _, file := range zr.File {
manifestMatch := manifestPattern.FindStringSubmatch(path.Base(file.Name))
if len(manifestMatch) == 0 {
continue
}
if file.UncompressedSize64 > maxManifestFileSize {
return nil, ErrManifestFileTooLarge
}
f, err := zr.Open(file.Name)
if err != nil {
return nil, err
}
content, err := io.ReadAll(f)
if err := f.Close(); err != nil {
return nil, err
}
if err != nil {
return nil, err
}
swiftVersion := ""
if len(manifestMatch) == 2 && manifestMatch[1] != "" {
v, err := version.NewSemver(manifestMatch[1])
if err != nil {
return nil, ErrInvalidManifestVersion
}
swiftVersion = TrimmedVersionString(v)
}
manifest := &Manifest{
Content: string(content),
}
toolsMatch := toolsVersionPattern.FindStringSubmatch(manifest.Content)
if len(toolsMatch) == 2 {
v, err := version.NewSemver(toolsMatch[1])
if err != nil {
return nil, ErrInvalidManifestVersion
}
manifest.ToolsVersion = TrimmedVersionString(v)
}
p.Metadata.Manifests[swiftVersion] = manifest
}
if _, found := p.Metadata.Manifests[""]; !found {
return nil, ErrMissingManifestFile
}
if mr != nil {
var ssc *SoftwareSourceCode
if err := json.NewDecoder(mr).Decode(&ssc); err != nil {
return nil, err
}
p.Metadata.Description = ssc.Description
p.Metadata.Keywords = ssc.Keywords
p.Metadata.License = ssc.License
p.Metadata.Author = Person{
GivenName: ssc.Author.GivenName,
MiddleName: ssc.Author.MiddleName,
FamilyName: ssc.Author.FamilyName,
}
p.Metadata.RepositoryURL = ssc.CodeRepository
if !validation.IsValidURL(p.Metadata.RepositoryURL) {
p.Metadata.RepositoryURL = ""
}
p.RepositoryURLs = ssc.RepositoryURLs
}
return p, nil
}
// TrimmedVersionString returns the version string without the patch segment if it is zero
func TrimmedVersionString(v *version.Version) string {
segments := v.Segments64()
var b strings.Builder
fmt.Fprintf(&b, "%d.%d", segments[0], segments[1])
if segments[2] != 0 {
fmt.Fprintf(&b, ".%d", segments[2])
}
return b.String()
}

View File

@ -0,0 +1,144 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package swift
import (
"archive/zip"
"bytes"
"strings"
"testing"
"github.com/hashicorp/go-version"
"github.com/stretchr/testify/assert"
)
const (
packageName = "gitea"
packageVersion = "1.0.1"
packageDescription = "Package Description"
packageRepositoryURL = "https://gitea.io/gitea/gitea"
packageAuthor = "KN4CK3R"
packageLicense = "MIT"
)
func TestParsePackage(t *testing.T) {
createArchive := func(files map[string][]byte) *bytes.Reader {
var buf bytes.Buffer
zw := zip.NewWriter(&buf)
for filename, content := range files {
w, _ := zw.Create(filename)
w.Write(content)
}
zw.Close()
return bytes.NewReader(buf.Bytes())
}
t.Run("MissingManifestFile", func(t *testing.T) {
data := createArchive(map[string][]byte{"dummy.txt": {}})
p, err := ParsePackage(data, data.Size(), nil)
assert.Nil(t, p)
assert.ErrorIs(t, err, ErrMissingManifestFile)
})
t.Run("ManifestFileTooLarge", func(t *testing.T) {
data := createArchive(map[string][]byte{
"Package.swift": make([]byte, maxManifestFileSize+1),
})
p, err := ParsePackage(data, data.Size(), nil)
assert.Nil(t, p)
assert.ErrorIs(t, err, ErrManifestFileTooLarge)
})
t.Run("WithoutMetadata", func(t *testing.T) {
content1 := "// swift-tools-version:5.7\n//\n// Package.swift"
content2 := "// swift-tools-version:5.6\n//\n// Package@swift-5.6.swift"
data := createArchive(map[string][]byte{
"Package.swift": []byte(content1),
"Package@swift-5.5.swift": []byte(content2),
})
p, err := ParsePackage(data, data.Size(), nil)
assert.NotNil(t, p)
assert.NoError(t, err)
assert.NotNil(t, p.Metadata)
assert.Empty(t, p.RepositoryURLs)
assert.Len(t, p.Metadata.Manifests, 2)
m := p.Metadata.Manifests[""]
assert.Equal(t, "5.7", m.ToolsVersion)
assert.Equal(t, content1, m.Content)
m = p.Metadata.Manifests["5.5"]
assert.Equal(t, "5.6", m.ToolsVersion)
assert.Equal(t, content2, m.Content)
})
t.Run("WithMetadata", func(t *testing.T) {
data := createArchive(map[string][]byte{
"Package.swift": []byte("// swift-tools-version:5.7\n//\n// Package.swift"),
})
p, err := ParsePackage(
data,
data.Size(),
strings.NewReader(`{"name":"`+packageName+`","version":"`+packageVersion+`","description":"`+packageDescription+`","keywords":["swift","package"],"license":"`+packageLicense+`","codeRepository":"`+packageRepositoryURL+`","author":{"givenName":"`+packageAuthor+`"},"repositoryURLs":["`+packageRepositoryURL+`"]}`),
)
assert.NotNil(t, p)
assert.NoError(t, err)
assert.NotNil(t, p.Metadata)
assert.Len(t, p.Metadata.Manifests, 1)
m := p.Metadata.Manifests[""]
assert.Equal(t, "5.7", m.ToolsVersion)
assert.Equal(t, packageDescription, p.Metadata.Description)
assert.ElementsMatch(t, []string{"swift", "package"}, p.Metadata.Keywords)
assert.Equal(t, packageLicense, p.Metadata.License)
assert.Equal(t, packageAuthor, p.Metadata.Author.GivenName)
assert.Equal(t, packageRepositoryURL, p.Metadata.RepositoryURL)
assert.ElementsMatch(t, []string{packageRepositoryURL}, p.RepositoryURLs)
})
}
func TestTrimmedVersionString(t *testing.T) {
cases := []struct {
Version *version.Version
Expected string
}{
{
Version: version.Must(version.NewVersion("1")),
Expected: "1.0",
},
{
Version: version.Must(version.NewVersion("1.0")),
Expected: "1.0",
},
{
Version: version.Must(version.NewVersion("1.0.0")),
Expected: "1.0",
},
{
Version: version.Must(version.NewVersion("1.0.1")),
Expected: "1.0.1",
},
{
Version: version.Must(version.NewVersion("1.0+meta")),
Expected: "1.0",
},
{
Version: version.Must(version.NewVersion("1.0.0+meta")),
Expected: "1.0",
},
{
Version: version.Must(version.NewVersion("1.0.1+meta")),
Expected: "1.0.1",
},
}
for _, c := range cases {
assert.Equal(t, c.Expected, TrimmedVersionString(c.Version))
}
}

View File

@ -12,9 +12,13 @@ import (
// Git settings // Git settings
var Git = struct { var Git = struct {
Path string Path string
HomePath string HomePath string
DisableDiffHighlight bool DisableDiffHighlight bool
Reflog struct {
Enabled bool
Expiration int
} `ini:"git.reflog"`
MaxGitDiffLines int MaxGitDiffLines int
MaxGitDiffLineCharacters int MaxGitDiffLineCharacters int
MaxGitDiffFiles int MaxGitDiffFiles int
@ -37,6 +41,13 @@ var Git = struct {
GC int `ini:"GC"` GC int `ini:"GC"`
} `ini:"git.timeout"` } `ini:"git.timeout"`
}{ }{
Reflog: struct {
Enabled bool
Expiration int
}{
Enabled: true,
Expiration: 90,
},
DisableDiffHighlight: false, DisableDiffHighlight: false,
MaxGitDiffLines: 1000, MaxGitDiffLines: 1000,
MaxGitDiffLineCharacters: 5000, MaxGitDiffLineCharacters: 5000,

View File

@ -39,6 +39,7 @@ var (
LimitSizePub int64 LimitSizePub int64
LimitSizePyPI int64 LimitSizePyPI int64
LimitSizeRubyGems int64 LimitSizeRubyGems int64
LimitSizeSwift int64
LimitSizeVagrant int64 LimitSizeVagrant int64
}{ }{
Enabled: true, Enabled: true,
@ -81,6 +82,7 @@ func loadPackagesFrom(rootCfg ConfigProvider) {
Packages.LimitSizePub = mustBytes(sec, "LIMIT_SIZE_PUB") Packages.LimitSizePub = mustBytes(sec, "LIMIT_SIZE_PUB")
Packages.LimitSizePyPI = mustBytes(sec, "LIMIT_SIZE_PYPI") Packages.LimitSizePyPI = mustBytes(sec, "LIMIT_SIZE_PYPI")
Packages.LimitSizeRubyGems = mustBytes(sec, "LIMIT_SIZE_RUBYGEMS") Packages.LimitSizeRubyGems = mustBytes(sec, "LIMIT_SIZE_RUBYGEMS")
Packages.LimitSizeSwift = mustBytes(sec, "LIMIT_SIZE_SWIFT")
Packages.LimitSizeVagrant = mustBytes(sec, "LIMIT_SIZE_VAGRANT") Packages.LimitSizeVagrant = mustBytes(sec, "LIMIT_SIZE_VAGRANT")
} }

View File

@ -90,6 +90,6 @@ func (s discardStorage) URL(_, _ string) (*url.URL, error) {
return nil, fmt.Errorf("%s", s) return nil, fmt.Errorf("%s", s)
} }
func (s discardStorage) IterateObjects(_ func(string, Object) error) error { func (s discardStorage) IterateObjects(_ string, _ func(string, Object) error) error {
return fmt.Errorf("%s", s) return fmt.Errorf("%s", s)
} }

View File

@ -42,7 +42,7 @@ func Test_discardStorage(t *testing.T) {
assert.Errorf(t, err, string(tt)) assert.Errorf(t, err, string(tt))
} }
{ {
err := tt.IterateObjects(func(_ string, _ Object) error { return nil }) err := tt.IterateObjects("", func(_ string, _ Object) error { return nil })
assert.Error(t, err, string(tt)) assert.Error(t, err, string(tt))
} }
}) })

View File

@ -127,8 +127,12 @@ func (l *LocalStorage) URL(path, name string) (*url.URL, error) {
} }
// IterateObjects iterates across the objects in the local storage // IterateObjects iterates across the objects in the local storage
func (l *LocalStorage) IterateObjects(fn func(path string, obj Object) error) error { func (l *LocalStorage) IterateObjects(prefix string, fn func(path string, obj Object) error) error {
return filepath.WalkDir(l.dir, func(path string, d os.DirEntry, err error) error { dir := l.dir
if prefix != "" {
dir = filepath.Join(l.dir, util.CleanPath(prefix))
}
return filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
if err != nil { if err != nil {
return err return err
} }

View File

@ -4,6 +4,10 @@
package storage package storage
import ( import (
"bytes"
"context"
"os"
"path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -50,3 +54,41 @@ func TestBuildLocalPath(t *testing.T) {
}) })
} }
} }
func TestLocalStorageIterator(t *testing.T) {
dir := filepath.Join(os.TempDir(), "TestLocalStorageIteratorTestDir")
l, err := NewLocalStorage(context.Background(), LocalStorageConfig{Path: dir})
assert.NoError(t, err)
testFiles := [][]string{
{"a/1.txt", "a1"},
{"/a/1.txt", "aa1"}, // same as above, but with leading slash that will be trim
{"b/1.txt", "b1"},
{"b/2.txt", "b2"},
{"b/3.txt", "b3"},
{"b/x 4.txt", "bx4"},
}
for _, f := range testFiles {
_, err = l.Save(f[0], bytes.NewBufferString(f[1]), -1)
assert.NoError(t, err)
}
expectedList := map[string][]string{
"a": {"a/1.txt"},
"b": {"b/1.txt", "b/2.txt", "b/3.txt", "b/x 4.txt"},
"": {"a/1.txt", "b/1.txt", "b/2.txt", "b/3.txt", "b/x 4.txt"},
"/": {"a/1.txt", "b/1.txt", "b/2.txt", "b/3.txt", "b/x 4.txt"},
"a/b/../../a": {"a/1.txt"},
}
for dir, expected := range expectedList {
count := 0
err = l.IterateObjects(dir, func(path string, f Object) error {
defer f.Close()
assert.Contains(t, expected, path)
count++
return nil
})
assert.NoError(t, err)
assert.Equal(t, count, len(expected))
}
}

View File

@ -209,12 +209,18 @@ func (m *MinioStorage) URL(path, name string) (*url.URL, error) {
} }
// IterateObjects iterates across the objects in the miniostorage // IterateObjects iterates across the objects in the miniostorage
func (m *MinioStorage) IterateObjects(fn func(path string, obj Object) error) error { func (m *MinioStorage) IterateObjects(prefix string, fn func(path string, obj Object) error) error {
opts := minio.GetObjectOptions{} opts := minio.GetObjectOptions{}
lobjectCtx, cancel := context.WithCancel(m.ctx) lobjectCtx, cancel := context.WithCancel(m.ctx)
defer cancel() defer cancel()
basePath := m.basePath
if prefix != "" {
basePath = m.buildMinioPath(prefix)
}
for mObjInfo := range m.client.ListObjects(lobjectCtx, m.bucket, minio.ListObjectsOptions{ for mObjInfo := range m.client.ListObjects(lobjectCtx, m.bucket, minio.ListObjectsOptions{
Prefix: m.basePath, Prefix: basePath,
Recursive: true, Recursive: true,
}) { }) {
object, err := m.client.GetObject(lobjectCtx, m.bucket, mObjInfo.Key, opts) object, err := m.client.GetObject(lobjectCtx, m.bucket, mObjInfo.Key, opts)
@ -223,7 +229,7 @@ func (m *MinioStorage) IterateObjects(fn func(path string, obj Object) error) er
} }
if err := func(object *minio.Object, fn func(path string, obj Object) error) error { if err := func(object *minio.Object, fn func(path string, obj Object) error) error {
defer object.Close() defer object.Close()
return fn(strings.TrimPrefix(mObjInfo.Key, m.basePath), &minioObject{object}) return fn(strings.TrimPrefix(mObjInfo.Key, basePath), &minioObject{object})
}(object, fn); err != nil { }(object, fn); err != nil {
return convertMinioErr(err) return convertMinioErr(err)
} }

View File

@ -65,7 +65,7 @@ type ObjectStorage interface {
Stat(path string) (os.FileInfo, error) Stat(path string) (os.FileInfo, error)
Delete(path string) error Delete(path string) error
URL(path, name string) (*url.URL, error) URL(path, name string) (*url.URL, error)
IterateObjects(func(path string, obj Object) error) error IterateObjects(path string, iterator func(path string, obj Object) error) error
} }
// Copy copies a file from source ObjectStorage to dest ObjectStorage // Copy copies a file from source ObjectStorage to dest ObjectStorage
@ -87,7 +87,7 @@ func Copy(dstStorage ObjectStorage, dstPath string, srcStorage ObjectStorage, sr
// Clean delete all the objects in this storage // Clean delete all the objects in this storage
func Clean(storage ObjectStorage) error { func Clean(storage ObjectStorage) error {
return storage.IterateObjects(func(path string, obj Object) error { return storage.IterateObjects("", func(path string, obj Object) error {
_ = obj.Close() _ = obj.Close()
return storage.Delete(path) return storage.Delete(path)
}) })

View File

@ -2934,6 +2934,8 @@ config.git_disable_diff_highlight = Disable Diff Syntax Highlight
config.git_max_diff_lines = Max Diff Lines (for a single file) config.git_max_diff_lines = Max Diff Lines (for a single file)
config.git_max_diff_line_characters = Max Diff Characters (for a single line) config.git_max_diff_line_characters = Max Diff Characters (for a single line)
config.git_max_diff_files = Max Diff Files (to be shown) config.git_max_diff_files = Max Diff Files (to be shown)
config.git_enable_reflogs = Enable Reflogs
config.git_reflog_expiry_time = Expiry Time
config.git_gc_args = GC Arguments config.git_gc_args = GC Arguments
config.git_migrate_timeout = Migration Timeout config.git_migrate_timeout = Migration Timeout
config.git_mirror_timeout = Mirror Update Timeout config.git_mirror_timeout = Mirror Update Timeout
@ -3237,6 +3239,10 @@ rubygems.dependencies.development = Development Dependencies
rubygems.required.ruby = Requires Ruby version rubygems.required.ruby = Requires Ruby version
rubygems.required.rubygems = Requires RubyGem version rubygems.required.rubygems = Requires RubyGem version
rubygems.documentation = For more information on the RubyGems registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/rubygems/">the documentation</a>. rubygems.documentation = For more information on the RubyGems registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/rubygems/">the documentation</a>.
swift.registry = Setup this registry from the command line:
swift.install = Add the package in your <code>Package.swift</code> file:
swift.install2 = and run the following command:
swift.documentation = For more information on the Swift registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/swift/">the documentation</a>.
vagrant.install = To add a Vagrant box, run the following command: vagrant.install = To add a Vagrant box, run the following command:
vagrant.documentation = For more information on the Vagrant registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/vagrant/">the documentation</a>. vagrant.documentation = For more information on the Vagrant registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/vagrant/">the documentation</a>.
settings.link = Link this package to a repository settings.link = Link this package to a repository

View File

@ -247,6 +247,7 @@ default_enable_timetracking_popup=Habilitar o cronômetro para novos repositóri
no_reply_address=Domínio de e-mail oculto no_reply_address=Domínio de e-mail oculto
no_reply_address_helper=Nome de domínio para usuários com um endereço de e-mail oculto. Por exemplo, o nome de usuário 'joe' será registrado no Git como 'joe@noreply.example.org' se o domínio de e-mail oculto estiver definido como 'noreply.example.org'. no_reply_address_helper=Nome de domínio para usuários com um endereço de e-mail oculto. Por exemplo, o nome de usuário 'joe' será registrado no Git como 'joe@noreply.example.org' se o domínio de e-mail oculto estiver definido como 'noreply.example.org'.
password_algorithm=Algoritmo Hash de Senha password_algorithm=Algoritmo Hash de Senha
invalid_password_algorithm=Algoritmo de hash de senha inválido
password_algorithm_helper=Escolha o algoritmo de hash para as senhas. Diferentes algoritmos têm requerimentos e forças diversos. O `Argon2` possui boa qualidade, porém usa muita memória e pode ser inapropriado para sistemas com menos recursos. password_algorithm_helper=Escolha o algoritmo de hash para as senhas. Diferentes algoritmos têm requerimentos e forças diversos. O `Argon2` possui boa qualidade, porém usa muita memória e pode ser inapropriado para sistemas com menos recursos.
enable_update_checker=Habilitar Verificador de Atualizações enable_update_checker=Habilitar Verificador de Atualizações
enable_update_checker_helper=Procura por novas versões periodicamente conectando-se ao gitea.io. enable_update_checker_helper=Procura por novas versões periodicamente conectando-se ao gitea.io.
@ -284,6 +285,7 @@ users=Usuários
organizations=Organizações organizations=Organizações
search=Pesquisar search=Pesquisar
code=Código code=Código
search.type.tooltip=Tipo de pesquisa
search.fuzzy=Similar search.fuzzy=Similar
search.fuzzy.tooltip=Incluir resultados que sejam próximos ao termo de busca search.fuzzy.tooltip=Incluir resultados que sejam próximos ao termo de busca
search.match=Correspondência search.match=Correspondência
@ -819,6 +821,7 @@ remove_account_link=Remover conta vinculada
remove_account_link_desc=A exclusão da chave SSH revogará o acesso à sua conta. Continuar? remove_account_link_desc=A exclusão da chave SSH revogará o acesso à sua conta. Continuar?
remove_account_link_success=A conta vinculada foi removida. remove_account_link_success=A conta vinculada foi removida.
hooks.desc=Adicionar webhooks que serão acionados para <strong>todos os repositórios</strong> pertencentes a este usuário.
orgs_none=Você não é membro de nenhuma organização. orgs_none=Você não é membro de nenhuma organização.
repos_none=Você não possui nenhum repositório repos_none=Você não possui nenhum repositório
@ -1231,6 +1234,7 @@ projects.column.color=Colorido
projects.open=Abrir projects.open=Abrir
projects.close=Fechar projects.close=Fechar
projects.column.assigned_to=Atribuído a projects.column.assigned_to=Atribuído a
projects.card_type.desc=Pré-visualizações de Cards
projects.card_type.images_and_text=Imagens e Texto projects.card_type.images_and_text=Imagens e Texto
projects.card_type.text_only=Somente texto projects.card_type.text_only=Somente texto
@ -1399,6 +1403,7 @@ issues.label_title=Nome da etiqueta
issues.label_description=Descrição da etiqueta issues.label_description=Descrição da etiqueta
issues.label_color=Cor da etiqueta issues.label_color=Cor da etiqueta
issues.label_exclusive=Exclusivo issues.label_exclusive=Exclusivo
issues.label_exclusive_desc=Nomeie o rótulo <code>escopo/item</code> para torná-lo mutuamente exclusivo com outros rótulos do <code>escopo/</code>.
issues.label_exclusive_warning=Quaisquer rótulos com escopo conflitantes serão removidos ao editar os rótulos de uma issue ou pull request. issues.label_exclusive_warning=Quaisquer rótulos com escopo conflitantes serão removidos ao editar os rótulos de uma issue ou pull request.
issues.label_count=%d etiquetas issues.label_count=%d etiquetas
issues.label_open_issues=%d issues abertas issues.label_open_issues=%d issues abertas
@ -1655,6 +1660,7 @@ pulls.merge_instruction_hint=`Você também pode ver as <a class="show-instructi
pulls.merge_instruction_step1_desc=No repositório do seu projeto, crie um novo branch e teste as alterações. pulls.merge_instruction_step1_desc=No repositório do seu projeto, crie um novo branch e teste as alterações.
pulls.merge_instruction_step2_desc=Faça merge das alterações e atualize no Gitea. pulls.merge_instruction_step2_desc=Faça merge das alterações e atualize no Gitea.
pulls.clear_merge_message=Limpar mensagem do merge pulls.clear_merge_message=Limpar mensagem do merge
pulls.clear_merge_message_hint=Limpar a mensagem de merge só irá remover o conteúdo da mensagem de commit e manter trailers git gerados, como "Co-Authored-By …".
pulls.auto_merge_button_when_succeed=(Quando a verificação for bem-sucedida) pulls.auto_merge_button_when_succeed=(Quando a verificação for bem-sucedida)
pulls.auto_merge_when_succeed=Mesclar automaticamente quando todas as verificações forem bem sucedidas pulls.auto_merge_when_succeed=Mesclar automaticamente quando todas as verificações forem bem sucedidas
@ -1810,6 +1816,7 @@ activity.git_stats_deletion_n=%d exclusões
search=Pesquisar search=Pesquisar
search.search_repo=Pesquisar no repositório... search.search_repo=Pesquisar no repositório...
search.type.tooltip=Tipo de pesquisa
search.fuzzy=Aproximada search.fuzzy=Aproximada
search.fuzzy.tooltip=Incluir resultados que sejam próximos ao termo de busca search.fuzzy.tooltip=Incluir resultados que sejam próximos ao termo de busca
search.match=Corresponde search.match=Corresponde
@ -2044,6 +2051,7 @@ settings.event_package_desc=Pacote criado ou excluído em um repositório.
settings.branch_filter=Filtro de branch settings.branch_filter=Filtro de branch
settings.branch_filter_desc=Lista dos branches a serem considerados nos eventos push, criação de branch e exclusão de branch, especificados como padrão glob. Se estiver vazio ou for <code>*</code>, eventos para todos os branches serão relatados. Veja <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentação da sintaxe. Exemplos: <code>master</code>, <code>{master,release*}</code>. settings.branch_filter_desc=Lista dos branches a serem considerados nos eventos push, criação de branch e exclusão de branch, especificados como padrão glob. Se estiver vazio ou for <code>*</code>, eventos para todos os branches serão relatados. Veja <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentação da sintaxe. Exemplos: <code>master</code>, <code>{master,release*}</code>.
settings.authorization_header=Header de Autorização settings.authorization_header=Header de Autorização
settings.authorization_header_desc=Será incluído como header de autorização para solicitações quando estiver presente. Exemplos: %s.
settings.active=Ativo settings.active=Ativo
settings.active_helper=Informações sobre eventos disparados serão enviadas para esta URL do webhook. settings.active_helper=Informações sobre eventos disparados serão enviadas para esta URL do webhook.
settings.add_hook_success=O webhook foi adicionado. settings.add_hook_success=O webhook foi adicionado.
@ -2124,6 +2132,7 @@ settings.dismiss_stale_approvals=Descartar aprovações obsoletas
settings.dismiss_stale_approvals_desc=Quando novos commits que mudam o conteúdo do pull request são enviados para o branch, as antigas aprovações serão descartadas. settings.dismiss_stale_approvals_desc=Quando novos commits que mudam o conteúdo do pull request são enviados para o branch, as antigas aprovações serão descartadas.
settings.require_signed_commits=Exibir commits assinados settings.require_signed_commits=Exibir commits assinados
settings.require_signed_commits_desc=Rejeitar pushes para este branch se não estiverem assinados ou não forem validáveis. settings.require_signed_commits_desc=Rejeitar pushes para este branch se não estiverem assinados ou não forem validáveis.
settings.protect_branch_name_pattern=Padrão de Nome de Branch Protegida
settings.protect_protected_file_patterns=Padrões de arquivos protegidos (separados usando ponto e vírgula '\;'): settings.protect_protected_file_patterns=Padrões de arquivos protegidos (separados usando ponto e vírgula '\;'):
settings.protect_protected_file_patterns_desc=Arquivos protegidos que não têm permissão para serem alterados diretamente, mesmo se o usuário tiver permissão para adicionar, editar ou apagar arquivos neste branch. Vários padrões podem ser separados usando ponto e vírgula ('\;'). Veja <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentação para sintaxe de padrões. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. settings.protect_protected_file_patterns_desc=Arquivos protegidos que não têm permissão para serem alterados diretamente, mesmo se o usuário tiver permissão para adicionar, editar ou apagar arquivos neste branch. Vários padrões podem ser separados usando ponto e vírgula ('\;'). Veja <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentação para sintaxe de padrões. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.protect_unprotected_file_patterns=Padrões de arquivos desprotegidos (separados usando ponto e vírgula '\;'): settings.protect_unprotected_file_patterns=Padrões de arquivos desprotegidos (separados usando ponto e vírgula '\;'):
@ -2132,6 +2141,7 @@ settings.add_protected_branch=Habilitar proteção
settings.delete_protected_branch=Desabilitar proteção settings.delete_protected_branch=Desabilitar proteção
settings.update_protect_branch_success=Proteção do branch '%s' foi atualizada. settings.update_protect_branch_success=Proteção do branch '%s' foi atualizada.
settings.remove_protected_branch_success=Proteção do branch '%s' foi desabilitada. settings.remove_protected_branch_success=Proteção do branch '%s' foi desabilitada.
settings.remove_protected_branch_failed=Removendo regra de proteção de branch '%s' falhou.
settings.protected_branch_deletion=Desabilitar proteção de branch settings.protected_branch_deletion=Desabilitar proteção de branch
settings.protected_branch_deletion_desc=Desabilitar a proteção de branch permite que os usuários com permissão de escrita realizem push. Continuar? settings.protected_branch_deletion_desc=Desabilitar a proteção de branch permite que os usuários com permissão de escrita realizem push. Continuar?
settings.block_rejected_reviews=Bloquear merge em revisões rejeitadas settings.block_rejected_reviews=Bloquear merge em revisões rejeitadas
@ -2146,6 +2156,8 @@ settings.default_merge_style_desc=Estilo de merge padrão para pull requests:
settings.choose_branch=Escolha um branch... settings.choose_branch=Escolha um branch...
settings.no_protected_branch=Não há branches protegidos. settings.no_protected_branch=Não há branches protegidos.
settings.edit_protected_branch=Editar settings.edit_protected_branch=Editar
settings.protected_branch_required_rule_name=Nome da regra é obrigatório
settings.protected_branch_duplicate_rule_name=Regra com nome duplicado
settings.protected_branch_required_approvals_min=Aprovações necessárias não podem ser negativas. settings.protected_branch_required_approvals_min=Aprovações necessárias não podem ser negativas.
settings.tags=Tags settings.tags=Tags
settings.tags.protection=Proteção das Tags settings.tags.protection=Proteção das Tags
@ -2278,6 +2290,8 @@ release.edit_subheader=Lançamentos organizam versões do projeto.
release.tag_name=Nome da tag release.tag_name=Nome da tag
release.target=Destino release.target=Destino
release.tag_helper=Escolha uma tag existente, ou crie uma nova tag. release.tag_helper=Escolha uma tag existente, ou crie uma nova tag.
release.tag_helper_new=Nova tag. Esta tag será criada a partir do alvo.
release.tag_helper_existing=Tag existente.
release.title=Título release.title=Título
release.content=Conteúdo release.content=Conteúdo
release.prerelease_desc=Marcar como pré-lançamento release.prerelease_desc=Marcar como pré-lançamento
@ -2570,6 +2584,10 @@ dashboard.delete_old_actions=Excluir todas as ações antigas do banco de dados
dashboard.delete_old_actions.started=A exclusão de todas as ações antigas do banco de dados foi iniciada. dashboard.delete_old_actions.started=A exclusão de todas as ações antigas do banco de dados foi iniciada.
dashboard.update_checker=Verificador de atualização dashboard.update_checker=Verificador de atualização
dashboard.delete_old_system_notices=Excluir todos os avisos de sistema antigos do banco de dados dashboard.delete_old_system_notices=Excluir todos os avisos de sistema antigos do banco de dados
dashboard.gc_lfs=Coletar lixos dos meta-objetos LFS
dashboard.stop_zombie_tasks=Parar tarefas zumbi
dashboard.stop_endless_tasks=Parar tarefas infinitas
dashboard.cancel_abandoned_jobs=Cancelar trabalhos abandonados
users.user_manage_panel=Gerenciamento de conta de usuário users.user_manage_panel=Gerenciamento de conta de usuário
users.new_account=Criar conta de usuário users.new_account=Criar conta de usuário
@ -2658,6 +2676,7 @@ repos.size=Tamanho
packages.package_manage_panel=Gerenciamento de Pacotes packages.package_manage_panel=Gerenciamento de Pacotes
packages.total_size=Tamanho Total: %s packages.total_size=Tamanho Total: %s
packages.unreferenced_size=Tamanho Não Referenciado: %s
packages.owner=Proprietário packages.owner=Proprietário
packages.creator=Criador packages.creator=Criador
packages.name=Nome packages.name=Nome
@ -2751,6 +2770,8 @@ auths.oauth2_required_claim_value_helper=Defina este valor para permitir o login
auths.oauth2_group_claim_name=Nome do claim que fornece os nomes dos grupos para esta fonte. (Opcional) auths.oauth2_group_claim_name=Nome do claim que fornece os nomes dos grupos para esta fonte. (Opcional)
auths.oauth2_admin_group=Valor do Claim de Grupo para os usuários administradores. (Opcional - requer nome do claim acima) auths.oauth2_admin_group=Valor do Claim de Grupo para os usuários administradores. (Opcional - requer nome do claim acima)
auths.oauth2_restricted_group=Valor do Claim de Grupo para os usuários restritos. (Opcional - requer nome do claim acima) auths.oauth2_restricted_group=Valor do Claim de Grupo para os usuários restritos. (Opcional - requer nome do claim acima)
auths.oauth2_map_group_to_team=Mapear grupos para Organizações. (Opcional - requer nome do claim acima)
auths.oauth2_map_group_to_team_removal=Remover usuários de equipes sincronizadas se o usuário não pertence ao grupo correspondente.
auths.enable_auto_register=Habilitar cadastro automático auths.enable_auto_register=Habilitar cadastro automático
auths.sspi_auto_create_users=Criar usuários automaticamente auths.sspi_auto_create_users=Criar usuários automaticamente
auths.sspi_auto_create_users_helper=Permitir que o método de autenticação SSPI crie automaticamente novas contas para usuários que fazem o login pela primeira vez auths.sspi_auto_create_users_helper=Permitir que o método de autenticação SSPI crie automaticamente novas contas para usuários que fazem o login pela primeira vez
@ -2791,6 +2812,8 @@ auths.still_in_used=A fonte de autenticação ainda está em uso. Converta ou ex
auths.deletion_success=A fonte de autenticação foi excluída. auths.deletion_success=A fonte de autenticação foi excluída.
auths.login_source_exist=A fonte de autenticação '%s' já existe. auths.login_source_exist=A fonte de autenticação '%s' já existe.
auths.login_source_of_type_exist=Uma fonte de autenticação deste tipo já existe. auths.login_source_of_type_exist=Uma fonte de autenticação deste tipo já existe.
auths.unable_to_initialize_openid=Não é possível inicializar o Provedor OpenID Connect: %s
auths.invalid_openIdConnectAutoDiscoveryURL=URL do Auto Discovery inválida (deve ser uma URL válida, começando com http:// ou https://)
config.server_config=Configuração do servidor config.server_config=Configuração do servidor
config.app_name=Nome do servidor config.app_name=Nome do servidor
@ -3039,6 +3062,7 @@ reopen_pull_request=`reabriu o pull request <a href="%[1]s">%[3]s#%[2]s</a>`
comment_issue=`comentou na issue <a href="%[1]s">%[3]s#%[2]s</a>` comment_issue=`comentou na issue <a href="%[1]s">%[3]s#%[2]s</a>`
comment_pull=`comentou no pull request <a href="%[1]s">%[3]s#%[2]s</a>` comment_pull=`comentou no pull request <a href="%[1]s">%[3]s#%[2]s</a>`
merge_pull_request=`fez merge do pull request <a href="%[1]s">%[3]s#%[2]s</a>` merge_pull_request=`fez merge do pull request <a href="%[1]s">%[3]s#%[2]s</a>`
auto_merge_pull_request=`fez merge automático do pull request <a href="%[1]s">%[3]s#%[2]s</a>`
transfer_repo=transferiu repositório de <code>%s</code> para <a href="%s">%s</a> transfer_repo=transferiu repositório de <code>%s</code> para <a href="%s">%s</a>
push_tag=fez push da tag <a href="%[2]s">%[3]s</a> to <a href="%[1]s">%[4]s</a> push_tag=fez push da tag <a href="%[2]s">%[3]s</a> to <a href="%[1]s">%[4]s</a>
delete_tag=excluiu tag %[2]s de <a href="%[1]s"> %[3]s</a> delete_tag=excluiu tag %[2]s de <a href="%[1]s"> %[3]s</a>
@ -3148,10 +3172,12 @@ dependency.id=ID
dependency.version=Versão dependency.version=Versão
cargo.registry=Configurar este registro no arquivo de configuração de Cargo (por exemplo <code>~/.cargo/config.toml</code>): cargo.registry=Configurar este registro no arquivo de configuração de Cargo (por exemplo <code>~/.cargo/config.toml</code>):
cargo.install=Para instalar o pacote usando Cargo, execute o seguinte comando: cargo.install=Para instalar o pacote usando Cargo, execute o seguinte comando:
cargo.documentation=Para obter mais informações sobre o registro Cargo, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/cargo/">a documentação</a>.
cargo.details.repository_site=Site do Repositório cargo.details.repository_site=Site do Repositório
cargo.details.documentation_site=Site da Documentação cargo.details.documentation_site=Site da Documentação
chef.registry=Configure este registro em seu arquivo <code>~/.chef/config.rb</code>: chef.registry=Configure este registro em seu arquivo <code>~/.chef/config.rb</code>:
chef.install=Para instalar o pacote, execute o seguinte comando: chef.install=Para instalar o pacote, execute o seguinte comando:
chef.documentation=Para obter mais informações sobre o registro Chef, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/chef/">a documentação</a>.
composer.registry=Configure este registro em seu arquivo <code>~/.composer/config.json</code>: composer.registry=Configure este registro em seu arquivo <code>~/.composer/config.json</code>:
composer.install=Para instalar o pacote usando o Composer, execute o seguinte comando: composer.install=Para instalar o pacote usando o Composer, execute o seguinte comando:
composer.documentation=Para obter mais informações sobre o registro do Composer, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/composer/">a documentação</a>. composer.documentation=Para obter mais informações sobre o registro do Composer, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/composer/">a documentação</a>.
@ -3224,6 +3250,15 @@ settings.delete.description=A exclusão de um pacote é permanente e não pode s
settings.delete.notice=Você está prestes a excluir %s (%s). Esta operação é irreversível, tem certeza? settings.delete.notice=Você está prestes a excluir %s (%s). Esta operação é irreversível, tem certeza?
settings.delete.success=O pacote foi excluído. settings.delete.success=O pacote foi excluído.
settings.delete.error=Falha ao excluir o pacote. settings.delete.error=Falha ao excluir o pacote.
owner.settings.cargo.title=Índice do Registro Cargo
owner.settings.cargo.initialize=Iniciar Índice
owner.settings.cargo.initialize.description=Para usar o registro Cargo é necessário um repositório git especial. Aqui você pode (re)criá-lo com a configuração necessária.
owner.settings.cargo.initialize.error=Falha ao inicializar índice Cargo: %v
owner.settings.cargo.initialize.success=O índice Cargo foi criado com sucesso.
owner.settings.cargo.rebuild=Reconstruir Índice
owner.settings.cargo.rebuild.description=Se o índice está fora de sincronia com os pacotes Cargo, você pode reconstruí-lo aqui.
owner.settings.cargo.rebuild.error=Falha ao reconstruir índice Cargo: %v
owner.settings.cargo.rebuild.success=O índice Cargo foi reconstruído com sucesso.
owner.settings.cleanuprules.title=Gerenciar Regras de Limpeza owner.settings.cleanuprules.title=Gerenciar Regras de Limpeza
owner.settings.cleanuprules.add=Adicionar Regra de Limpeza owner.settings.cleanuprules.add=Adicionar Regra de Limpeza
owner.settings.cleanuprules.edit=Editar Regra de Limpeza owner.settings.cleanuprules.edit=Editar Regra de Limpeza
@ -3232,6 +3267,7 @@ owner.settings.cleanuprules.preview=Pré-visualizar Regra de Limpeza
owner.settings.cleanuprules.preview.overview=%d pacotes agendados para serem removidos. owner.settings.cleanuprules.preview.overview=%d pacotes agendados para serem removidos.
owner.settings.cleanuprules.preview.none=A regra de limpeza não corresponde a nenhum pacote. owner.settings.cleanuprules.preview.none=A regra de limpeza não corresponde a nenhum pacote.
owner.settings.cleanuprules.enabled=Habilitado owner.settings.cleanuprules.enabled=Habilitado
owner.settings.cleanuprules.pattern_full_match=Aplicar padrão ao nome completo do pacote
owner.settings.cleanuprules.keep.title=Versões que correspondem a estas regras são mantidas, mesmo se corresponderem a uma regra de remoção abaixo. owner.settings.cleanuprules.keep.title=Versões que correspondem a estas regras são mantidas, mesmo se corresponderem a uma regra de remoção abaixo.
owner.settings.cleanuprules.keep.count=Manter o mais recente owner.settings.cleanuprules.keep.count=Manter o mais recente
owner.settings.cleanuprules.keep.count.1=1 versão por pacote owner.settings.cleanuprules.keep.count.1=1 versão por pacote
@ -3245,6 +3281,7 @@ owner.settings.cleanuprules.success.update=Regra de limpeza foi atualizada.
owner.settings.cleanuprules.success.delete=Regra de limpeza foi excluída. owner.settings.cleanuprules.success.delete=Regra de limpeza foi excluída.
owner.settings.chef.title=Registro Chef owner.settings.chef.title=Registro Chef
owner.settings.chef.keypair=Gerar par de chaves owner.settings.chef.keypair=Gerar par de chaves
owner.settings.chef.keypair.description=Gerar um par de chaves usado para autenticar no registro Chef. A chave anterior não pode ser usada depois.
[secrets] [secrets]
secrets=Segredos secrets=Segredos
@ -3253,6 +3290,8 @@ none=Não há segredos ainda.
value=Valor value=Valor
name=Nome name=Nome
creation=Adicionar Segredo creation=Adicionar Segredo
creation.name_placeholder=apenas caracteres alfanuméricos ou underline (_), não pode começar com GITEA_ ou GITHUB_
creation.value_placeholder=Insira qualquer conteúdo. Espaços em branco no início e no fim serão omitidos.
creation.success=O segredo '%s' foi adicionado. creation.success=O segredo '%s' foi adicionado.
creation.failed=Falha ao adicionar segredo. creation.failed=Falha ao adicionar segredo.
deletion=Excluir segredo deletion=Excluir segredo
@ -3274,6 +3313,10 @@ status.cancelled=Cancelado
status.skipped=Ignorado status.skipped=Ignorado
status.blocked=Bloqueado status.blocked=Bloqueado
runners=Runners
runners.runner_manage_panel=Gerenciamento de Runners
runners.new=Criar novo Runner
runners.new_notice=Como iniciar um runner
runners.status=Status runners.status=Status
runners.id=ID runners.id=ID
runners.name=Nome runners.name=Nome
@ -3281,21 +3324,36 @@ runners.owner_type=Tipo
runners.description=Descrição runners.description=Descrição
runners.labels=Rótulos runners.labels=Rótulos
runners.last_online=Última Vez Online runners.last_online=Última Vez Online
runners.agent_labels=Etiquetas do Agente
runners.custom_labels=Etiquetas Personalizadas runners.custom_labels=Etiquetas Personalizadas
runners.custom_labels_helper=Etiquetas personalizadas são etiquetas que são adicionadas manualmente por um administrador. Separe as etiquetas com vírgula. Espaço em branco no começo ou no final de cada etiqueta é ignorado. runners.custom_labels_helper=Etiquetas personalizadas são etiquetas que são adicionadas manualmente por um administrador. Separe as etiquetas com vírgula. Espaço em branco no começo ou no final de cada etiqueta é ignorado.
runners.runner_title=Runner
runners.task_list=Tarefas recentes neste runner
runners.task_list.run=Executar runners.task_list.run=Executar
runners.task_list.status=Status runners.task_list.status=Status
runners.task_list.repository=Repositório runners.task_list.repository=Repositório
runners.task_list.commit=Commit runners.task_list.commit=Commit
runners.task_list.done_at=Feito em
runners.edit_runner=Editar Runner
runners.update_runner=Atualizar as Alterações runners.update_runner=Atualizar as Alterações
runners.update_runner_success=Runner atualizado com sucesso
runners.update_runner_failed=Falha ao atualizar runner
runners.delete_runner=Deletar esse runner
runners.delete_runner_success=Runner excluído com sucesso
runners.delete_runner_failed=Falha ao excluir runner
runners.delete_runner_header=Confirme para excluir este runner
runners.delete_runner_notice=Se uma tarefa estiver sendo executada neste runner, ela será encerrada e marcada como falha. Pode quebrar o workflow de construção.
runners.none=Nenhum runner disponível
runners.status.unspecified=Desconhecido runners.status.unspecified=Desconhecido
runners.status.idle=Inativo runners.status.idle=Inativo
runners.status.active=Ativo runners.status.active=Ativo
runners.status.offline=Offiline runners.status.offline=Offiline
runs.all_workflows=Todos os Workflows
runs.open_tab=%d Aberto runs.open_tab=%d Aberto
runs.closed_tab=%d Fechado runs.closed_tab=%d Fechado
runs.commit=Commit runs.commit=Commit
runs.pushed_by=Push realizado por runs.pushed_by=Push realizado por
need_approval_desc=Precisa de aprovação para executar workflowa para pull request do fork.

View File

@ -0,0 +1 @@
<svg viewBox="0 0 448 512" class="svg fontawesome-save" width="16" height="16" aria-hidden="true"><path d="m434 130-84-84a48 48 0 0 0-33.9-14H48A48 48 0 0 0 0 80v352a48 48 0 0 0 48 48h352a48 48 0 0 0 48-48V163.9a48 48 0 0 0-14-34zM224 416a64 64 0 1 1 0-128 64 64 0 0 1 0 128zm96-304.5V212a12 12 0 0 1-12 12H76a12 12 0 0 1-12-12V108a12 12 0 0 1 12-12h228.5a12 12 0 0 1 8.5 3.5l3.5 3.5a12 12 0 0 1 3.5 8.5z"/></svg>

After

Width:  |  Height:  |  Size: 413 B

View File

@ -0,0 +1 @@
<svg xml:space="preserve" viewBox="0 0 59.5 59.5" class="svg gitea-swift" width="16" height="16" aria-hidden="true"><path fill="#F05138" d="M59.387 16.45a82.463 82.463 0 0 0-.027-1.792c-.034-1.301-.111-2.614-.343-3.9-.234-1.308-.618-2.523-1.222-3.71a12.464 12.464 0 0 0-5.452-5.452C51.156.992 49.94.609 48.635.374c-1.287-.232-2.6-.308-3.902-.343a85.714 85.714 0 0 0-1.792-.027C42.231 0 41.522 0 40.813 0H18.578c-.71 0-1.418 0-2.127.004-.598.004-1.196.01-1.793.027-.325.008-.651.02-.978.036-.978.047-1.959.133-2.924.307-.98.176-1.908.436-2.811.81A12.503 12.503 0 0 0 3.89 3.89a12.46 12.46 0 0 0-2.294 3.158C.992 8.235.61 9.45.374 10.758c-.231 1.286-.308 2.599-.343 3.9a85.767 85.767 0 0 0-.027 1.792C-.001 17.16 0 17.869 0 18.578v22.234c0 .71 0 1.419.004 2.129.004.597.01 1.194.027 1.79.035 1.302.112 2.615.343 3.902.235 1.306.618 2.522 1.222 3.71a12.457 12.457 0 0 0 5.453 5.453c1.186.603 2.401.986 3.707 1.22 1.287.232 2.6.309 3.902.344.597.016 1.195.023 1.793.026.709.005 1.418.004 2.127.004h22.235c.71 0 1.419.001 2.128-.004.598-.003 1.195-.01 1.792-.026 1.302-.035 2.615-.112 3.902-.344 1.306-.234 2.521-.617 3.708-1.221a12.461 12.461 0 0 0 5.452-5.453c.604-1.187.988-2.403 1.222-3.71.232-1.286.309-2.599.343-3.9.017-.597.023-1.194.027-1.791.005-.71.004-1.42.004-2.129V18.578c0-.71 0-1.419-.004-2.128z"/><path fill="#fff" d="m47.061 36.661-.004-.005c.066-.223.133-.446.19-.675 2.466-9.82-3.55-21.432-13.731-27.545 4.461 6.048 6.434 13.373 4.681 19.78-.156.572-.344 1.12-.552 1.653-.225-.148-.51-.316-.89-.526 0 0-10.128-6.253-21.104-17.313-.288-.29 5.853 8.777 12.822 16.14-3.283-1.842-12.434-8.5-18.227-13.802.712 1.187 1.559 2.33 2.49 3.43 4.837 6.136 11.145 13.705 18.703 19.518-5.31 3.25-12.814 3.502-20.285.003a30.646 30.646 0 0 1-5.193-3.098c3.162 5.058 8.033 9.423 13.96 11.97 7.07 3.039 14.1 2.833 19.337.05l-.004.007.079-.047c.215-.116.428-.233.637-.358 2.516-1.306 7.485-2.63 10.152 2.559.653 1.27 2.041-5.46-3.062-11.739z"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,50 @@
# Gitea Package Registry
This document gives a brief overview how the package registry is organized in code.
## Structure
The package registry code is divided into multiple modules to split the functionality and make code reuse possible.
| Module | Description |
| - | - |
| `models/packages` | Common methods and models used by all registry types |
| `models/packages/<type>` | Methods used by specific registry type. There should be no need to use type specific models. |
| `modules/packages` | Common methods and types used by multiple registry types |
| `modules/packages/<type>` | Registry type specific methods and types (e.g. metadata extraction of package files) |
| `routers/api/packages` | Route definitions for all registry types |
| `routers/api/packages/<type>` | Route implementation for a specific registry type |
| `services/packages` | Helper methods used by registry types to handle common tasks like package creation and deletion in `routers` |
| `services/packages/<type>` | Registry type specific methods used by `routers` and `services` |
## Models
Every package registry implementation uses the same underlaying models:
| Model | Description |
| - | - |
| `Package` | The root of a package providing values fixed for every version (e.g. the package name) |
| `PackageVersion` | A version of a package containing metadata (e.g. the package description) |
| `PackageFile` | A file of a package describing its content (e.g. file name) |
| `PackageBlob` | The content of a file (may be shared by multiple files) |
| `PackageProperty` | Additional properties attached to `Package`, `PackageVersion` or `PackageFile` (e.g. used if metadata is needed for routing) |
The following diagram shows the relationship between the models:
```
Package <1---*> PackageVersion <1---*> PackageFile <*---1> PackageBlob
```
## Adding a new package registry type
Before adding a new package registry type have a look at the existing implementation to get an impression of how it could work.
Most registry types offer endpoints to retrieve the metadata, upload and download package files.
The upload endpoint is often the heavy part because it must validate the uploaded blob, extract metadata and create the models.
The methods to validate and extract the metadata should be added in the `modules/packages/<type>` package.
If the upload is valid the methods in `services/packages` allow to store the upload and create the corresponding models.
It depends if the registry type allows multiple files per package version which method should be called:
- `CreatePackageAndAddFile`: error if package version already exists
- `CreatePackageOrAddFileToExisting`: error if file already exists
- `AddFileToExistingPackage`: error if package version does not exist or file already exists
`services/packages` also contains helper methods to download a file or to remove a package version.
There are no helper methods for metadata endpoints because they are very type specific.

View File

@ -28,6 +28,7 @@ import (
"code.gitea.io/gitea/routers/api/packages/pub" "code.gitea.io/gitea/routers/api/packages/pub"
"code.gitea.io/gitea/routers/api/packages/pypi" "code.gitea.io/gitea/routers/api/packages/pypi"
"code.gitea.io/gitea/routers/api/packages/rubygems" "code.gitea.io/gitea/routers/api/packages/rubygems"
"code.gitea.io/gitea/routers/api/packages/swift"
"code.gitea.io/gitea/routers/api/packages/vagrant" "code.gitea.io/gitea/routers/api/packages/vagrant"
"code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth"
context_service "code.gitea.io/gitea/services/context" context_service "code.gitea.io/gitea/services/context"
@ -375,6 +376,41 @@ func CommonRoutes(ctx gocontext.Context) *web.Route {
r.Delete("/yank", rubygems.DeletePackage) r.Delete("/yank", rubygems.DeletePackage)
}, reqPackageAccess(perm.AccessModeWrite)) }, reqPackageAccess(perm.AccessModeWrite))
}, reqPackageAccess(perm.AccessModeRead)) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/swift", func() {
r.Group("/{scope}/{name}", func() {
r.Group("", func() {
r.Get("", swift.EnumeratePackageVersions)
r.Get(".json", swift.EnumeratePackageVersions)
}, swift.CheckAcceptMediaType(swift.AcceptJSON))
r.Group("/{version}", func() {
r.Get("/Package.swift", swift.CheckAcceptMediaType(swift.AcceptSwift), swift.DownloadManifest)
r.Put("", reqPackageAccess(perm.AccessModeWrite), swift.CheckAcceptMediaType(swift.AcceptJSON), swift.UploadPackageFile)
r.Get("", func(ctx *context.Context) {
// Can't use normal routes here: https://github.com/go-chi/chi/issues/781
version := ctx.Params("version")
if strings.HasSuffix(version, ".zip") {
swift.CheckAcceptMediaType(swift.AcceptZip)(ctx)
if ctx.Written() {
return
}
ctx.SetParams("version", version[:len(version)-4])
swift.DownloadPackageFile(ctx)
} else {
swift.CheckAcceptMediaType(swift.AcceptJSON)(ctx)
if ctx.Written() {
return
}
if strings.HasSuffix(version, ".json") {
ctx.SetParams("version", version[:len(version)-5])
}
swift.PackageVersionMetadata(ctx)
}
})
})
})
r.Get("/identifiers", swift.CheckAcceptMediaType(swift.AcceptJSON), swift.LookupPackageIdentifiers)
}, reqPackageAccess(perm.AccessModeRead))
r.Group("/vagrant", func() { r.Group("/vagrant", func() {
r.Group("/authenticate", func() { r.Group("/authenticate", func() {
r.Get("", vagrant.CheckAuthenticate) r.Get("", vagrant.CheckAuthenticate)

View File

@ -0,0 +1,464 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package swift
import (
"errors"
"fmt"
"io"
"net/http"
"regexp"
"sort"
"strings"
packages_model "code.gitea.io/gitea/models/packages"
"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"
swift_module "code.gitea.io/gitea/modules/packages/swift"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
packages_service "code.gitea.io/gitea/services/packages"
"github.com/hashicorp/go-version"
)
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#35-api-versioning
const (
AcceptJSON = "application/vnd.swift.registry.v1+json"
AcceptSwift = "application/vnd.swift.registry.v1+swift"
AcceptZip = "application/vnd.swift.registry.v1+zip"
)
var (
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#361-package-scope
scopePattern = regexp.MustCompile(`\A[a-zA-Z0-9][a-zA-Z0-9-]{0,38}\z`)
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#362-package-name
namePattern = regexp.MustCompile(`\A[a-zA-Z0-9][a-zA-Z0-9-_]{0,99}\z`)
)
type headers struct {
Status int
ContentType string
Digest string
Location string
Link string
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#35-api-versioning
func setResponseHeaders(resp http.ResponseWriter, h *headers) {
if h.ContentType != "" {
resp.Header().Set("Content-Type", h.ContentType)
}
if h.Digest != "" {
resp.Header().Set("Digest", "sha256="+h.Digest)
}
if h.Location != "" {
resp.Header().Set("Location", h.Location)
}
if h.Link != "" {
resp.Header().Set("Link", h.Link)
}
resp.Header().Set("Content-Version", "1")
if h.Status != 0 {
resp.WriteHeader(h.Status)
}
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#33-error-handling
func apiError(ctx *context.Context, status int, obj interface{}) {
// https://www.rfc-editor.org/rfc/rfc7807
type Problem struct {
Status int `json:"status"`
Detail string `json:"detail"`
}
helper.LogAndProcessError(ctx, status, obj, func(message string) {
setResponseHeaders(ctx.Resp, &headers{
Status: status,
ContentType: "application/problem+json",
})
if err := json.NewEncoder(ctx.Resp).Encode(Problem{
Status: status,
Detail: message,
}); err != nil {
log.Error("JSON encode: %v", err)
}
})
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#35-api-versioning
func CheckAcceptMediaType(requiredAcceptHeader string) func(ctx *context.Context) {
return func(ctx *context.Context) {
accept := ctx.Req.Header.Get("Accept")
if accept != "" && accept != requiredAcceptHeader {
apiError(ctx, http.StatusBadRequest, fmt.Sprintf("Unexpected accept header. Should be '%s'.", requiredAcceptHeader))
}
}
}
func buildPackageID(scope, name string) string {
return scope + "." + name
}
type Release struct {
URL string `json:"url"`
}
type EnumeratePackageVersionsResponse struct {
Releases map[string]Release `json:"releases"`
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#41-list-package-releases
func EnumeratePackageVersions(ctx *context.Context) {
packageScope := ctx.Params("scope")
packageName := ctx.Params("name")
pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeSwift, buildPackageID(packageScope, packageName))
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
if len(pvs) == 0 {
apiError(ctx, http.StatusNotFound, nil)
return
}
pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
sort.Slice(pds, func(i, j int) bool {
return pds[i].SemVer.LessThan(pds[j].SemVer)
})
baseURL := fmt.Sprintf("%sapi/packages/%s/swift/%s/%s/", setting.AppURL, ctx.Package.Owner.LowerName, packageScope, packageName)
releases := make(map[string]Release)
for _, pd := range pds {
version := pd.SemVer.String()
releases[version] = Release{
URL: baseURL + version,
}
}
setResponseHeaders(ctx.Resp, &headers{
Link: fmt.Sprintf(`<%s%s>; rel="latest-version"`, baseURL, pds[len(pds)-1].Version.Version),
})
ctx.JSON(http.StatusOK, EnumeratePackageVersionsResponse{
Releases: releases,
})
}
type Resource struct {
Name string `json:"id"`
Type string `json:"type"`
Checksum string `json:"checksum"`
}
type PackageVersionMetadataResponse struct {
ID string `json:"id"`
Version string `json:"version"`
Resources []Resource `json:"resources"`
Metadata *swift_module.SoftwareSourceCode `json:"metadata"`
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-2
func PackageVersionMetadata(ctx *context.Context) {
id := buildPackageID(ctx.Params("scope"), ctx.Params("name"))
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeSwift, id, ctx.Params("version"))
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
pd, err := packages_model.GetPackageDescriptor(ctx, pv)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
metadata := pd.Metadata.(*swift_module.Metadata)
setResponseHeaders(ctx.Resp, &headers{})
ctx.JSON(http.StatusOK, PackageVersionMetadataResponse{
ID: id,
Version: pd.Version.Version,
Resources: []Resource{
{
Name: "source-archive",
Type: "application/zip",
Checksum: pd.Files[0].Blob.HashSHA256,
},
},
Metadata: &swift_module.SoftwareSourceCode{
Context: []string{"http://schema.org/"},
Type: "SoftwareSourceCode",
Name: pd.PackageProperties.GetByName(swift_module.PropertyName),
Version: pd.Version.Version,
Description: metadata.Description,
Keywords: metadata.Keywords,
CodeRepository: metadata.RepositoryURL,
License: metadata.License,
ProgrammingLanguage: swift_module.ProgrammingLanguage{
Type: "ComputerLanguage",
Name: "Swift",
URL: "https://swift.org",
},
Author: swift_module.Person{
Type: "Person",
GivenName: metadata.Author.GivenName,
MiddleName: metadata.Author.MiddleName,
FamilyName: metadata.Author.FamilyName,
},
},
})
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#43-fetch-manifest-for-a-package-release
func DownloadManifest(ctx *context.Context) {
packageScope := ctx.Params("scope")
packageName := ctx.Params("name")
packageVersion := ctx.Params("version")
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeSwift, buildPackageID(packageScope, packageName), packageVersion)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
pd, err := packages_model.GetPackageDescriptor(ctx, pv)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
swiftVersion := ctx.FormTrim("swift-version")
if swiftVersion != "" {
v, err := version.NewVersion(swiftVersion)
if err == nil {
swiftVersion = swift_module.TrimmedVersionString(v)
}
}
m, ok := pd.Metadata.(*swift_module.Metadata).Manifests[swiftVersion]
if !ok {
setResponseHeaders(ctx.Resp, &headers{
Status: http.StatusSeeOther,
Location: fmt.Sprintf("%sapi/packages/%s/swift/%s/%s/%s/Package.swift", setting.AppURL, ctx.Package.Owner.LowerName, packageScope, packageName, packageVersion),
})
return
}
setResponseHeaders(ctx.Resp, &headers{})
filename := "Package.swift"
if swiftVersion != "" {
filename = fmt.Sprintf("Package@swift-%s.swift", swiftVersion)
}
ctx.ServeContent(strings.NewReader(m.Content), &context.ServeHeaderOptions{
ContentType: "text/x-swift",
Filename: filename,
LastModified: pv.CreatedUnix.AsLocalTime(),
})
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-6
func UploadPackageFile(ctx *context.Context) {
packageScope := ctx.Params("scope")
packageName := ctx.Params("name")
v, err := version.NewVersion(ctx.Params("version"))
if !scopePattern.MatchString(packageScope) || !namePattern.MatchString(packageName) || err != nil {
apiError(ctx, http.StatusBadRequest, err)
return
}
packageVersion := v.Core().String()
file, _, err := ctx.Req.FormFile("source-archive")
if err != nil {
apiError(ctx, http.StatusBadRequest, err)
return
}
defer file.Close()
buf, err := packages_module.CreateHashedBufferFromReader(file, 32*1024*1024)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer buf.Close()
var mr io.Reader
metadata := ctx.Req.FormValue("metadata")
if metadata != "" {
mr = strings.NewReader(metadata)
}
pck, err := swift_module.ParsePackage(buf, buf.Size(), mr)
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
}
pv, _, err := packages_service.CreatePackageAndAddFile(
&packages_service.PackageCreationInfo{
PackageInfo: packages_service.PackageInfo{
Owner: ctx.Package.Owner,
PackageType: packages_model.TypeSwift,
Name: buildPackageID(packageScope, packageName),
Version: packageVersion,
},
SemverCompatible: true,
Creator: ctx.Doer,
Metadata: pck.Metadata,
PackageProperties: map[string]string{
swift_module.PropertyScope: packageScope,
swift_module.PropertyName: packageName,
},
},
&packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{
Filename: fmt.Sprintf("%s-%s.zip", packageName, packageVersion),
},
Creator: ctx.Doer,
Data: buf,
IsLead: true,
},
)
if err != nil {
switch err {
case packages_model.ErrDuplicatePackageVersion:
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
}
for _, url := range pck.RepositoryURLs {
_, err = packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, swift_module.PropertyRepositoryURL, url)
if err != nil {
log.Error("InsertProperty failed: %v", err)
}
}
setResponseHeaders(ctx.Resp, &headers{})
ctx.Status(http.StatusCreated)
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-4
func DownloadPackageFile(ctx *context.Context) {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeSwift, buildPackageID(ctx.Params("scope"), ctx.Params("name")), ctx.Params("version"))
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
} else {
apiError(ctx, http.StatusInternalServerError, err)
}
return
}
pd, err := packages_model.GetPackageDescriptor(ctx, pv)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
pf := pd.Files[0].File
s, _, err := packages_service.GetPackageFileStream(ctx, pf)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer s.Close()
setResponseHeaders(ctx.Resp, &headers{
Digest: pd.Files[0].Blob.HashSHA256,
})
ctx.ServeContent(s, &context.ServeHeaderOptions{
Filename: pf.Name,
ContentType: "application/zip",
LastModified: pf.CreatedUnix.AsLocalTime(),
})
}
type LookupPackageIdentifiersResponse struct {
Identifiers []string `json:"identifiers"`
}
// https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-5
func LookupPackageIdentifiers(ctx *context.Context) {
url := ctx.FormTrim("url")
if url == "" {
apiError(ctx, http.StatusBadRequest, nil)
return
}
pvs, _, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
OwnerID: ctx.Package.Owner.ID,
Type: packages_model.TypeSwift,
Properties: map[string]string{
swift_module.PropertyRepositoryURL: url,
},
IsInternal: util.OptionalBoolFalse,
})
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
if len(pvs) == 0 {
apiError(ctx, http.StatusNotFound, nil)
return
}
pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
identifiers := make([]string, 0, len(pds))
for _, pd := range pds {
identifiers = append(identifiers, pd.Package.Name)
}
setResponseHeaders(ctx.Resp, &headers{})
ctx.JSON(http.StatusOK, LookupPackageIdentifiersResponse{
Identifiers: identifiers,
})
}

View File

@ -32,6 +32,8 @@ func CreateRepo(ctx *context.APIContext) {
// responses: // responses:
// "201": // "201":
// "$ref": "#/responses/Repository" // "$ref": "#/responses/Repository"
// "400":
// "$ref": "#/responses/error"
// "403": // "403":
// "$ref": "#/responses/forbidden" // "$ref": "#/responses/forbidden"
// "404": // "404":

View File

@ -305,6 +305,10 @@ func DeleteUser(ctx *context.APIContext) {
// description: username of user to delete // description: username of user to delete
// type: string // type: string
// required: true // required: true
// - name: purge
// in: query
// description: purge the user from the system completely
// type: boolean
// responses: // responses:
// "204": // "204":
// "$ref": "#/responses/empty" // "$ref": "#/responses/empty"

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: [cargo, chef, composer, conan, conda, container, generic, helm, maven, npm, nuget, pub, pypi, rubygems, vagrant] // enum: [cargo, chef, composer, conan, conda, container, generic, helm, maven, npm, nuget, pub, pypi, rubygems, swift, vagrant]
// - name: q // - name: q
// in: query // in: query
// description: name filter // description: name filter

View File

@ -231,6 +231,13 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
if opt.AutoInit && opt.Readme == "" { if opt.AutoInit && opt.Readme == "" {
opt.Readme = "Default" opt.Readme = "Default"
} }
// If the readme template does not exist, a 400 will be returned.
if opt.AutoInit && len(opt.Readme) > 0 && !util.SliceContains(repo_module.Readmes, opt.Readme) {
ctx.Error(http.StatusBadRequest, "", fmt.Errorf("readme template does not exist, available templates: %v", repo_module.Readmes))
return
}
repo, err := repo_service.CreateRepository(ctx, ctx.Doer, owner, repo_module.CreateRepoOptions{ repo, err := repo_service.CreateRepository(ctx, ctx.Doer, owner, repo_module.CreateRepoOptions{
Name: opt.Name, Name: opt.Name,
Description: opt.Description, Description: opt.Description,
@ -283,6 +290,8 @@ func Create(ctx *context.APIContext) {
// responses: // responses:
// "201": // "201":
// "$ref": "#/responses/Repository" // "$ref": "#/responses/Repository"
// "400":
// "$ref": "#/responses/error"
// "409": // "409":
// description: The repository with the same name already exists. // description: The repository with the same name already exists.
// "422": // "422":
@ -464,6 +473,8 @@ func CreateOrgRepo(ctx *context.APIContext) {
// responses: // responses:
// "201": // "201":
// "$ref": "#/responses/Repository" // "$ref": "#/responses/Repository"
// "400":
// "$ref": "#/responses/error"
// "404": // "404":
// "$ref": "#/responses/notFound" // "$ref": "#/responses/notFound"
// "403": // "403":

View File

@ -31,7 +31,7 @@ func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) {
return return
} }
if err := repos.LoadAttributes(); err != nil { if err := repos.LoadAttributes(ctx); err != nil {
ctx.Error(http.StatusInternalServerError, "RepositoryList.LoadAttributes", err) ctx.Error(http.StatusInternalServerError, "RepositoryList.LoadAttributes", err)
return return
} }

View File

@ -133,6 +133,8 @@ func List(ctx *context.Context) {
pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5) pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx) pager.SetDefaultParams(ctx)
pager.AddParamString("workflow", workflow)
pager.AddParamString("state", ctx.FormString("state"))
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplListActions) ctx.HTML(http.StatusOK, tplListActions)

View File

@ -117,7 +117,7 @@ func getNotifications(ctx *context.Context) {
return return
} }
notifications = notifications.Without(failures) notifications = notifications.Without(failures)
if err := repos.LoadAttributes(); err != nil { // TODO if err := repos.LoadAttributes(ctx); err != nil {
ctx.ServerError("LoadAttributes", err) ctx.ServerError("LoadAttributes", err)
return return
} }

View File

@ -21,35 +21,60 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
} }
run := job.Run run := job.Run
if run.Event != webhook_module.HookEventPush { var (
sha string
creatorID int64
)
switch run.Event {
case webhook_module.HookEventPush:
payload, err := run.GetPushEventPayload()
if err != nil {
return fmt.Errorf("GetPushEventPayload: %w", err)
}
// Since the payload comes from json data, we should check if it's broken, or it will cause panic
switch {
case payload.Repo == nil:
return fmt.Errorf("repo is missing in event payload")
case payload.Pusher == nil:
return fmt.Errorf("pusher is missing in event payload")
case payload.HeadCommit == nil:
return fmt.Errorf("head commit is missing in event payload")
}
sha = payload.HeadCommit.ID
creatorID = payload.Pusher.ID
case webhook_module.HookEventPullRequest:
payload, err := run.GetPullRequestEventPayload()
if err != nil {
return fmt.Errorf("GetPullRequestEventPayload: %w", err)
}
switch {
case payload.PullRequest == nil:
return fmt.Errorf("pull request is missing in event payload")
case payload.PullRequest.Head == nil:
return fmt.Errorf("head of pull request is missing in event payload")
case payload.PullRequest.Head.Repository == nil:
return fmt.Errorf("head repository of pull request is missing in event payload")
case payload.PullRequest.Head.Repository.Owner == nil:
return fmt.Errorf("owner of head repository of pull request is missing in evnt payload")
}
sha = payload.PullRequest.Head.Sha
creatorID = payload.PullRequest.Head.Repository.Owner.ID
default:
return nil return nil
} }
payload, err := run.GetPushEventPayload() repo := run.Repo
if err != nil { ctxname := job.Name
return fmt.Errorf("GetPushEventPayload: %w", err) state := toCommitStatus(job.Status)
} creator, err := user_model.GetUserByID(ctx, creatorID)
// Since the payload comes from json data, we should check if it's broken, or it will cause panic
switch {
case payload.Repo == nil:
return fmt.Errorf("repo is missing in event payload")
case payload.Pusher == nil:
return fmt.Errorf("pusher is missing in event payload")
case payload.HeadCommit == nil:
return fmt.Errorf("head commit is missing in event payload")
}
creator, err := user_model.GetUserByID(ctx, payload.Pusher.ID)
if err != nil { if err != nil {
return fmt.Errorf("GetUserByID: %w", err) return fmt.Errorf("GetUserByID: %w", err)
} }
repo := run.Repo
sha := payload.HeadCommit.ID
ctxname := job.Name
state := toCommitStatus(job.Status)
if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{}); err == nil { if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{}); err == nil {
for _, v := range statuses { for _, v := range statuses {
if v.Context == ctxname { if v.Context == ctxname {
@ -65,14 +90,14 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{
Repo: repo, Repo: repo,
SHA: payload.HeadCommit.ID, SHA: sha,
Creator: creator, Creator: creator,
CommitStatus: &git_model.CommitStatus{ CommitStatus: &git_model.CommitStatus{
SHA: sha, SHA: sha,
TargetURL: run.Link(), TargetURL: run.Link(),
Description: "", Description: "",
Context: ctxname, Context: ctxname,
CreatorID: payload.Pusher.ID, CreatorID: creatorID,
State: state, State: state,
}, },
}); err != nil { }); err != nil {

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(cargo,chef,composer,conan,conda,container,generic,helm,maven,npm,nuget,pub,pypi,rubygems,vagrant)"` Type string `binding:"Required;In(cargo,chef,composer,conan,conda,container,generic,helm,maven,npm,nuget,pub,pypi,rubygems,swift,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

@ -361,6 +361,8 @@ func CheckSizeQuotaExceeded(ctx context.Context, doer, owner *user_model.User, p
typeSpecificSize = setting.Packages.LimitSizePyPI typeSpecificSize = setting.Packages.LimitSizePyPI
case packages_model.TypeRubyGems: case packages_model.TypeRubyGems:
typeSpecificSize = setting.Packages.LimitSizeRubyGems typeSpecificSize = setting.Packages.LimitSizeRubyGems
case packages_model.TypeSwift:
typeSpecificSize = setting.Packages.LimitSizeSwift
case packages_model.TypeVagrant: case packages_model.TypeVagrant:
typeSpecificSize = setting.Packages.LimitSizeVagrant typeSpecificSize = setting.Packages.LimitSizeVagrant
} }

View File

@ -433,7 +433,7 @@
<div class="field"> <div class="field">
<button class="ui green button">{{.locale.Tr "admin.auths.update"}}</button> <button class="ui green button">{{.locale.Tr "admin.auths.update"}}</button>
<div class="ui red button delete-button" data-url="{{$.Link}}/delete" data-id="{{.Source.ID}}">{{.locale.Tr "admin.auths.delete"}}</div> <button class="ui red button delete-button" data-url="{{$.Link}}/delete" data-id="{{.Source.ID}}">{{.locale.Tr "admin.auths.delete"}}</button>
</div> </div>
</form> </form>
</div> </div>

View File

@ -331,7 +331,19 @@
<dd>{{.Git.MaxGitDiffFiles}}</dd> <dd>{{.Git.MaxGitDiffFiles}}</dd>
<dt>{{.locale.Tr "admin.config.git_gc_args"}}</dt> <dt>{{.locale.Tr "admin.config.git_gc_args"}}</dt>
<dd><code>{{.Git.GCArgs}}</code></dd> <dd><code>{{.Git.GCArgs}}</code></dd>
<div class="ui divider"></div> <div class="ui divider"></div>
<dt>{{.locale.Tr "admin.config.git_enable_reflogs"}}</dt>
<dd>{{if .Git.Reflog.Enabled}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
{{if .Git.Reflog.Enabled}}
<dt>{{.locale.Tr "admin.config.git_reflog_expiry_time"}}</dt>
<dd>{{.locale.Tr "tool.days" .Git.Reflog.Expiration}}</dd>
{{end}}
<div class="ui divider"></div>
<dt>{{.locale.Tr "admin.config.git_migrate_timeout"}}</dt> <dt>{{.locale.Tr "admin.config.git_migrate_timeout"}}</dt>
<dd>{{.Git.Timeout.Migrate}} {{.locale.Tr "tool.raw_seconds"}}</dd> <dd>{{.Git.Timeout.Migrate}} {{.locale.Tr "tool.raw_seconds"}}</dd>
<dt>{{.locale.Tr "admin.config.git_mirror_timeout"}}</dt> <dt>{{.locale.Tr "admin.config.git_mirror_timeout"}}</dt>

View File

@ -78,7 +78,7 @@
{{.locale.Tr "admin.emails.change_email_header"}} {{.locale.Tr "admin.emails.change_email_header"}}
</div> </div>
<div class="content center"> <div class="content center">
<p>{{.locale.Tr "admin.emails.change_email_text"}}</p> <p class="center">{{.locale.Tr "admin.emails.change_email_text"}}</p>
<form class="ui form" id="email-action-form" action="{{AppSubUrl}}/admin/emails/activate" method="post"> <form class="ui form" id="email-action-form" action="{{AppSubUrl}}/admin/emails/activate" method="post">
{{$.CsrfTokenHtml}} {{$.CsrfTokenHtml}}
@ -93,11 +93,9 @@
<input type="hidden" id="form-primary" name="primary" value="" required> <input type="hidden" id="form-primary" name="primary" value="" required>
<input type="hidden" id="form-activate" name="activate" value="" required> <input type="hidden" id="form-activate" name="activate" value="" required>
<div class="center actions"> <div class="center">
<div class="ui basic cancel inverted button">{{$.locale.Tr "settings.cancel"}}</div> {{template "base/delete_modal_actions" .}}
<button class="ui basic inverted yellow button">{{$.locale.Tr "modal.yes"}}</button>
</div> </div>
</form> </form>
</div> </div>
</div> </div>

View File

@ -23,7 +23,7 @@
<tr> <tr>
<td class="collapsing"> <td class="collapsing">
<div class="ui fitted checkbox" data-id="{{.ID}}"> <div class="ui fitted checkbox" data-id="{{.ID}}">
<input type="checkbox"> <label></label> <input type="checkbox">
</div> </div>
</td> </td>
<td>{{.ID}}</td> <td>{{.ID}}</td>
@ -39,13 +39,11 @@
<tr> <tr>
<th></th> <th></th>
<th colspan="5"> <th colspan="5">
<div class="ui right"> <form class="ui right" method="post" action="{{AppSubUrl}}/admin/notices/empty">
<form method="post" action="{{AppSubUrl}}/admin/notices/empty"> {{.CsrfTokenHtml}}
{{.CsrfTokenHtml}} <button type="submit" class="ui red small button">{{.locale.Tr "admin.notices.delete_all"}}</button>
<button type="submit" class="ui red small button">{{.locale.Tr "admin.notices.delete_all"}}</button> </form>
</form> <div class="ui floating upward dropdown small button">{{/* TODO: Make this dropdown accessible */}}
</div>
<div class="ui floating upward dropdown small button">
<span class="text">{{.locale.Tr "admin.notices.operations"}}</span> <span class="text">{{.locale.Tr "admin.notices.operations"}}</span>
<div class="menu"> <div class="menu">
<div class="item select action" data-action="select-all"> <div class="item select action" data-action="select-all">
@ -59,9 +57,9 @@
</div> </div>
</div> </div>
</div> </div>
<div class="ui small teal button" id="delete-selection" data-link="{{.Link}}/delete" data-redirect="{{.Link}}?page={{.Page.Paginater.Current}}"> <button class="ui small teal button" id="delete-selection" data-link="{{.Link}}/delete" data-redirect="{{.Link}}?page={{.Page.Paginater.Current}}">
{{.locale.Tr "admin.notices.delete_selected"}} {{.locale.Tr "admin.notices.delete_selected"}}
</div> </button>
</th> </th>
</tr> </tr>
</tfoot> </tfoot>

View File

@ -43,16 +43,7 @@
<input type="hidden" name="action" value="adopt"> <input type="hidden" name="action" value="adopt">
<input type="hidden" name="q" value="{{$.Keyword}}"> <input type="hidden" name="q" value="{{$.Keyword}}">
<input type="hidden" name="page" value="{{$.CurrentPage}}"> <input type="hidden" name="page" value="{{$.CurrentPage}}">
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
{{svg "octicon-trash" 16 "gt-mr-2"}}
{{$.locale.Tr "modal.no"}}
</div>
<button class="ui green basic inverted ok button">
{{svg "octicon-check" 16 "gt-mr-2"}}
{{$.locale.Tr "modal.yes"}}
</button>
</div>
</form> </form>
</div> </div>
<button class="ui button submit tiny red delete show-modal" data-modal="#delete-unadopted-modal-{{$dirI}}"><span class="icon">{{svg "octicon-x"}}</span><span class="label">{{$.locale.Tr "repo.delete_preexisting_label"}}</span></button> <button class="ui button submit tiny red delete show-modal" data-modal="#delete-unadopted-modal-{{$dirI}}"><span class="icon">{{svg "octicon-x"}}</span><span class="label">{{$.locale.Tr "repo.delete_preexisting_label"}}</span></button>
@ -70,16 +61,7 @@
<input type="hidden" name="action" value="delete"> <input type="hidden" name="action" value="delete">
<input type="hidden" name="q" value="{{$.Keyword}}"> <input type="hidden" name="q" value="{{$.Keyword}}">
<input type="hidden" name="page" value="{{$.CurrentPage}}"> <input type="hidden" name="page" value="{{$.CurrentPage}}">
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
{{svg "octicon-trash" 16 "gt-mr-2"}}
{{$.locale.Tr "modal.no"}}
</div>
<button class="ui green basic inverted ok button">
{{svg "octicon-check" 16 "gt-mr-2"}}
{{$.locale.Tr "modal.yes"}}
</button>
</div>
</form> </form>
</div> </div>
</div> </div>

View File

@ -151,7 +151,7 @@
<div class="field"> <div class="field">
<button class="ui green button">{{.locale.Tr "admin.users.update_profile"}}</button> <button class="ui green button">{{.locale.Tr "admin.users.update_profile"}}</button>
<div class="ui red button show-modal" data-modal="#delete-user-modal">{{.locale.Tr "admin.users.delete_account"}}</div> <button class="ui red button show-modal" data-modal="#delete-user-modal">{{.locale.Tr "admin.users.delete_account"}}</button>
</div> </div>
</form> </form>
</div> </div>
@ -189,7 +189,7 @@
<div class="field"> <div class="field">
<button class="ui green button">{{$.locale.Tr "settings.update_avatar"}}</button> <button class="ui green button">{{$.locale.Tr "settings.update_avatar"}}</button>
<a class="ui red button delete-post" data-request-url="{{.Link}}/avatar/delete" data-done-url="{{.Link}}">{{$.locale.Tr "settings.delete_current_avatar"}}</a> <a class="ui red button delete-post" data-request-url="{{.Link}}/avatar/delete" data-done-url="{{.Link}}">{{$.locale.Tr "settings.delete_current_avatar"}}</a>{{/* TODO: Convert links without href to buttons for a11y */}}
</div> </div>
</form> </form>
</div> </div>
@ -213,16 +213,7 @@
</div> </div>
<p class="help">{{.locale.Tr "admin.users.purge_help"}}</p> <p class="help">{{.locale.Tr "admin.users.purge_help"}}</p>
</div> </div>
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
{{svg "octicon-x"}}
{{.locale.Tr "modal.no"}}
</div>
<button class="ui green basic inverted ok button">
{{svg "octicon-check"}}
{{.locale.Tr "modal.yes"}}
</button>
</div>
</form> </form>
</div> </div>
{{template "base/footer" .}} {{template "base/footer" .}}

View File

@ -1,10 +1,10 @@
<div class="actions"> <div class="actions">
<div class="ui red basic inverted cancel button"> <button class="ui red basic inverted cancel button">
{{svg "octicon-x"}} {{svg "octicon-x"}}
{{.locale.Tr "modal.no"}} {{.locale.Tr "modal.no"}}
</div> </button>
<div class="ui green basic inverted ok button"> <button class="ui green basic inverted ok button">
{{svg "octicon-check"}} {{svg "octicon-check"}}
{{.locale.Tr "modal.yes"}} {{.locale.Tr "modal.yes"}}
</div> </button>
</div> </div>

View File

@ -19,9 +19,9 @@
<label for="org_name">{{.locale.Tr "org.org_name_holder"}}</label> <label for="org_name">{{.locale.Tr "org.org_name_holder"}}</label>
<input id="org_name" name="org_name" value="" autocomplete="off" autofocus required> <input id="org_name" name="org_name" value="" autocomplete="off" autofocus required>
</div> </div>
<div class="ui red button delete-button" data-type="form" data-form="#delete-form"> <button class="ui red button delete-button" data-type="form" data-form="#delete-form">
{{.locale.Tr "org.settings.confirm_delete_account"}} {{.locale.Tr "org.settings.confirm_delete_account"}}
</div> </button>
</form> </form>
</div> </div>
</div> </div>

View File

@ -11,7 +11,7 @@
</div> </div>
<div class="right floated three wide column"> <div class="right floated three wide column">
<div class="ui right"> <div class="ui right">
<div class="ui green new-label button">{{.locale.Tr "repo.issues.new_label"}}</div> <button class="ui green new-label button">{{.locale.Tr "repo.issues.new_label"}}</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,40 @@
{{if eq .PackageDescriptor.Package.Type "swift"}}
<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-terminal"}} {{.locale.Tr "packages.swift.registry"}}</label>
<div class="markup"><pre class="code-block"><code>swift package-registry set <gitea-origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/swift"></gitea-origin-url></code></pre></div>
</div>
<div class="field">
<label>{{svg "octicon-code"}} {{.locale.Tr "packages.swift.install" | Safe}}</label>
<div class="markup"><pre class="code-block"><code>dependencies: [
.package(id: "{{.PackageDescriptor.Package.Name}}", from:"{{.PackageDescriptor.Version.Version}}")
]</code></pre></div>
</div>
<div class="field">
<label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.swift.install2"}}</label>
<div class="markup"><pre class="code-block"><code>swift package resolve</code></pre></div>
</div>
<div class="field">
<label>{{.locale.Tr "packages.swift.documentation" | Safe}}</label>
</div>
</div>
</div>
{{if .PackageDescriptor.Metadata.Description}}
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
<div class="ui attached segment">
{{if .PackageDescriptor.Metadata.Description}}{{.PackageDescriptor.Metadata.Description}}{{end}}
</div>
{{end}}
{{if .PackageDescriptor.Metadata.Keywords}}
<h4 class="ui top attached header">{{.locale.Tr "packages.keywords"}}</h4>
<div class="ui attached segment">
{{range .PackageDescriptor.Metadata.Keywords}}
{{.}}
{{end}}
</div>
{{end}}
{{end}}

View File

@ -0,0 +1,4 @@
{{if eq .PackageDescriptor.Package.Type "swift"}}
{{if .PackageDescriptor.Metadata.Author.String}}<div class="item" title="{{.locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Author}}</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.details.project_site"}}</a></div>{{end}}
{{end}}

View File

@ -57,10 +57,7 @@
<form class="ui form" action="{{.Link}}" method="post"> <form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<input type="hidden" name="action" value="delete"> <input type="hidden" name="action" value="delete">
<div class="text right actions"> {{template "base/delete_modal_actions" .}}
<div class="ui cancel button">{{.locale.Tr "cancel"}}</div>
<button class="ui red button">{{.locale.Tr "ok"}}</button>
</div>
</form> </form>
</div> </div>
</div> </div>

View File

@ -33,6 +33,7 @@
{{template "package/content/pub" .}} {{template "package/content/pub" .}}
{{template "package/content/pypi" .}} {{template "package/content/pypi" .}}
{{template "package/content/rubygems" .}} {{template "package/content/rubygems" .}}
{{template "package/content/swift" .}}
{{template "package/content/vagrant" .}} {{template "package/content/vagrant" .}}
</div> </div>
<div class="four wide column"> <div class="four wide column">
@ -59,6 +60,7 @@
{{template "package/metadata/pub" .}} {{template "package/metadata/pub" .}}
{{template "package/metadata/pypi" .}} {{template "package/metadata/pypi" .}}
{{template "package/metadata/rubygems" .}} {{template "package/metadata/rubygems" .}}
{{template "package/metadata/swift" .}}
{{template "package/metadata/vagrant" .}} {{template "package/metadata/vagrant" .}}
<div class="item">{{svg "octicon-database" 16 "gt-mr-3"}} {{FileSize .PackageDescriptor.CalculateBlobSize}}</div> <div class="item">{{svg "octicon-database" 16 "gt-mr-3"}} {{FileSize .PackageDescriptor.CalculateBlobSize}}</div>
</div> </div>

View File

@ -84,15 +84,6 @@
<div class="content"> <div class="content">
<p>{{.locale.Tr "repo.projects.deletion_desc"}}</p> <p>{{.locale.Tr "repo.projects.deletion_desc"}}</p>
</div> </div>
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i>
{{.locale.Tr "modal.no"}}
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i>
{{.locale.Tr "modal.yes"}}
</div>
</div>
</div> </div>
{{end}} {{end}}

View File

@ -29,7 +29,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</button>
<button data-url="{{$.Link}}" class="ui primary button" id="new_board_submit">{{$.locale.Tr "repo.projects.column.new_submit"}}</button> <button data-url="{{$.Link}}" class="ui primary button" id="new_board_submit">{{$.locale.Tr "repo.projects.column.new_submit"}}</button>
</div> </div>
</form> </form>
@ -127,7 +127,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</button>
<button data-url="{{$.Link}}/{{.ID}}" class="ui primary button edit-column-button">{{$.locale.Tr "repo.projects.column.edit"}}</button> <button data-url="{{$.Link}}/{{.ID}}" class="ui primary button edit-column-button">{{$.locale.Tr "repo.projects.column.edit"}}</button>
</div> </div>
</form> </form>
@ -144,7 +144,7 @@
</label> </label>
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</button>
<button class="ui primary button set-default-project-board" data-url="{{$.Link}}/{{.ID}}/default">{{$.locale.Tr "repo.projects.column.set_default"}}</button> <button class="ui primary button set-default-project-board" data-url="{{$.Link}}/{{.ID}}/default">{{$.locale.Tr "repo.projects.column.set_default"}}</button>
</div> </div>
</div> </div>
@ -158,8 +158,8 @@
{{$.locale.Tr "repo.projects.column.deletion_desc"}} {{$.locale.Tr "repo.projects.column.deletion_desc"}}
</label> </label>
</div> </div>
<div class="text right actions"> <div class="text right actions">{{/* TODO: convert to base/delete_modal_actions.tmpl */}}
<div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</button>
<button class="ui red button delete-project-board" data-url="{{$.Link}}/{{.ID}}">{{$.locale.Tr "repo.projects.column.delete"}}</button> <button class="ui red button delete-project-board" data-url="{{$.Link}}/{{.ID}}">{{$.locale.Tr "repo.projects.column.delete"}}</button>
</div> </div>
</div> </div>
@ -265,15 +265,6 @@
<div class="content"> <div class="content">
<p>{{.locale.Tr "repo.projects.deletion_desc"}}</p> <p>{{.locale.Tr "repo.projects.deletion_desc"}}</p>
</div> </div>
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i>
{{.locale.Tr "modal.no"}}
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i>
{{.locale.Tr "modal.yes"}}
</div>
</div>
</div> </div>
{{end}} {{end}}

View File

@ -1,14 +1,18 @@
<div class="issue list"> <div class="issue list">
{{range .Runs}} {{range .Runs}}
<li class="item gt-df gt-py-3"> <li class="item gt-df gt-py-3 gt-ab">
<div class="issue-item-left gt-df"> <div class="issue-item-left gt-df">
{{template "repo/actions/status" .Status}} {{template "repo/actions/status" .Status}}
</div> </div>
<div class="issue-item-main gt-f1 gt-fc gt-df"> <div class="issue-item-main action-item-main gt-f1 gt-fc gt-df gt-mr-3">
<div class="issue-item-top-row"> <div class="issue-item-top-row">
<a class="index gt-ml-0 gt-mr-2" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}"> <a class="index gt-ml-0 gt-mr-2" title="{{.Title}}" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
{{- .Title -}} {{- .Title -}}
</a> </a>
</div>
<div class="desc issue-item-bottom-row gt-df gt-ac gt-fw gt-my-1">
<b>{{if not $.CurWorkflow}}{{.WorkflowID}} {{end}}#{{.Index}}</b>: {{$.locale.Tr "actions.runs.commit"}}
<a href="{{$.RepoLink}}/commit/{{.CommitSHA}}">{{ShortSha .CommitSHA}}</a>{{$.locale.Tr "actions.runs.pushed_by"}} {{.TriggerUser.GetDisplayName}}
<span class="ui label"> <span class="ui label">
{{if .RefLink}} {{if .RefLink}}
<a href="{{.RefLink}}">{{.PrettyRef}}</a> <a href="{{.RefLink}}">{{.PrettyRef}}</a>
@ -17,10 +21,6 @@
{{end}} {{end}}
</span> </span>
</div> </div>
<div class="desc issue-item-bottom-row gt-df gt-ac gt-fw gt-my-1">
<b>{{if not $.CurWorkflow}}{{.WorkflowID}} {{end}}#{{.Index}}</b>: {{$.locale.Tr "actions.runs.commit"}}
<a href="{{$.RepoLink}}/commit/{{.CommitSHA}}">{{ShortSha .CommitSHA}}</a>&nbsp; {{$.locale.Tr "actions.runs.pushed_by"}} {{.TriggerUser.GetDisplayName | Escape}}
</div>
</div> </div>
<div class="issue-item-right"> <div class="issue-item-right">
<div>{{TimeSinceUnix .Updated $.locale}}</div> <div>{{TimeSinceUnix .Updated $.locale}}</div>

View File

@ -176,7 +176,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{.locale.Tr "settings.cancel"}}</button>
<button class="ui green button">{{.locale.Tr "repo.branch.confirm_create_branch"}}</button> <button class="ui green button">{{.locale.Tr "repo.branch.confirm_create_branch"}}</button>
</div> </div>
</form> </form>

View File

@ -15,8 +15,8 @@
</div> </div>
</div> </div>
<div class="actions"> <div class="actions">
<div class="ui black deny button"> <button class="ui black cancel button">
{{.locale.Tr "cancel"}} {{.locale.Tr "cancel"}}
</div> </button>
</div> </div>
</div> </div>

View File

@ -96,7 +96,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{.locale.Tr "settings.cancel"}}</button>
<button class="ui green button">{{.locale.Tr "repo.branch.confirm_create_branch"}}</button> <button class="ui green button">{{.locale.Tr "repo.branch.confirm_create_branch"}}</button>
</div> </div>
</form> </form>
@ -121,7 +121,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{.locale.Tr "settings.cancel"}}</button>
<button class="ui green button">{{.locale.Tr "repo.tag.confirm_create_tag"}}</button> <button class="ui green button">{{.locale.Tr "repo.tag.confirm_create_tag"}}</button>
</div> </div>
</form> </form>

View File

@ -1,6 +1,9 @@
{{if eq .State "pending"}} {{if eq .State "pending"}}
{{svg "octicon-dot-fill" 18 "commit-status icon text yellow"}} {{svg "octicon-dot-fill" 18 "commit-status icon text yellow"}}
{{end}} {{end}}
{{if eq .State "running"}}
{{svg "octicon-dot-fill" 18 "commit-status icon text yellow"}}
{{end}}
{{if eq .State "success"}} {{if eq .State "success"}}
{{svg "octicon-check" 18 "commit-status icon text green"}} {{svg "octicon-check" 18 "commit-status icon text green"}}
{{end}} {{end}}

View File

@ -107,8 +107,8 @@
<div class="diff-file-header-actions gt-df gt-ac"> <div class="diff-file-header-actions gt-df gt-ac">
{{if $showFileViewToggle}} {{if $showFileViewToggle}}
<div class="ui compact icon buttons"> <div class="ui compact icon buttons">
<span class="ui tiny basic button tooltip file-view-toggle" data-toggle-selector="#diff-source-{{$file.NameHash}}" data-content="{{$.locale.Tr "repo.file_view_source"}}" data-position="bottom center">{{svg "octicon-code"}}</span> <button class="ui tiny basic button tooltip file-view-toggle" data-toggle-selector="#diff-source-{{$file.NameHash}}" data-content="{{$.locale.Tr "repo.file_view_source"}}" data-position="bottom center">{{svg "octicon-code"}}</button>
<span class="ui tiny basic button tooltip file-view-toggle active" data-toggle-selector="#diff-rendered-{{$file.NameHash}}" data-content="{{$.locale.Tr "repo.file_view_rendered"}}" data-position="bottom center">{{svg "octicon-file"}}</span> <button class="ui tiny basic button tooltip file-view-toggle active" data-toggle-selector="#diff-rendered-{{$file.NameHash}}" data-content="{{$.locale.Tr "repo.file_view_rendered"}}" data-position="bottom center">{{svg "octicon-file"}}</button>
</div> </div>
{{end}} {{end}}
{{if $file.IsProtected}} {{if $file.IsProtected}}
@ -200,8 +200,8 @@
{{$.locale.Tr "loading"}} {{$.locale.Tr "loading"}}
</div> </div>
<div class="text right edit buttons"> <div class="text right edit buttons">
<div class="ui basic primary cancel button" tabindex="3">{{.locale.Tr "repo.issues.cancel"}}</div> <button class="ui basic primary cancel button" tabindex="3">{{.locale.Tr "repo.issues.cancel"}}</button>
<div class="ui green save button" tabindex="2">{{.locale.Tr "repo.issues.save"}}</div> <button class="ui green save button" tabindex="2">{{.locale.Tr "repo.issues.save"}}</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -65,14 +65,14 @@
<p>{{.locale.Tr "repo.editor.commit_empty_file_text"}}</p> <p>{{.locale.Tr "repo.editor.commit_empty_file_text"}}</p>
</div> </div>
<div class="actions"> <div class="actions">
<div class="ui red basic cancel inverted button"> <button class="ui red basic cancel inverted button">
<i class="remove icon"></i> <i class="remove icon"></i>
{{.locale.Tr "repo.editor.cancel"}} {{.locale.Tr "repo.editor.cancel"}}
</div> </button>
<div class="ui green basic ok inverted button"> <button class="ui green basic ok inverted button">
<i class="save icon"></i> <i class="save icon"></i>
{{.locale.Tr "repo.editor.commit_changes"}} {{.locale.Tr "repo.editor.commit_changes"}}
</div> </button>
</div> </div>
</div> </div>

View File

@ -45,14 +45,14 @@
<p>{{.locale.Tr "repo.editor.commit_empty_file_text"}}</p> <p>{{.locale.Tr "repo.editor.commit_empty_file_text"}}</p>
</div> </div>
<div class="actions"> <div class="actions">
<div class="ui red basic cancel inverted button"> <button class="ui red basic inverted cancel button">
<i class="remove icon"></i> {{svg "octicon-x"}}
{{.locale.Tr "repo.editor.cancel"}} {{.locale.Tr "repo.editor.cancel"}}
</div> </button>
<div class="ui green basic ok inverted button"> <button class="ui green basic inverted ok button">
<i class="save icon"></i> {{svg "fontawesome-save"}}
{{.locale.Tr "repo.editor.commit_changes"}} {{.locale.Tr "repo.editor.commit_changes"}}
</div> </button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -6,7 +6,7 @@
{{template "repo/issue/navbar" .}} {{template "repo/issue/navbar" .}}
{{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}} {{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}}
<div class="ui right"> <div class="ui right">
<div class="ui green new-label button">{{.locale.Tr "repo.issues.new_label"}}</div> <button class="ui green new-label button">{{.locale.Tr "repo.issues.new_label"}}</button>
</div> </div>
{{end}} {{end}}
</div> </div>

View File

@ -6,16 +6,7 @@
<div class="content"> <div class="content">
<p>{{.locale.Tr "repo.issues.label_deletion_desc"}}</p> <p>{{.locale.Tr "repo.issues.label_deletion_desc"}}</p>
</div> </div>
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i>
{{.locale.Tr "modal.no"}}
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i>
{{.locale.Tr "modal.yes"}}
</div>
</div>
</div> </div>
<div class="ui small edit-label modal"> <div class="ui small edit-label modal">
@ -61,11 +52,13 @@
</form> </form>
</div> </div>
<div class="actions"> <div class="actions">
<div class="ui secondary small basic cancel button"> <button class="ui secondary small basic cancel button">
{{svg "octicon-x"}}
{{.locale.Tr "cancel"}} {{.locale.Tr "cancel"}}
</div> </button>
<div class="ui primary small approve button"> <button class="ui primary small approve button">
<i class="save icon"></i>
{{.locale.Tr "save"}} {{.locale.Tr "save"}}
</div> </button>
</div> </div>
</div> </div>

View File

@ -36,12 +36,15 @@
</div> </div>
</form> </form>
</div> </div>
<div class="actions"> <div class="actions">
<div class="ui secondary small basic cancel button"> <button class="ui red basic inverted cancel button">
{{svg "octicon-x"}}
{{.locale.Tr "cancel"}} {{.locale.Tr "cancel"}}
</div> </button>
<div class="ui primary small approve button"> <button class="ui green basic inverted ok button">
{{svg "octicon-check"}}
{{.locale.Tr "repo.issues.create_label"}} {{.locale.Tr "repo.issues.create_label"}}
</div> </button>
</div> </div>
</div> </div>

View File

@ -213,9 +213,9 @@
{{if not .Repository.IsArchived}} {{if not .Repository.IsArchived}}
<!-- Action Button --> <!-- Action Button -->
{{if .IsShowClosed}} {{if .IsShowClosed}}
<div class="ui green active basic button issue-action" data-action="open" data-url="{{$.RepoLink}}/issues/status" style="margin-left: auto">{{.locale.Tr "repo.issues.action_open"}}</div> <button class="ui green active basic button issue-action" data-action="open" data-url="{{$.RepoLink}}/issues/status" style="margin-left: auto">{{.locale.Tr "repo.issues.action_open"}}</button>
{{else}} {{else}}
<div class="ui red active basic button issue-action" data-action="close" data-url="{{$.RepoLink}}/issues/status" style="margin-left: auto">{{.locale.Tr "repo.issues.action_close"}}</div> <button class="ui red active basic button issue-action" data-action="close" data-url="{{$.RepoLink}}/issues/status" style="margin-left: auto">{{.locale.Tr "repo.issues.action_close"}}</button>
{{end}} {{end}}
<!-- Labels --> <!-- Labels -->
<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item"> <div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item">

View File

@ -149,9 +149,9 @@
<div class="ui secondary filter stackable menu"> <div class="ui secondary filter stackable menu">
<!-- Action Button --> <!-- Action Button -->
{{if .IsShowClosed}} {{if .IsShowClosed}}
<div class="ui green active basic button issue-action" data-action="open" data-url="{{$.RepoLink}}/issues/status" style="margin-left: auto">{{.locale.Tr "repo.issues.action_open"}}</div> <button class="ui green active basic button issue-action" data-action="open" data-url="{{$.RepoLink}}/issues/status" style="margin-left: auto">{{.locale.Tr "repo.issues.action_open"}}</button>
{{else}} {{else}}
<div class="ui red active basic button issue-action" data-action="close" data-url="{{$.RepoLink}}/issues/status" style="margin-left: auto">{{.locale.Tr "repo.issues.action_close"}}</div> <button class="ui red active basic button issue-action" data-action="close" data-url="{{$.RepoLink}}/issues/status" style="margin-left: auto">{{.locale.Tr "repo.issues.action_close"}}</button>
{{end}} {{end}}
<!-- Labels --> <!-- Labels -->
<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item"> <div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item">

View File

@ -124,16 +124,7 @@
<div class="content"> <div class="content">
<p>{{.locale.Tr "repo.milestones.deletion_desc"}}</p> <p>{{.locale.Tr "repo.milestones.deletion_desc"}}</p>
</div> </div>
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i>
{{.locale.Tr "modal.no"}}
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i>
{{.locale.Tr "modal.yes"}}
</div>
</div>
</div> </div>
{{end}} {{end}}
{{template "base/footer" .}} {{template "base/footer" .}}

View File

@ -112,13 +112,13 @@
<div class="text right"> <div class="text right">
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .DisableStatusChange)}} {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .DisableStatusChange)}}
{{if .Issue.IsClosed}} {{if .Issue.IsClosed}}
<div id="status-button" class="ui green basic button" tabindex="6" data-status="{{.locale.Tr "repo.issues.reopen_issue"}}" data-status-and-comment="{{.locale.Tr "repo.issues.reopen_comment_issue"}}" data-status-val="reopen"> <button id="status-button" class="ui green basic button" tabindex="6" data-status="{{.locale.Tr "repo.issues.reopen_issue"}}" data-status-and-comment="{{.locale.Tr "repo.issues.reopen_comment_issue"}}" data-status-val="reopen">
{{.locale.Tr "repo.issues.reopen_issue"}} {{.locale.Tr "repo.issues.reopen_issue"}}
</div> </button>
{{else}} {{else}}
<div id="status-button" class="ui red basic button" tabindex="6" data-status="{{.locale.Tr "repo.issues.close_issue"}}" data-status-and-comment="{{.locale.Tr "repo.issues.close_comment_issue"}}" data-status-val="close"> <button id="status-button" class="ui red basic button" tabindex="6" data-status="{{.locale.Tr "repo.issues.close_issue"}}" data-status-and-comment="{{.locale.Tr "repo.issues.close_comment_issue"}}" data-status-val="close">
{{.locale.Tr "repo.issues.close_issue"}} {{.locale.Tr "repo.issues.close_issue"}}
</div> </button>
{{end}} {{end}}
{{end}} {{end}}
<button class="ui green button loading-button" tabindex="5"> <button class="ui green button loading-button" tabindex="5">
@ -163,13 +163,13 @@
<div class="text right"> <div class="text right">
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .DisableStatusChange)}} {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .DisableStatusChange)}}
{{if .Issue.IsClosed}} {{if .Issue.IsClosed}}
<div id="status-button" class="ui green basic button" tabindex="6" data-status="{{.locale.Tr "repo.issues.reopen_issue"}}" data-status-and-comment="{{.locale.Tr "repo.issues.reopen_comment_issue"}}" data-status-val="reopen"> <button id="status-button" class="ui green basic button" tabindex="6" data-status="{{.locale.Tr "repo.issues.reopen_issue"}}" data-status-and-comment="{{.locale.Tr "repo.issues.reopen_comment_issue"}}" data-status-val="reopen">
{{.locale.Tr "repo.issues.reopen_issue"}} {{.locale.Tr "repo.issues.reopen_issue"}}
</div> </button>
{{else}} {{else}}
<div id="status-button" class="ui red basic button" tabindex="6" data-status="{{.locale.Tr "repo.issues.close_issue"}}" data-status-and-comment="{{.locale.Tr "repo.issues.close_comment_issue"}}" data-status-val="close"> <button id="status-button" class="ui red basic button" tabindex="6" data-status="{{.locale.Tr "repo.issues.close_issue"}}" data-status-and-comment="{{.locale.Tr "repo.issues.close_comment_issue"}}" data-status-val="close">
{{.locale.Tr "repo.issues.close_issue"}} {{.locale.Tr "repo.issues.close_issue"}}
</div> </button>
{{end}} {{end}}
{{end}} {{end}}
<button class="ui green button loading-button" tabindex="5"> <button class="ui green button loading-button" tabindex="5">
@ -215,8 +215,8 @@
{{end}} {{end}}
<div class="field footer"> <div class="field footer">
<div class="text right edit"> <div class="text right edit">
<div class="ui basic secondary cancel button" tabindex="3">{{.locale.Tr "repo.issues.cancel"}}</div> <button class="ui basic secondary cancel button" tabindex="3">{{.locale.Tr "repo.issues.cancel"}}</button>
<div class="ui primary save button" tabindex="2">{{.locale.Tr "repo.issues.save"}}</div> <button class="ui primary save button" tabindex="2">{{.locale.Tr "repo.issues.save"}}</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -7,10 +7,7 @@
{{.ctxData.CsrfTokenHtml}} {{.ctxData.CsrfTokenHtml}}
</form> </form>
<div class="header">{{.ctxData.locale.Tr "repo.issues.del_time"}}</div> <div class="header">{{.ctxData.locale.Tr "repo.issues.del_time"}}</div>
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red approve button">{{.ctxData.locale.Tr "repo.issues.context.delete"}}</div>
<div class="ui cancel button">{{.ctxData.locale.Tr "repo.issues.add_time_cancel"}}</div>
</div>
</div> </div>
<button class="ui icon button compact mini issue-delete-time tooltip" data-id="{{.comment.Time.ID}}" data-content="{{.ctxData.locale.Tr "repo.issues.del_time"}}" data-position="top right"> <button class="ui icon button compact mini issue-delete-time tooltip" data-id="{{.comment.Time.ID}}" data-content="{{.ctxData.locale.Tr "repo.issues.del_time"}}" data-position="top right">
{{svg "octicon-trash"}} {{svg "octicon-trash"}}

View File

@ -58,7 +58,7 @@
<input id="message" name="message"> <input id="message" name="message">
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</button>
<button class="ui red button" type="submit">{{$.locale.Tr "ok"}}</button> <button class="ui red button" type="submit">{{$.locale.Tr "ok"}}</button>
</div> </div>
</form> </form>

View File

@ -346,8 +346,8 @@
</form> </form>
</div> </div>
<div class="actions"> <div class="actions">
<div class="ui green approve button">{{.locale.Tr "repo.issues.add_time_short"}}</div> <button class="ui green approve button">{{.locale.Tr "repo.issues.add_time_short"}}</button>
<div class="ui red cancel button">{{.locale.Tr "repo.issues.add_time_cancel"}}</div> <button class="ui red cancel button">{{.locale.Tr "repo.issues.add_time_cancel"}}</button>
</div> </div>
</div> </div>
<button class="ui fluid button green tooltip issue-add-time gt-mt-3" data-content='{{.locale.Tr "repo.issues.add_time"}}' data-position="top center">{{.locale.Tr "repo.issues.add_time_short"}}</button> <button class="ui fluid button green tooltip issue-add-time gt-mt-3" data-content='{{.locale.Tr "repo.issues.add_time"}}' data-position="top center">{{.locale.Tr "repo.issues.add_time_short"}}</button>
@ -532,14 +532,14 @@
{{end}}</p> {{end}}</p>
</div> </div>
<div class="actions"> <div class="actions">
<div class="ui red cancel inverted button"> <button class="ui red cancel inverted button">
{{svg "octicon-x"}} {{svg "octicon-x"}}
{{.locale.Tr "repo.issues.dependency.cancel"}} {{.locale.Tr "repo.issues.dependency.cancel"}}
</div> </button>
<div class="ui green ok inverted button"> <button class="ui green ok inverted button">
{{svg "octicon-check"}} {{svg "octicon-check"}}
{{.locale.Tr "repo.issues.dependency.remove"}} {{.locale.Tr "repo.issues.dependency.remove"}}
</div> </button>
</div> </div>
</div> </div>
{{end}} {{end}}
@ -619,7 +619,7 @@
{{end}} {{end}}
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{.locale.Tr "settings.cancel"}}</button>
<button class="ui red button"> <button class="ui red button">
{{if .Issue.IsLocked}} {{if .Issue.IsLocked}}
{{.locale.Tr "repo.issues.unlock_confirm"}} {{.locale.Tr "repo.issues.unlock_confirm"}}
@ -655,7 +655,7 @@
<form action="{{.Issue.Link}}/delete" method="post"> <form action="{{.Issue.Link}}/delete" method="post">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<div class="center actions"> <div class="center actions">
<div class="ui basic cancel inverted button">{{.locale.Tr "settings.cancel"}}</div> <button class="ui basic cancel inverted button">{{.locale.Tr "settings.cancel"}}</button>
<button class="ui basic red inverted button">{{.locale.Tr "modal.yes"}}</button> <button class="ui basic red inverted button">{{.locale.Tr "modal.yes"}}</button>
</div> </div>
</form> </form>

View File

@ -19,8 +19,8 @@
<div class="ui dropdown icon button no-text"> <div class="ui dropdown icon button no-text">
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu"> <div class="menu">
<div class="item active selected" data-do="{{.Link}}/update">{{$.locale.Tr "repo.pulls.update_branch"}}</div> <a class="item active selected" data-do="{{.Link}}/update">{{$.locale.Tr "repo.pulls.update_branch"}}</a>
<div class="item" data-do="{{.Link}}/update?style=rebase">{{$.locale.Tr "repo.pulls.update_branch_rebase"}}</div> <a class="item" data-do="{{.Link}}/update?style=rebase">{{$.locale.Tr "repo.pulls.update_branch_rebase"}}</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,9 +1,7 @@
<div class="sixteen wide column title"> <div class="sixteen wide column title">
<div class="issue-title" id="issue-title-wrapper"> <div class="issue-title" id="issue-title-wrapper">
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
<div class="edit-button"> <button id="edit-title" class="ui basic button secondary edit-button not-in-edit">{{.locale.Tr "repo.issues.edit"}}</button>
<button id="edit-title" class="ui basic button secondary not-in-edit">{{.locale.Tr "repo.issues.edit"}}</button>
</div>
{{end}} {{end}}
<h1> <h1>
<span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title $.RepoLink $.Repository.ComposeMetas | RenderCodeBlock}}</span> <span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title $.RepoLink $.Repository.ComposeMetas | RenderCodeBlock}}</span>

View File

@ -72,7 +72,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{.locale.Tr "settings.cancel"}}</button>
<button class="ui red button">{{.locale.Tr "repo.settings.confirm_delete"}}</button> <button class="ui red button">{{.locale.Tr "repo.settings.confirm_delete"}}</button>
</div> </div>
</form> </form>

View File

@ -86,16 +86,7 @@
<div class="content"> <div class="content">
<p>{{.locale.Tr "repo.projects.deletion_desc"}}</p> <p>{{.locale.Tr "repo.projects.deletion_desc"}}</p>
</div> </div>
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i>
{{.locale.Tr "modal.no"}}
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i>
{{.locale.Tr "modal.yes"}}
</div>
</div>
</div> </div>
{{end}} {{end}}
{{template "base/footer" .}} {{template "base/footer" .}}

View File

@ -33,7 +33,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</button>
<button data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}" class="ui primary button" id="new_board_submit">{{$.locale.Tr "repo.projects.column.new_submit"}}</button> <button data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}" class="ui primary button" id="new_board_submit">{{$.locale.Tr "repo.projects.column.new_submit"}}</button>
</div> </div>
</form> </form>
@ -131,7 +131,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</button>
<button data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}" class="ui primary button">{{$.locale.Tr "repo.projects.column.edit"}}</button> <button data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}" class="ui primary button">{{$.locale.Tr "repo.projects.column.edit"}}</button>
</div> </div>
</form> </form>
@ -148,7 +148,7 @@
</label> </label>
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</button>
<button class="ui primary button set-default-project-board" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}/default">{{$.locale.Tr "repo.projects.column.set_default"}}</button> <button class="ui primary button set-default-project-board" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}/default">{{$.locale.Tr "repo.projects.column.set_default"}}</button>
</div> </div>
</div> </div>
@ -162,8 +162,8 @@
{{$.locale.Tr "repo.projects.column.deletion_desc"}} {{$.locale.Tr "repo.projects.column.deletion_desc"}}
</label> </label>
</div> </div>
<div class="text right actions"> <div class="text right actions">{{/* TODO: Convert to base/delete_modal_actions.tmpl? */}}
<div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</button>
<button class="ui red button delete-project-board" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}">{{$.locale.Tr "repo.projects.column.delete"}}</button> <button class="ui red button delete-project-board" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}">{{$.locale.Tr "repo.projects.column.delete"}}</button>
</div> </div>
</div> </div>
@ -276,16 +276,7 @@
<div class="content"> <div class="content">
<p>{{.locale.Tr "repo.projects.deletion_desc"}}</p> <p>{{.locale.Tr "repo.projects.deletion_desc"}}</p>
</div> </div>
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i>
{{.locale.Tr "modal.no"}}
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i>
{{.locale.Tr "modal.yes"}}
</div>
</div>
</div> </div>
{{end}} {{end}}

View File

@ -114,7 +114,7 @@
{{$.locale.Tr "repo.release.delete_release"}} {{$.locale.Tr "repo.release.delete_release"}}
</a> </a>
{{if .IsDraft}} {{if .IsDraft}}
<input class="ui button" type="submit" name="draft" value="{{.locale.Tr "repo.release.save_draft"}}"/> <button class="ui button" type="submit" name="draft" value="{{.locale.Tr "repo.release.save_draft"}}">{{.locale.Tr "repo.release.save_draft"}}</button>
<button class="ui primary button"> <button class="ui primary button">
{{.locale.Tr "repo.release.publish"}} {{.locale.Tr "repo.release.publish"}}
</button> </button>
@ -125,9 +125,9 @@
{{end}} {{end}}
{{else}} {{else}}
{{if not .tag_name}} {{if not .tag_name}}
<input class="ui grey button" type="submit" name="tag_only" value="{{.locale.Tr "repo.release.add_tag"}}"/> <button class="ui grey button" type="submit" name="tag_only" value="{{.locale.Tr "repo.release.add_tag"}}">{{.locale.Tr "repo.release.add_tag"}}</button>
{{end}} {{end}}
<input class="ui button" type="submit" name="draft" value="{{.locale.Tr "repo.release.save_draft"}}"/> <button class="ui button" type="submit" name="draft" value="{{.locale.Tr "repo.release.save_draft"}}">{{.locale.Tr "repo.release.save_draft"}}</button>
<button class="ui primary button"> <button class="ui primary button">
{{.locale.Tr "repo.release.publish"}} {{.locale.Tr "repo.release.publish"}}
</button> </button>

View File

@ -8,9 +8,9 @@
{{.locale.Tr "repo.settings.deploy_keys"}} {{.locale.Tr "repo.settings.deploy_keys"}}
<div class="ui right"> <div class="ui right">
{{if not .DisableSSH}} {{if not .DisableSSH}}
<div class="ui primary tiny show-panel button" data-panel="#add-deploy-key-panel">{{.locale.Tr "repo.settings.add_deploy_key"}}</div> <button class="ui primary tiny show-panel button" data-panel="#add-deploy-key-panel">{{.locale.Tr "repo.settings.add_deploy_key"}}</button>
{{else}} {{else}}
<div class="ui primary tiny button disabled">{{.locale.Tr "settings.ssh_disabled"}}</div> <button class="ui primary tiny button disabled">{{.locale.Tr "settings.ssh_disabled"}}</button>
{{end}} {{end}}
</div> </div>
</h4> </h4>
@ -85,15 +85,6 @@
<div class="content"> <div class="content">
<p>{{.locale.Tr "repo.settings.deploy_key_deletion_desc"}}</p> <p>{{.locale.Tr "repo.settings.deploy_key_deletion_desc"}}</p>
</div> </div>
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i>
{{.locale.Tr "modal.no"}}
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i>
{{.locale.Tr "modal.yes"}}
</div>
</div>
</div> </div>
{{template "base/footer" .}} {{template "base/footer" .}}

View File

@ -50,8 +50,8 @@
</p> </p>
<form class="ui form" action="{{$.Link}}/delete/{{.Oid}}" method="post"> <form class="ui form" action="{{$.Link}}/delete/{{.Oid}}" method="post">
{{$.CsrfTokenHtml}} {{$.CsrfTokenHtml}}
<div class="center actions"> <div class="center actions">{{/* TODO: Convert to base/delete_modal_actions */}}
<div class="ui basic cancel inverted button">{{$.locale.Tr "settings.cancel"}}</div> <button class="ui basic cancel inverted button">{{$.locale.Tr "settings.cancel"}}</button>
<button class="ui basic inverted yellow button">{{$.locale.Tr "modal.yes"}}</button> <button class="ui basic inverted yellow button">{{$.locale.Tr "modal.yes"}}</button>
</div> </div>
</form> </form>

View File

@ -49,9 +49,9 @@
{{ShortSha .Oid}} {{ShortSha .Oid}}
</a> </a>
{{else}} {{else}}
<span class="ui detail icon button brown disabled truncate"> <button class="ui detail icon button brown disabled truncate">
{{ShortSha .Oid}} {{ShortSha .Oid}}
</span> </button>
{{end}} {{end}}
</span> </span>
</td> </td>

View File

@ -825,7 +825,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{.locale.Tr "settings.cancel"}}</button>
<button class="ui red button">{{.locale.Tr "repo.settings.convert_confirm"}}</button> <button class="ui red button">{{.locale.Tr "repo.settings.convert_confirm"}}</button>
</div> </div>
</form> </form>
@ -856,7 +856,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{.locale.Tr "settings.cancel"}}</button>
<button class="ui red button">{{.locale.Tr "repo.settings.convert_fork_confirm"}}</button> <button class="ui red button">{{.locale.Tr "repo.settings.convert_fork_confirm"}}</button>
</div> </div>
</form> </form>
@ -892,7 +892,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{.locale.Tr "settings.cancel"}}</button>
<button class="ui red button">{{.locale.Tr "repo.settings.transfer_perform"}}</button> <button class="ui red button">{{.locale.Tr "repo.settings.transfer_perform"}}</button>
</div> </div>
</form> </form>
@ -926,7 +926,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{.locale.Tr "settings.cancel"}}</button>
<button class="ui red button">{{.locale.Tr "repo.settings.confirm_delete"}}</button> <button class="ui red button">{{.locale.Tr "repo.settings.confirm_delete"}}</button>
</div> </div>
</form> </form>
@ -958,7 +958,7 @@
</div> </div>
<div class="text right actions"> <div class="text right actions">
<div class="ui cancel button">{{.locale.Tr "settings.cancel"}}</div> <button class="ui cancel button">{{.locale.Tr "settings.cancel"}}</button>
<button class="ui red button">{{.locale.Tr "repo.settings.confirm_wiki_delete"}}</button> <button class="ui red button">{{.locale.Tr "repo.settings.confirm_wiki_delete"}}</button>
</div> </div>
</form> </form>
@ -988,10 +988,7 @@
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<input type="hidden" name="action" value="{{if .Repository.IsArchived}}unarchive{{else}}archive{{end}}"> <input type="hidden" name="action" value="{{if .Repository.IsArchived}}unarchive{{else}}archive{{end}}">
<input type="hidden" name="repo_id" value="{{.Repository.ID}}"> <input type="hidden" name="repo_id" value="{{.Repository.ID}}">
<div class="center actions"> {{template "base/delete_modal_actions" .}}
<div class="ui basic cancel inverted button">{{.locale.Tr "settings.cancel"}}</div>
<button class="ui basic inverted yellow button">{{.locale.Tr "modal.yes"}}</button>
</div>
</form> </form>
</div> </div>
{{end}} {{end}}

View File

@ -6,14 +6,5 @@
<div class="content"> <div class="content">
<p>{{.locale.Tr "repo.settings.webhook_deletion_desc"}}</p> <p>{{.locale.Tr "repo.settings.webhook_deletion_desc"}}</p>
</div> </div>
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i>
{{.locale.Tr "modal.no"}}
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i>
{{.locale.Tr "modal.yes"}}
</div>
</div>
</div> </div>

View File

@ -1,7 +1,7 @@
{{if .EscapeStatus}} {{if .EscapeStatus}}
{{if .EscapeStatus.HasInvisible}} {{if .EscapeStatus.HasInvisible}}
<div class="ui error message unicode-escape-prompt gt-tl"> <div class="ui error message unicode-escape-prompt gt-tl">
<span class="close icon hide-panel button" data-panel-closest=".message">{{svg "octicon-x" 16 "close inside"}}</span> <button class="close icon hide-panel button" data-panel-closest=".message">{{svg "octicon-x" 16 "close inside"}}</button>
<div class="header"> <div class="header">
{{$.root.locale.Tr "repo.invisible_runes_header"}} {{$.root.locale.Tr "repo.invisible_runes_header"}}
</div> </div>
@ -12,7 +12,7 @@
</div> </div>
{{else if .EscapeStatus.HasAmbiguous}} {{else if .EscapeStatus.HasAmbiguous}}
<div class="ui warning message unicode-escape-prompt gt-tl"> <div class="ui warning message unicode-escape-prompt gt-tl">
<span class="close icon hide-panel button" data-panel-closest=".message">{{svg "octicon-x" 16 "close inside"}}</span> <button class="close icon hide-panel button" data-panel-closest=".message">{{svg "octicon-x" 16 "close inside"}}</button>
<div class="header"> <div class="header">
{{$.root.locale.Tr "repo.ambiguous_runes_header"}} {{$.root.locale.Tr "repo.ambiguous_runes_header"}}
</div> </div>

View File

@ -20,9 +20,9 @@
</div> </div>
<div class="ui input"> <div class="ui input">
<input type="text" value="{{.RegistrationToken}}"> <input type="text" value="{{.RegistrationToken}}">
<div class="ui basic label button" data-clipboard-text="{{.RegistrationToken}}"> <button class="ui basic label button" aria-label="{{.locale.Tr "copy"}}" data-clipboard-text="{{.RegistrationToken}}">
{{svg "octicon-copy" 14}} {{svg "octicon-copy" 14}}
</div> </button>
</div> </div>
<div class="divider"></div> <div class="divider"></div>
<div class="item"> <div class="item">

View File

@ -1,7 +1,7 @@
<h4 class="ui top attached header"> <h4 class="ui top attached header">
{{.locale.Tr "secrets.secrets"}} {{.locale.Tr "secrets.secrets"}}
<div class="ui right"> <div class="ui right">
<div class="ui primary tiny show-panel button" data-panel="#add-secret-panel">{{.locale.Tr "secrets.creation"}}</div> <button class="ui primary tiny show-panel button" data-panel="#add-secret-panel">{{.locale.Tr "secrets.creation"}}</button>
</div> </div>
</h4> </h4>
<div class="ui attached segment"> <div class="ui attached segment">

View File

@ -493,6 +493,12 @@
"name": "username", "name": "username",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "boolean",
"description": "purge the user from the system completely",
"name": "purge",
"in": "query"
} }
], ],
"responses": { "responses": {
@ -707,6 +713,9 @@
"201": { "201": {
"$ref": "#/responses/Repository" "$ref": "#/responses/Repository"
}, },
"400": {
"$ref": "#/responses/error"
},
"403": { "403": {
"$ref": "#/responses/forbidden" "$ref": "#/responses/forbidden"
}, },
@ -1920,6 +1929,9 @@
"201": { "201": {
"$ref": "#/responses/Repository" "$ref": "#/responses/Repository"
}, },
"400": {
"$ref": "#/responses/error"
},
"403": { "403": {
"$ref": "#/responses/forbidden" "$ref": "#/responses/forbidden"
}, },
@ -2114,6 +2126,7 @@
"pub", "pub",
"pypi", "pypi",
"rubygems", "rubygems",
"swift",
"vagrant" "vagrant"
], ],
"type": "string", "type": "string",
@ -13375,6 +13388,9 @@
"201": { "201": {
"$ref": "#/responses/Repository" "$ref": "#/responses/Repository"
}, },
"400": {
"$ref": "#/responses/error"
},
"409": { "409": {
"description": "The repository with the same name already exists." "description": "The repository with the same name already exists."
}, },

View File

@ -23,7 +23,7 @@
<input type="hidden" name="scope" value="{{.Scope}}"> <input type="hidden" name="scope" value="{{.Scope}}">
<input type="hidden" name="nonce" value="{{.Nonce}}"> <input type="hidden" name="nonce" value="{{.Nonce}}">
<input type="hidden" name="redirect_uri" value="{{.RedirectURI}}"> <input type="hidden" name="redirect_uri" value="{{.RedirectURI}}">
<input type="submit" id="authorize-app" value="{{.locale.Tr "auth.authorize_application"}}" class="ui red inline button"/> <button type="submit" id="authorize-app" value="{{.locale.Tr "auth.authorize_application"}}" class="ui red inline button">{{.locale.Tr "auth.authorize_application"}}</button>
<a href="{{.RedirectURI}}" class="ui basic primary inline button">Cancel</a> <a href="{{.RedirectURI}}" class="ui basic primary inline button">Cancel</a>
</form> </form>
</div> </div>

View File

@ -17,6 +17,6 @@
</div> </div>
<div class="actions"> <div class="actions">
<button onclick="window.location.reload()" class="success ui button gt-hidden webauthn_error_timeout">{{.locale.Tr "webauthn_reload"}}</button> <button onclick="window.location.reload()" class="success ui button gt-hidden webauthn_error_timeout">{{.locale.Tr "webauthn_reload"}}</button>
<div class="ui cancel button">{{.locale.Tr "cancel"}}</div> <button class="ui cancel button">{{.locale.Tr "cancel"}}</button>
</div> </div>
</div> </div>

View File

@ -151,9 +151,9 @@
<input id="password-confirmation" name="password" type="password" autocomplete="off" required> <input id="password-confirmation" name="password" type="password" autocomplete="off" required>
</div> </div>
<div class="field"> <div class="field">
<div class="ui red button delete-button" data-modal-id="delete-account" data-type="form" data-form="#delete-form"> <button class="ui red button delete-button" data-modal-id="delete-account" data-type="form" data-form="#delete-form">
{{.locale.Tr "settings.confirm_delete_account"}} {{.locale.Tr "settings.confirm_delete_account"}}
</div> </button>
<a href="{{AppSubUrl}}/user/forgot_password?email={{.Email}}">{{.locale.Tr "auth.forgot_password"}}</a> <a href="{{AppSubUrl}}/user/forgot_password?email={{.Email}}">{{.locale.Tr "auth.forgot_password"}}</a>
</div> </div>
</form> </form>

View File

@ -276,15 +276,16 @@
<div class="content"> <div class="content">
<p>{{.locale.Tr "settings.access_token_deletion_desc"}}</p> <p>{{.locale.Tr "settings.access_token_deletion_desc"}}</p>
</div> </div>
<div class="actions">
<div class="ui cancel button"> <div class="actions">{{/* TODO: Convert to base/delete_modal_actions.tmpl */}}
<i class="remove icon"></i> <button class="ui green basic inverted cancel button">
{{svg "octicon-x"}}
{{.locale.Tr "settings.access_token_deletion_cancel_action"}} {{.locale.Tr "settings.access_token_deletion_cancel_action"}}
</div> </button>
<div class="ui red basic inverted ok button"> <button class="ui red basic inverted ok button">
<i class="checkmark icon"></i> {{svg "octicon-check"}}
{{.locale.Tr "settings.access_token_deletion_confirm_action"}} {{.locale.Tr "settings.access_token_deletion_confirm_action"}}
</div> </button>
</div> </div>
</div> </div>

View File

@ -1,7 +1,7 @@
<h4 class="ui top attached header"> <h4 class="ui top attached header">
{{.locale.Tr "settings.manage_gpg_keys"}} {{.locale.Tr "settings.manage_gpg_keys"}}
<div class="ui right"> <div class="ui right">
<div class="ui primary tiny show-panel button" data-panel="#add-gpg-key-panel">{{.locale.Tr "settings.add_key"}}</div> <button class="ui primary tiny show-panel button" data-panel="#add-gpg-key-panel">{{.locale.Tr "settings.add_key"}}</button>
</div> </div>
</h4> </h4>
<div class="ui attached segment"> <div class="ui attached segment">

View File

@ -3,9 +3,9 @@
{{.locale.Tr "settings.manage_ssh_principals"}} {{.locale.Tr "settings.manage_ssh_principals"}}
<div class="ui right"> <div class="ui right">
{{if not .DisableSSH}} {{if not .DisableSSH}}
<div class="ui primary tiny show-panel button" data-panel="#add-ssh-principal-panel">{{.locale.Tr "settings.add_new_principal"}}</div> <button class="ui primary tiny show-panel button" data-panel="#add-ssh-principal-panel">{{.locale.Tr "settings.add_new_principal"}}</button>
{{else}} {{else}}
<div class="ui primary tiny button disabled">{{.locale.Tr "settings.ssh_disabled"}}</div> <button class="ui primary tiny button disabled">{{.locale.Tr "settings.ssh_disabled"}}</button>
{{end}} {{end}}
</div> </div>
</h4> </h4>

View File

@ -2,11 +2,11 @@
{{.locale.Tr "settings.manage_ssh_keys"}} {{.locale.Tr "settings.manage_ssh_keys"}}
<div class="ui right"> <div class="ui right">
{{if not .DisableSSH}} {{if not .DisableSSH}}
<div id="add-ssh-button" class="ui primary tiny show-panel button" data-panel="#add-ssh-key-panel"> <button id="add-ssh-button" class="ui primary tiny show-panel button" data-panel="#add-ssh-key-panel">
{{.locale.Tr "settings.add_key"}} {{.locale.Tr "settings.add_key"}}
</div> </button>
{{else}} {{else}}
<div class="ui primary tiny button disabled">{{.locale.Tr "settings.ssh_disabled"}}</div> <button class="ui primary tiny button disabled">{{.locale.Tr "settings.ssh_disabled"}}</button>
{{end}} {{end}}
</div> </div>
</h4> </h4>

View File

@ -50,16 +50,7 @@
{{$.CsrfTokenHtml}} {{$.CsrfTokenHtml}}
<input type="hidden" name="id" value="{{$dir}}"> <input type="hidden" name="id" value="{{$dir}}">
<input type="hidden" name="action" value="adopt"> <input type="hidden" name="action" value="adopt">
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i>
{{$.locale.Tr "modal.no"}}
</div>
<button class="ui green basic inverted ok button">
<i class="checkmark icon"></i>
{{$.locale.Tr "modal.yes"}}
</button>
</div>
</form> </form>
</div> </div>
{{end}} {{end}}
@ -77,16 +68,7 @@
{{$.CsrfTokenHtml}} {{$.CsrfTokenHtml}}
<input type="hidden" name="id" value="{{$dir}}"> <input type="hidden" name="id" value="{{$dir}}">
<input type="hidden" name="action" value="delete"> <input type="hidden" name="action" value="delete">
<div class="actions"> {{template "base/delete_modal_actions" .}}
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i>
{{$.locale.Tr "modal.no"}}
</div>
<button class="ui green basic inverted ok button">
<i class="checkmark icon"></i>
{{$.locale.Tr "modal.yes"}}
</button>
</div>
</form> </form>
</div> </div>
{{end}} {{end}}

View File

@ -13,7 +13,7 @@
<form class="ui form" action="{{AppSubUrl}}/user/settings/security/two_factor/disable" method="post" enctype="multipart/form-data" id="disable-form"> <form class="ui form" action="{{AppSubUrl}}/user/settings/security/two_factor/disable" method="post" enctype="multipart/form-data" id="disable-form">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<p>{{.locale.Tr "settings.twofa_disable_note"}}</p> <p>{{.locale.Tr "settings.twofa_disable_note"}}</p>
<div class="ui red button delete-button" data-modal-id="disable-twofa" data-type="form" data-form="#disable-form">{{$.locale.Tr "settings.twofa_disable"}}</div> <button class="ui red button delete-button" data-modal-id="disable-twofa" data-type="form" data-form="#disable-form">{{$.locale.Tr "settings.twofa_disable"}}</button>
</form> </form>
{{else}} {{else}}
<p>{{.locale.Tr "settings.twofa_not_enrolled"}}</p> <p>{{.locale.Tr "settings.twofa_not_enrolled"}}</p>

View File

@ -0,0 +1,326 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"archive/zip"
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"strings"
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
swift_module "code.gitea.io/gitea/modules/packages/swift"
"code.gitea.io/gitea/modules/setting"
swift_router "code.gitea.io/gitea/routers/api/packages/swift"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
)
func TestPackageSwift(t *testing.T) {
defer tests.PrepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
packageScope := "test-scope"
packageName := "test_package"
packageID := packageScope + "." + packageName
packageVersion := "1.0.3"
packageAuthor := "KN4CK3R"
packageDescription := "Gitea Test Package"
packageRepositoryURL := "https://gitea.io/gitea/gitea"
contentManifest1 := "// swift-tools-version:5.7\n//\n// Package.swift"
contentManifest2 := "// swift-tools-version:5.6\n//\n// Package@swift-5.6.swift"
url := fmt.Sprintf("/api/packages/%s/swift", user.Name)
t.Run("CheckAcceptMediaType", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
for _, sub := range []string{
"/scope/package",
"/scope/package.json",
"/scope/package/1.0.0",
"/scope/package/1.0.0.json",
"/scope/package/1.0.0.zip",
"/scope/package/1.0.0/Package.swift",
"/identifiers",
} {
req := NewRequest(t, "GET", url+sub)
req.Header.Add("Accept", "application/unknown")
resp := MakeRequest(t, req, http.StatusBadRequest)
assert.Equal(t, "1", resp.Header().Get("Content-Version"))
assert.Equal(t, "application/problem+json", resp.Header().Get("Content-Type"))
}
req := NewRequestWithBody(t, "PUT", url+"/scope/package/1.0.0", strings.NewReader(""))
req = AddBasicAuthHeader(req, user.Name)
req.Header.Add("Accept", "application/unknown")
resp := MakeRequest(t, req, http.StatusBadRequest)
assert.Equal(t, "1", resp.Header().Get("Content-Version"))
assert.Equal(t, "application/problem+json", resp.Header().Get("Content-Type"))
})
t.Run("Upload", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
uploadPackage := func(t *testing.T, url string, expectedStatus int, sr io.Reader, metadata string) {
var body bytes.Buffer
mpw := multipart.NewWriter(&body)
part, _ := mpw.CreateFormFile("source-archive", "source-archive.zip")
io.Copy(part, sr)
if metadata != "" {
mpw.WriteField("metadata", metadata)
}
mpw.Close()
req := NewRequestWithBody(t, "PUT", url, &body)
req.Header.Add("Content-Type", mpw.FormDataContentType())
req.Header.Add("Accept", swift_router.AcceptJSON)
req = AddBasicAuthHeader(req, user.Name)
MakeRequest(t, req, expectedStatus)
}
createArchive := func(files map[string]string) *bytes.Buffer {
var buf bytes.Buffer
zw := zip.NewWriter(&buf)
for filename, content := range files {
w, _ := zw.Create(filename)
w.Write([]byte(content))
}
zw.Close()
return &buf
}
for _, triple := range []string{"/sc_ope/package/1.0.0", "/scope/pack~age/1.0.0", "/scope/package/1_0.0"} {
req := NewRequestWithBody(t, "PUT", url+triple, bytes.NewReader([]byte{}))
req = AddBasicAuthHeader(req, user.Name)
resp := MakeRequest(t, req, http.StatusBadRequest)
assert.Equal(t, "1", resp.Header().Get("Content-Version"))
assert.Equal(t, "application/problem+json", resp.Header().Get("Content-Type"))
}
uploadURL := fmt.Sprintf("%s/%s/%s/%s", url, packageScope, packageName, packageVersion)
req := NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{}))
MakeRequest(t, req, http.StatusUnauthorized)
uploadPackage(
t,
uploadURL,
http.StatusCreated,
createArchive(map[string]string{
"Package.swift": contentManifest1,
"Package@swift-5.6.swift": contentManifest2,
}),
`{"name":"`+packageName+`","version":"`+packageVersion+`","description":"`+packageDescription+`","codeRepository":"`+packageRepositoryURL+`","author":{"givenName":"`+packageAuthor+`"},"repositoryURLs":["`+packageRepositoryURL+`"]}`,
)
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeSwift)
assert.NoError(t, err)
assert.Len(t, pvs, 1)
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
assert.NoError(t, err)
assert.NotNil(t, pd.SemVer)
assert.Equal(t, packageID, pd.Package.Name)
assert.Equal(t, packageVersion, pd.Version.Version)
assert.IsType(t, &swift_module.Metadata{}, pd.Metadata)
metadata := pd.Metadata.(*swift_module.Metadata)
assert.Equal(t, packageDescription, metadata.Description)
assert.Len(t, metadata.Manifests, 2)
assert.Equal(t, contentManifest1, metadata.Manifests[""].Content)
assert.Equal(t, contentManifest2, metadata.Manifests["5.6"].Content)
assert.Len(t, pd.VersionProperties, 1)
assert.Equal(t, packageRepositoryURL, pd.VersionProperties.GetByName(swift_module.PropertyRepositoryURL))
pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
assert.NoError(t, err)
assert.Len(t, pfs, 1)
assert.Equal(t, fmt.Sprintf("%s-%s.zip", packageName, packageVersion), pfs[0].Name)
assert.True(t, pfs[0].IsLead)
uploadPackage(
t,
uploadURL,
http.StatusConflict,
createArchive(map[string]string{
"Package.swift": contentManifest1,
}),
"",
)
})
t.Run("Download", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/%s.zip", url, packageScope, packageName, packageVersion))
req = AddBasicAuthHeader(req, user.Name)
req.Header.Add("Accept", swift_router.AcceptZip)
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "1", resp.Header().Get("Content-Version"))
assert.Equal(t, "application/zip", resp.Header().Get("Content-Type"))
pv, err := packages.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages.TypeSwift, packageID, packageVersion)
assert.NotNil(t, pv)
assert.NoError(t, err)
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pv)
assert.NoError(t, err)
assert.Equal(t, "sha256="+pd.Files[0].Blob.HashSHA256, resp.Header().Get("Digest"))
})
t.Run("EnumeratePackageVersions", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s", url, packageScope, packageName))
req = AddBasicAuthHeader(req, user.Name)
req.Header.Add("Accept", swift_router.AcceptJSON)
resp := MakeRequest(t, req, http.StatusOK)
versionURL := setting.AppURL + url[1:] + fmt.Sprintf("/%s/%s/%s", packageScope, packageName, packageVersion)
assert.Equal(t, "1", resp.Header().Get("Content-Version"))
assert.Equal(t, fmt.Sprintf(`<%s>; rel="latest-version"`, versionURL), resp.Header().Get("Link"))
body := resp.Body.String()
var result *swift_router.EnumeratePackageVersionsResponse
DecodeJSON(t, resp, &result)
assert.Len(t, result.Releases, 1)
assert.Contains(t, result.Releases, packageVersion)
assert.Equal(t, versionURL, result.Releases[packageVersion].URL)
req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s.json", url, packageScope, packageName))
req = AddBasicAuthHeader(req, user.Name)
resp = MakeRequest(t, req, http.StatusOK)
assert.Equal(t, body, resp.Body.String())
})
t.Run("PackageVersionMetadata", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/%s", url, packageScope, packageName, packageVersion))
req = AddBasicAuthHeader(req, user.Name)
req.Header.Add("Accept", swift_router.AcceptJSON)
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "1", resp.Header().Get("Content-Version"))
body := resp.Body.String()
var result *swift_router.PackageVersionMetadataResponse
DecodeJSON(t, resp, &result)
pv, err := packages.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages.TypeSwift, packageID, packageVersion)
assert.NotNil(t, pv)
assert.NoError(t, err)
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pv)
assert.NoError(t, err)
assert.Equal(t, packageID, result.ID)
assert.Equal(t, packageVersion, result.Version)
assert.Len(t, result.Resources, 1)
assert.Equal(t, "source-archive", result.Resources[0].Name)
assert.Equal(t, "application/zip", result.Resources[0].Type)
assert.Equal(t, pd.Files[0].Blob.HashSHA256, result.Resources[0].Checksum)
assert.Equal(t, "SoftwareSourceCode", result.Metadata.Type)
assert.Equal(t, packageName, result.Metadata.Name)
assert.Equal(t, packageVersion, result.Metadata.Version)
assert.Equal(t, packageDescription, result.Metadata.Description)
assert.Equal(t, "Swift", result.Metadata.ProgrammingLanguage.Name)
assert.Equal(t, packageAuthor, result.Metadata.Author.GivenName)
req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/%s.json", url, packageScope, packageName, packageVersion))
req = AddBasicAuthHeader(req, user.Name)
resp = MakeRequest(t, req, http.StatusOK)
assert.Equal(t, body, resp.Body.String())
})
t.Run("DownloadManifest", func(t *testing.T) {
manifestURL := fmt.Sprintf("%s/%s/%s/%s/Package.swift", url, packageScope, packageName, packageVersion)
t.Run("Default", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", manifestURL)
req = AddBasicAuthHeader(req, user.Name)
req.Header.Add("Accept", swift_router.AcceptSwift)
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "1", resp.Header().Get("Content-Version"))
assert.Equal(t, "text/x-swift", resp.Header().Get("Content-Type"))
assert.Equal(t, contentManifest1, resp.Body.String())
})
t.Run("DifferentVersion", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", manifestURL+"?swift-version=5.6")
req = AddBasicAuthHeader(req, user.Name)
resp := MakeRequest(t, req, http.StatusOK)
assert.Equal(t, "1", resp.Header().Get("Content-Version"))
assert.Equal(t, "text/x-swift", resp.Header().Get("Content-Type"))
assert.Equal(t, contentManifest2, resp.Body.String())
req = NewRequest(t, "GET", manifestURL+"?swift-version=5.6.0")
req = AddBasicAuthHeader(req, user.Name)
MakeRequest(t, req, http.StatusOK)
})
t.Run("Redirect", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", manifestURL+"?swift-version=1.0")
req = AddBasicAuthHeader(req, user.Name)
resp := MakeRequest(t, req, http.StatusSeeOther)
assert.Equal(t, "1", resp.Header().Get("Content-Version"))
assert.Equal(t, setting.AppURL+url[1:]+fmt.Sprintf("/%s/%s/%s/Package.swift", packageScope, packageName, packageVersion), resp.Header().Get("Location"))
})
})
t.Run("LookupPackageIdentifiers", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
req := NewRequest(t, "GET", url+"/identifiers")
req.Header.Add("Accept", swift_router.AcceptJSON)
resp := MakeRequest(t, req, http.StatusBadRequest)
assert.Equal(t, "1", resp.Header().Get("Content-Version"))
assert.Equal(t, "application/problem+json", resp.Header().Get("Content-Type"))
req = NewRequest(t, "GET", url+"/identifiers?url=https://unknown.host/")
MakeRequest(t, req, http.StatusNotFound)
req = NewRequest(t, "GET", url+"/identifiers?url="+packageRepositoryURL)
req.Header.Add("Accept", swift_router.AcceptJSON)
resp = MakeRequest(t, req, http.StatusOK)
var result *swift_router.LookupPackageIdentifiersResponse
DecodeJSON(t, resp, &result)
assert.Len(t, result.Identifiers, 1)
assert.Equal(t, packageID, result.Identifiers[0])
})
}

View File

@ -201,7 +201,7 @@ func PrepareTestEnv(t testing.TB, skip ...int) func() {
lfsFixtures, err := storage.NewStorage("", storage.LocalStorageConfig{Path: path.Join(filepath.Dir(setting.AppPath), "tests/gitea-lfs-meta")}) lfsFixtures, err := storage.NewStorage("", storage.LocalStorageConfig{Path: path.Join(filepath.Dir(setting.AppPath), "tests/gitea-lfs-meta")})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, storage.Clean(storage.LFS)) assert.NoError(t, storage.Clean(storage.LFS))
assert.NoError(t, lfsFixtures.IterateObjects(func(path string, _ storage.Object) error { assert.NoError(t, lfsFixtures.IterateObjects("", func(path string, _ storage.Object) error {
_, err := storage.Copy(storage.LFS, path, lfsFixtures, path) _, err := storage.Copy(storage.LFS, path, lfsFixtures, path)
return err return err
})) }))
@ -258,7 +258,7 @@ func ResetFixtures(t *testing.T) {
lfsFixtures, err := storage.NewStorage("", storage.LocalStorageConfig{Path: path.Join(filepath.Dir(setting.AppPath), "tests/gitea-lfs-meta")}) lfsFixtures, err := storage.NewStorage("", storage.LocalStorageConfig{Path: path.Join(filepath.Dir(setting.AppPath), "tests/gitea-lfs-meta")})
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, storage.Clean(storage.LFS)) assert.NoError(t, storage.Clean(storage.LFS))
assert.NoError(t, lfsFixtures.IterateObjects(func(path string, _ storage.Object) error { assert.NoError(t, lfsFixtures.IterateObjects("", func(path string, _ storage.Object) error {
_, err := storage.Copy(storage.LFS, path, lfsFixtures, path) _, err := storage.Copy(storage.LFS, path, lfsFixtures, path)
return err return err
})) }))

View File

@ -43,8 +43,7 @@
<div class="job-step-container"> <div class="job-step-container">
<div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i"> <div class="job-step-section" v-for="(jobStep, i) in currentJob.steps" :key="i">
<div class="job-step-summary" @click.stop="toggleStepLogs(i)"> <div class="job-step-summary" @click.stop="toggleStepLogs(i)">
<SvgIcon name="octicon-chevron-down" class="gt-mr-3" v-show="currentJobStepsStates[i].expanded"/> <SvgIcon :name="currentJobStepsStates[i].expanded ? 'octicon-chevron-down': 'octicon-chevron-right'" class="gt-mr-3"/>
<SvgIcon name="octicon-chevron-right" class="gt-mr-3" v-show="!currentJobStepsStates[i].expanded"/>
<ActionRunStatus :status="jobStep.status" class="gt-mr-3"/> <ActionRunStatus :status="jobStep.status" class="gt-mr-3"/>

View File

@ -198,7 +198,8 @@ export function initAdminCommon() {
break; break;
} }
}); });
$('#delete-selection').on('click', function () { $('#delete-selection').on('click', function (e) {
e.preventDefault();
const $this = $(this); const $this = $(this);
$this.addClass('loading disabled'); $this.addClass('loading disabled');
const ids = []; const ids = [];

Some files were not shown because too many files have changed in this diff Show More