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") {
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()
if err != nil {
return err
@ -351,7 +351,7 @@ func runDump(ctx *cli.Context) error {
if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-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()
if err != nil {
return err
@ -364,7 +364,7 @@ func runDump(ctx *cli.Context) error {
if ctx.IsSet("skip-package-data") && ctx.Bool("skip-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()
if err != nil {
return err

View File

@ -2272,6 +2272,17 @@ ROUTER = console
;PULL = 300
;GC = 60
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Git Reflog timeout in days
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[git.reflog]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;ENABLED = true
;EXPIRATION = 90
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[mirror]
@ -2505,6 +2516,8 @@ ROUTER = console
;LIMIT_SIZE_PYPI = -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 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`)
;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_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`)
- `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_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_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`)
## 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` |
| [PyPI]({{< relref "doc/packages/pypi.en-us.md" >}}) | Python | `pip`, `twine` |
| [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` |
**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)
}
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 {
_, err := db.GetEngine(ctx).ID(repo.ID).
SetExpr("num_action_runs",

View File

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

View File

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

View File

@ -62,7 +62,8 @@ func RepositoryListOfMap(repoMap map[int64]*Repository) RepositoryList {
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 {
return nil
}
@ -107,11 +108,6 @@ func (repos RepositoryList) loadAttributes(ctx context.Context) error {
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
type SearchRepoOptions struct {
db.ListOptions
@ -547,7 +543,7 @@ func SearchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, c
}
if loadAttributes {
if err := repos.loadAttributes(ctx); err != nil {
if err := repos.LoadAttributes(ctx); err != nil {
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)
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()
totalCount++

View File

@ -201,6 +201,23 @@ func InitFull(ctx context.Context) (err error) {
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)
func syncGitConfig() (err error) {
if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil {
@ -224,6 +241,16 @@ func syncGitConfig() (err error) {
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 err := configSet("receive.advertisePushOptions", "true"); err != nil {
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

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

View File

@ -39,6 +39,7 @@ var (
LimitSizePub int64
LimitSizePyPI int64
LimitSizeRubyGems int64
LimitSizeSwift int64
LimitSizeVagrant int64
}{
Enabled: true,
@ -81,6 +82,7 @@ func loadPackagesFrom(rootCfg ConfigProvider) {
Packages.LimitSizePub = mustBytes(sec, "LIMIT_SIZE_PUB")
Packages.LimitSizePyPI = mustBytes(sec, "LIMIT_SIZE_PYPI")
Packages.LimitSizeRubyGems = mustBytes(sec, "LIMIT_SIZE_RUBYGEMS")
Packages.LimitSizeSwift = mustBytes(sec, "LIMIT_SIZE_SWIFT")
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)
}
func (s discardStorage) IterateObjects(_ func(string, Object) error) error {
func (s discardStorage) IterateObjects(_ string, _ func(string, Object) error) error {
return fmt.Errorf("%s", s)
}

View File

@ -42,7 +42,7 @@ func Test_discardStorage(t *testing.T) {
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))
}
})

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
func (l *LocalStorage) IterateObjects(fn func(path string, obj Object) error) error {
return filepath.WalkDir(l.dir, func(path string, d os.DirEntry, err error) error {
func (l *LocalStorage) IterateObjects(prefix string, fn func(path string, obj Object) 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 {
return err
}

View File

@ -4,6 +4,10 @@
package storage
import (
"bytes"
"context"
"os"
"path/filepath"
"testing"
"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
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{}
lobjectCtx, cancel := context.WithCancel(m.ctx)
defer cancel()
basePath := m.basePath
if prefix != "" {
basePath = m.buildMinioPath(prefix)
}
for mObjInfo := range m.client.ListObjects(lobjectCtx, m.bucket, minio.ListObjectsOptions{
Prefix: m.basePath,
Prefix: basePath,
Recursive: true,
}) {
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 {
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 {
return convertMinioErr(err)
}

View File

@ -65,7 +65,7 @@ type ObjectStorage interface {
Stat(path string) (os.FileInfo, error)
Delete(path string) 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
@ -87,7 +87,7 @@ func Copy(dstStorage ObjectStorage, dstPath string, srcStorage ObjectStorage, sr
// Clean delete all the objects in this storage
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()
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_line_characters = Max Diff Characters (for a single line)
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_migrate_timeout = Migration 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.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>.
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.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

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_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
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.
enable_update_checker=Habilitar Verificador de Atualizações
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
search=Pesquisar
code=Código
search.type.tooltip=Tipo de pesquisa
search.fuzzy=Similar
search.fuzzy.tooltip=Incluir resultados que sejam próximos ao termo de busca
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_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.
repos_none=Você não possui nenhum repositório
@ -1231,6 +1234,7 @@ projects.column.color=Colorido
projects.open=Abrir
projects.close=Fechar
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.text_only=Somente texto
@ -1399,6 +1403,7 @@ issues.label_title=Nome da etiqueta
issues.label_description=Descrição da etiqueta
issues.label_color=Cor da etiqueta
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_count=%d etiquetas
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_step2_desc=Faça merge das alterações e atualize no Gitea.
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_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.search_repo=Pesquisar no repositório...
search.type.tooltip=Tipo de pesquisa
search.fuzzy=Aproximada
search.fuzzy.tooltip=Incluir resultados que sejam próximos ao termo de busca
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_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_desc=Será incluído como header de autorização para solicitações quando estiver presente. Exemplos: %s.
settings.active=Ativo
settings.active_helper=Informações sobre eventos disparados serão enviadas para esta URL do webhook.
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.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.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_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 '\;'):
@ -2132,6 +2141,7 @@ settings.add_protected_branch=Habilitar proteção
settings.delete_protected_branch=Desabilitar proteção
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_failed=Removendo regra de proteção de branch '%s' falhou.
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.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.no_protected_branch=Não há branches protegidos.
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.tags=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.target=Destino
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.content=Conteúdo
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.update_checker=Verificador de atualização
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.new_account=Criar conta de usuário
@ -2658,6 +2676,7 @@ repos.size=Tamanho
packages.package_manage_panel=Gerenciamento de Pacotes
packages.total_size=Tamanho Total: %s
packages.unreferenced_size=Tamanho Não Referenciado: %s
packages.owner=Proprietário
packages.creator=Criador
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_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_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.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
@ -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.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.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.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_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>`
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>
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>
@ -3148,10 +3172,12 @@ dependency.id=ID
dependency.version=Versão
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.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.documentation_site=Site da Documentação
chef.registry=Configure este registro em seu arquivo <code>~/.chef/config.rb</code>:
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.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>.
@ -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.success=O pacote foi excluído.
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.add=Adicionar 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.none=A regra de limpeza não corresponde a nenhum pacote.
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.count=Manter o mais recente
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.chef.title=Registro Chef
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=Segredos
@ -3253,6 +3290,8 @@ none=Não há segredos ainda.
value=Valor
name=Nome
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.failed=Falha ao adicionar segredo.
deletion=Excluir segredo
@ -3274,6 +3313,10 @@ status.cancelled=Cancelado
status.skipped=Ignorado
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.id=ID
runners.name=Nome
@ -3281,21 +3324,36 @@ runners.owner_type=Tipo
runners.description=Descrição
runners.labels=Rótulos
runners.last_online=Última Vez Online
runners.agent_labels=Etiquetas do Agente
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.runner_title=Runner
runners.task_list=Tarefas recentes neste runner
runners.task_list.run=Executar
runners.task_list.status=Status
runners.task_list.repository=Repositório
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_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.idle=Inativo
runners.status.active=Ativo
runners.status.offline=Offiline
runs.all_workflows=Todos os Workflows
runs.open_tab=%d Aberto
runs.closed_tab=%d Fechado
runs.commit=Commit
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/pypi"
"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/services/auth"
context_service "code.gitea.io/gitea/services/context"
@ -375,6 +376,41 @@ func CommonRoutes(ctx gocontext.Context) *web.Route {
r.Delete("/yank", rubygems.DeletePackage)
}, reqPackageAccess(perm.AccessModeWrite))
}, 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("/authenticate", func() {
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:
// "201":
// "$ref": "#/responses/Repository"
// "400":
// "$ref": "#/responses/error"
// "403":
// "$ref": "#/responses/forbidden"
// "404":

View File

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

View File

@ -40,7 +40,7 @@ func ListPackages(ctx *context.APIContext) {
// in: query
// description: package type filter
// 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
// in: query
// 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 == "" {
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{
Name: opt.Name,
Description: opt.Description,
@ -283,6 +290,8 @@ func Create(ctx *context.APIContext) {
// responses:
// "201":
// "$ref": "#/responses/Repository"
// "400":
// "$ref": "#/responses/error"
// "409":
// description: The repository with the same name already exists.
// "422":
@ -464,6 +473,8 @@ func CreateOrgRepo(ctx *context.APIContext) {
// responses:
// "201":
// "$ref": "#/responses/Repository"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
// "403":

View File

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

View File

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

View File

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

View File

@ -21,10 +21,13 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
}
run := job.Run
if run.Event != webhook_module.HookEventPush {
return nil
}
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)
@ -40,16 +43,38 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
return fmt.Errorf("head commit is missing in event payload")
}
creator, err := user_model.GetUserByID(ctx, payload.Pusher.ID)
sha = payload.HeadCommit.ID
creatorID = payload.Pusher.ID
case webhook_module.HookEventPullRequest:
payload, err := run.GetPullRequestEventPayload()
if err != nil {
return fmt.Errorf("GetUserByID: %w", err)
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
}
repo := run.Repo
sha := payload.HeadCommit.ID
ctxname := job.Name
state := toCommitStatus(job.Status)
creator, err := user_model.GetUserByID(ctx, creatorID)
if err != nil {
return fmt.Errorf("GetUserByID: %w", err)
}
if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{}); err == nil {
for _, v := range statuses {
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{
Repo: repo,
SHA: payload.HeadCommit.ID,
SHA: sha,
Creator: creator,
CommitStatus: &git_model.CommitStatus{
SHA: sha,
TargetURL: run.Link(),
Description: "",
Context: ctxname,
CreatorID: payload.Pusher.ID,
CreatorID: creatorID,
State: state,
},
}); err != nil {

View File

@ -15,7 +15,7 @@ import (
type PackageCleanupRuleForm struct {
ID int64
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)"`
KeepPattern string `binding:"RegexPattern"`
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
case packages_model.TypeRubyGems:
typeSpecificSize = setting.Packages.LimitSizeRubyGems
case packages_model.TypeSwift:
typeSpecificSize = setting.Packages.LimitSizeSwift
case packages_model.TypeVagrant:
typeSpecificSize = setting.Packages.LimitSizeVagrant
}

View File

@ -433,7 +433,7 @@
<div class="field">
<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>
</form>
</div>

View File

@ -331,7 +331,19 @@
<dd>{{.Git.MaxGitDiffFiles}}</dd>
<dt>{{.locale.Tr "admin.config.git_gc_args"}}</dt>
<dd><code>{{.Git.GCArgs}}</code></dd>
<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>
<dd>{{.Git.Timeout.Migrate}} {{.locale.Tr "tool.raw_seconds"}}</dd>
<dt>{{.locale.Tr "admin.config.git_mirror_timeout"}}</dt>

View File

@ -78,7 +78,7 @@
{{.locale.Tr "admin.emails.change_email_header"}}
</div>
<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">
{{$.CsrfTokenHtml}}
@ -93,11 +93,9 @@
<input type="hidden" id="form-primary" name="primary" value="" required>
<input type="hidden" id="form-activate" name="activate" value="" required>
<div class="center 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 class="center">
{{template "base/delete_modal_actions" .}}
</div>
</form>
</div>
</div>

View File

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

View File

@ -43,16 +43,7 @@
<input type="hidden" name="action" value="adopt">
<input type="hidden" name="q" value="{{$.Keyword}}">
<input type="hidden" name="page" value="{{$.CurrentPage}}">
<div class="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>
{{template "base/delete_modal_actions" .}}
</form>
</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>
@ -70,16 +61,7 @@
<input type="hidden" name="action" value="delete">
<input type="hidden" name="q" value="{{$.Keyword}}">
<input type="hidden" name="page" value="{{$.CurrentPage}}">
<div class="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>
{{template "base/delete_modal_actions" .}}
</form>
</div>
</div>

View File

@ -151,7 +151,7 @@
<div class="field">
<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>
</form>
</div>
@ -189,7 +189,7 @@
<div class="field">
<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>
</form>
</div>
@ -213,16 +213,7 @@
</div>
<p class="help">{{.locale.Tr "admin.users.purge_help"}}</p>
</div>
<div class="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>
{{template "base/delete_modal_actions" .}}
</form>
</div>
{{template "base/footer" .}}

View File

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

View File

@ -19,9 +19,9 @@
<label for="org_name">{{.locale.Tr "org.org_name_holder"}}</label>
<input id="org_name" name="org_name" value="" autocomplete="off" autofocus required>
</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"}}
</div>
</button>
</form>
</div>
</div>

View File

@ -11,7 +11,7 @@
</div>
<div class="right floated three wide column">
<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>

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">
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="delete">
<div class="text right actions">
<div class="ui cancel button">{{.locale.Tr "cancel"}}</div>
<button class="ui red button">{{.locale.Tr "ok"}}</button>
</div>
{{template "base/delete_modal_actions" .}}
</form>
</div>
</div>

View File

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

View File

@ -84,15 +84,6 @@
<div class="content">
<p>{{.locale.Tr "repo.projects.deletion_desc"}}</p>
</div>
<div class="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>
{{template "base/delete_modal_actions" .}}
</div>
{{end}}

View File

@ -29,7 +29,7 @@
</div>
<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>
</div>
</form>
@ -127,7 +127,7 @@
</div>
<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>
</div>
</form>
@ -144,7 +144,7 @@
</label>
</div>
<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>
</div>
</div>
@ -158,8 +158,8 @@
{{$.locale.Tr "repo.projects.column.deletion_desc"}}
</label>
</div>
<div class="text right actions">
<div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div>
<div class="text right actions">{{/* TODO: convert to base/delete_modal_actions.tmpl */}}
<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>
</div>
</div>
@ -265,15 +265,6 @@
<div class="content">
<p>{{.locale.Tr "repo.projects.deletion_desc"}}</p>
</div>
<div class="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>
{{template "base/delete_modal_actions" .}}
</div>
{{end}}

View File

@ -1,14 +1,18 @@
<div class="issue list">
{{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">
{{template "repo/actions/status" .Status}}
</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">
<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 -}}
</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">
{{if .RefLink}}
<a href="{{.RefLink}}">{{.PrettyRef}}</a>
@ -17,10 +21,6 @@
{{end}}
</span>
</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 class="issue-item-right">
<div>{{TimeSinceUnix .Updated $.locale}}</div>

View File

@ -176,7 +176,7 @@
</div>
<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>
</div>
</form>

View File

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

View File

@ -96,7 +96,7 @@
</div>
<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>
</div>
</form>
@ -121,7 +121,7 @@
</div>
<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>
</div>
</form>

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@
{{template "repo/issue/navbar" .}}
{{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}}
<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>
{{end}}
</div>

View File

@ -6,16 +6,7 @@
<div class="content">
<p>{{.locale.Tr "repo.issues.label_deletion_desc"}}</p>
</div>
<div class="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>
{{template "base/delete_modal_actions" .}}
</div>
<div class="ui small edit-label modal">
@ -61,11 +52,13 @@
</form>
</div>
<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"}}
</div>
<div class="ui primary small approve button">
</button>
<button class="ui primary small approve button">
<i class="save icon"></i>
{{.locale.Tr "save"}}
</div>
</button>
</div>
</div>

View File

@ -36,12 +36,15 @@
</div>
</form>
</div>
<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"}}
</div>
<div class="ui primary small approve button">
</button>
<button class="ui green basic inverted ok button">
{{svg "octicon-check"}}
{{.locale.Tr "repo.issues.create_label"}}
</div>
</button>
</div>
</div>

View File

@ -213,9 +213,9 @@
{{if not .Repository.IsArchived}}
<!-- Action Button -->
{{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}}
<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}}
<!-- Labels -->
<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item">

View File

@ -149,9 +149,9 @@
<div class="ui secondary filter stackable menu">
<!-- Action Button -->
{{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}}
<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}}
<!-- Labels -->
<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item">

View File

@ -124,16 +124,7 @@
<div class="content">
<p>{{.locale.Tr "repo.milestones.deletion_desc"}}</p>
</div>
<div class="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>
{{template "base/delete_modal_actions" .}}
</div>
{{end}}
{{template "base/footer" .}}

View File

@ -112,13 +112,13 @@
<div class="text right">
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .DisableStatusChange)}}
{{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"}}
</div>
</button>
{{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"}}
</div>
</button>
{{end}}
{{end}}
<button class="ui green button loading-button" tabindex="5">
@ -163,13 +163,13 @@
<div class="text right">
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .DisableStatusChange)}}
{{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"}}
</div>
</button>
{{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"}}
</div>
</button>
{{end}}
{{end}}
<button class="ui green button loading-button" tabindex="5">
@ -215,8 +215,8 @@
{{end}}
<div class="field footer">
<div class="text right edit">
<div class="ui basic secondary cancel button" tabindex="3">{{.locale.Tr "repo.issues.cancel"}}</div>
<div class="ui primary save button" tabindex="2">{{.locale.Tr "repo.issues.save"}}</div>
<button class="ui basic secondary cancel button" tabindex="3">{{.locale.Tr "repo.issues.cancel"}}</button>
<button class="ui primary save button" tabindex="2">{{.locale.Tr "repo.issues.save"}}</button>
</div>
</div>
</div>

View File

@ -7,10 +7,7 @@
{{.ctxData.CsrfTokenHtml}}
</form>
<div class="header">{{.ctxData.locale.Tr "repo.issues.del_time"}}</div>
<div class="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>
{{template "base/delete_modal_actions" .}}
</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">
{{svg "octicon-trash"}}

View File

@ -58,7 +58,7 @@
<input id="message" name="message">
</div>
<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>
</div>
</form>

View File

@ -346,8 +346,8 @@
</form>
</div>
<div class="actions">
<div class="ui green approve button">{{.locale.Tr "repo.issues.add_time_short"}}</div>
<div class="ui red cancel button">{{.locale.Tr "repo.issues.add_time_cancel"}}</div>
<button class="ui green approve button">{{.locale.Tr "repo.issues.add_time_short"}}</button>
<button class="ui red cancel button">{{.locale.Tr "repo.issues.add_time_cancel"}}</button>
</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>
@ -532,14 +532,14 @@
{{end}}</p>
</div>
<div class="actions">
<div class="ui red cancel inverted button">
<button class="ui red cancel inverted button">
{{svg "octicon-x"}}
{{.locale.Tr "repo.issues.dependency.cancel"}}
</div>
<div class="ui green ok inverted button">
</button>
<button class="ui green ok inverted button">
{{svg "octicon-check"}}
{{.locale.Tr "repo.issues.dependency.remove"}}
</div>
</button>
</div>
</div>
{{end}}
@ -619,7 +619,7 @@
{{end}}
<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">
{{if .Issue.IsLocked}}
{{.locale.Tr "repo.issues.unlock_confirm"}}
@ -655,7 +655,7 @@
<form action="{{.Issue.Link}}/delete" method="post">
{{.CsrfTokenHtml}}
<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>
</div>
</form>

View File

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

View File

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

View File

@ -72,7 +72,7 @@
</div>
<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>
</div>
</form>

View File

@ -86,16 +86,7 @@
<div class="content">
<p>{{.locale.Tr "repo.projects.deletion_desc"}}</p>
</div>
<div class="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>
{{template "base/delete_modal_actions" .}}
</div>
{{end}}
{{template "base/footer" .}}

View File

@ -33,7 +33,7 @@
</div>
<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>
</div>
</form>
@ -131,7 +131,7 @@
</div>
<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>
</div>
</form>
@ -148,7 +148,7 @@
</label>
</div>
<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>
</div>
</div>
@ -162,8 +162,8 @@
{{$.locale.Tr "repo.projects.column.deletion_desc"}}
</label>
</div>
<div class="text right actions">
<div class="ui cancel button">{{$.locale.Tr "settings.cancel"}}</div>
<div class="text right actions">{{/* TODO: Convert to base/delete_modal_actions.tmpl? */}}
<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>
</div>
</div>
@ -276,16 +276,7 @@
<div class="content">
<p>{{.locale.Tr "repo.projects.deletion_desc"}}</p>
</div>
<div class="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>
{{template "base/delete_modal_actions" .}}
</div>
{{end}}

View File

@ -114,7 +114,7 @@
{{$.locale.Tr "repo.release.delete_release"}}
</a>
{{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">
{{.locale.Tr "repo.release.publish"}}
</button>
@ -125,9 +125,9 @@
{{end}}
{{else}}
{{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}}
<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">
{{.locale.Tr "repo.release.publish"}}
</button>

View File

@ -8,9 +8,9 @@
{{.locale.Tr "repo.settings.deploy_keys"}}
<div class="ui right">
{{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}}
<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}}
</div>
</h4>
@ -85,15 +85,6 @@
<div class="content">
<p>{{.locale.Tr "repo.settings.deploy_key_deletion_desc"}}</p>
</div>
<div class="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>
{{template "base/delete_modal_actions" .}}
</div>
{{template "base/footer" .}}

View File

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

View File

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

View File

@ -825,7 +825,7 @@
</div>
<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>
</div>
</form>
@ -856,7 +856,7 @@
</div>
<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>
</div>
</form>
@ -892,7 +892,7 @@
</div>
<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>
</div>
</form>
@ -926,7 +926,7 @@
</div>
<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>
</div>
</form>
@ -958,7 +958,7 @@
</div>
<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>
</div>
</form>
@ -988,10 +988,7 @@
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="{{if .Repository.IsArchived}}unarchive{{else}}archive{{end}}">
<input type="hidden" name="repo_id" value="{{.Repository.ID}}">
<div class="center 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>
{{template "base/delete_modal_actions" .}}
</form>
</div>
{{end}}

View File

@ -6,14 +6,5 @@
<div class="content">
<p>{{.locale.Tr "repo.settings.webhook_deletion_desc"}}</p>
</div>
<div class="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>
{{template "base/delete_modal_actions" .}}
</div>

View File

@ -1,7 +1,7 @@
{{if .EscapeStatus}}
{{if .EscapeStatus.HasInvisible}}
<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">
{{$.root.locale.Tr "repo.invisible_runes_header"}}
</div>
@ -12,7 +12,7 @@
</div>
{{else if .EscapeStatus.HasAmbiguous}}
<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">
{{$.root.locale.Tr "repo.ambiguous_runes_header"}}
</div>

View File

@ -20,9 +20,9 @@
</div>
<div class="ui input">
<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}}
</div>
</button>
</div>
<div class="divider"></div>
<div class="item">

View File

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

View File

@ -493,6 +493,12 @@
"name": "username",
"in": "path",
"required": true
},
{
"type": "boolean",
"description": "purge the user from the system completely",
"name": "purge",
"in": "query"
}
],
"responses": {
@ -707,6 +713,9 @@
"201": {
"$ref": "#/responses/Repository"
},
"400": {
"$ref": "#/responses/error"
},
"403": {
"$ref": "#/responses/forbidden"
},
@ -1920,6 +1929,9 @@
"201": {
"$ref": "#/responses/Repository"
},
"400": {
"$ref": "#/responses/error"
},
"403": {
"$ref": "#/responses/forbidden"
},
@ -2114,6 +2126,7 @@
"pub",
"pypi",
"rubygems",
"swift",
"vagrant"
],
"type": "string",
@ -13375,6 +13388,9 @@
"201": {
"$ref": "#/responses/Repository"
},
"400": {
"$ref": "#/responses/error"
},
"409": {
"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="nonce" value="{{.Nonce}}">
<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>
</form>
</div>

View File

@ -17,6 +17,6 @@
</div>
<div class="actions">
<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>

View File

@ -151,9 +151,9 @@
<input id="password-confirmation" name="password" type="password" autocomplete="off" required>
</div>
<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"}}
</div>
</button>
<a href="{{AppSubUrl}}/user/forgot_password?email={{.Email}}">{{.locale.Tr "auth.forgot_password"}}</a>
</div>
</form>

View File

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

View File

@ -1,7 +1,7 @@
<h4 class="ui top attached header">
{{.locale.Tr "settings.manage_gpg_keys"}}
<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>
</h4>
<div class="ui attached segment">

View File

@ -3,9 +3,9 @@
{{.locale.Tr "settings.manage_ssh_principals"}}
<div class="ui right">
{{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}}
<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}}
</div>
</h4>

View File

@ -2,11 +2,11 @@
{{.locale.Tr "settings.manage_ssh_keys"}}
<div class="ui right">
{{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"}}
</div>
</button>
{{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}}
</div>
</h4>

View File

@ -50,16 +50,7 @@
{{$.CsrfTokenHtml}}
<input type="hidden" name="id" value="{{$dir}}">
<input type="hidden" name="action" value="adopt">
<div class="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>
{{template "base/delete_modal_actions" .}}
</form>
</div>
{{end}}
@ -77,16 +68,7 @@
{{$.CsrfTokenHtml}}
<input type="hidden" name="id" value="{{$dir}}">
<input type="hidden" name="action" value="delete">
<div class="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>
{{template "base/delete_modal_actions" .}}
</form>
</div>
{{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">
{{.CsrfTokenHtml}}
<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>
{{else}}
<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")})
assert.NoError(t, err)
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)
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")})
assert.NoError(t, err)
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)
return err
}))

View File

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

View File

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

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