mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-18 00:01:32 -04:00
Compare commits
12 Commits
a8e13e64da
...
b942838bd4
Author | SHA1 | Date | |
---|---|---|---|
|
b942838bd4 | ||
|
81fe5d6185 | ||
|
605fd15ad6 | ||
|
8570593d55 | ||
|
8421b8264f | ||
|
5eea61dbc8 | ||
|
c709fa17a7 | ||
|
0a6f6354bb | ||
|
d74a7efb60 | ||
|
cdc9e91750 | ||
|
757b4c17e9 | ||
|
f521e88240 |
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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`)
|
||||
|
@ -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 的下一个版本才会出现。
|
||||
|
||||
如果使用开发版本,则在同步更改内容后,它应该会在更新后立即显示。
|
||||
|
@ -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!**
|
||||
|
93
docs/content/doc/packages/swift.en-us.md
Normal file
93
docs/content/doc/packages/swift.en-us.md
Normal 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
|
||||
```
|
@ -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",
|
||||
|
@ -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:
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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++
|
||||
|
@ -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
|
||||
|
214
modules/packages/swift/metadata.go
Normal file
214
modules/packages/swift/metadata.go
Normal 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()
|
||||
}
|
144
modules/packages/swift/metadata_test.go
Normal file
144
modules/packages/swift/metadata_test.go
Normal 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))
|
||||
}
|
||||
}
|
@ -12,9 +12,13 @@ import (
|
||||
|
||||
// Git settings
|
||||
var Git = struct {
|
||||
Path string
|
||||
HomePath string
|
||||
DisableDiffHighlight bool
|
||||
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,
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
})
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
1
public/img/svg/fontawesome-save.svg
Normal file
1
public/img/svg/fontawesome-save.svg
Normal 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 |
1
public/img/svg/gitea-swift.svg
Normal file
1
public/img/svg/gitea-swift.svg
Normal 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 |
50
routers/api/packages/README.md
Normal file
50
routers/api/packages/README.md
Normal 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.
|
@ -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)
|
||||
|
464
routers/api/packages/swift/swift.go
Normal file
464
routers/api/packages/swift/swift.go
Normal 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,
|
||||
})
|
||||
}
|
@ -32,6 +32,8 @@ func CreateRepo(ctx *context.APIContext) {
|
||||
// responses:
|
||||
// "201":
|
||||
// "$ref": "#/responses/Repository"
|
||||
// "400":
|
||||
// "$ref": "#/responses/error"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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":
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -21,35 +21,60 @@ func CreateCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
|
||||
}
|
||||
|
||||
run := job.Run
|
||||
if run.Event != webhook_module.HookEventPush {
|
||||
var (
|
||||
sha string
|
||||
creatorID int64
|
||||
)
|
||||
|
||||
switch run.Event {
|
||||
case webhook_module.HookEventPush:
|
||||
payload, err := run.GetPushEventPayload()
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetPushEventPayload: %w", err)
|
||||
}
|
||||
|
||||
// Since the payload comes from json data, we should check if it's broken, or it will cause panic
|
||||
switch {
|
||||
case payload.Repo == nil:
|
||||
return fmt.Errorf("repo is missing in event payload")
|
||||
case payload.Pusher == nil:
|
||||
return fmt.Errorf("pusher is missing in event payload")
|
||||
case payload.HeadCommit == nil:
|
||||
return fmt.Errorf("head commit is missing in event payload")
|
||||
}
|
||||
|
||||
sha = payload.HeadCommit.ID
|
||||
creatorID = payload.Pusher.ID
|
||||
case webhook_module.HookEventPullRequest:
|
||||
payload, err := run.GetPullRequestEventPayload()
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetPullRequestEventPayload: %w", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case payload.PullRequest == nil:
|
||||
return fmt.Errorf("pull request is missing in event payload")
|
||||
case payload.PullRequest.Head == nil:
|
||||
return fmt.Errorf("head of pull request is missing in event payload")
|
||||
case payload.PullRequest.Head.Repository == nil:
|
||||
return fmt.Errorf("head repository of pull request is missing in event payload")
|
||||
case payload.PullRequest.Head.Repository.Owner == nil:
|
||||
return fmt.Errorf("owner of head repository of pull request is missing in evnt payload")
|
||||
}
|
||||
|
||||
sha = payload.PullRequest.Head.Sha
|
||||
creatorID = payload.PullRequest.Head.Repository.Owner.ID
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
payload, err := run.GetPushEventPayload()
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetPushEventPayload: %w", err)
|
||||
}
|
||||
|
||||
// Since the payload comes from json data, we should check if it's broken, or it will cause panic
|
||||
switch {
|
||||
case payload.Repo == nil:
|
||||
return fmt.Errorf("repo is missing in event payload")
|
||||
case payload.Pusher == nil:
|
||||
return fmt.Errorf("pusher is missing in event payload")
|
||||
case payload.HeadCommit == nil:
|
||||
return fmt.Errorf("head commit is missing in event payload")
|
||||
}
|
||||
|
||||
creator, err := user_model.GetUserByID(ctx, payload.Pusher.ID)
|
||||
repo := run.Repo
|
||||
ctxname := job.Name
|
||||
state := toCommitStatus(job.Status)
|
||||
creator, err := user_model.GetUserByID(ctx, creatorID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetUserByID: %w", err)
|
||||
}
|
||||
|
||||
repo := run.Repo
|
||||
sha := payload.HeadCommit.ID
|
||||
ctxname := job.Name
|
||||
state := toCommitStatus(job.Status)
|
||||
|
||||
if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{}); err == nil {
|
||||
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 {
|
||||
|
@ -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)"`
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
{{.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">
|
||||
<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 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>
|
||||
|
@ -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>
|
||||
|
@ -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" .}}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
40
templates/package/content/swift.tmpl
Normal file
40
templates/package/content/swift.tmpl
Normal 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}}
|
4
templates/package/metadata/swift.tmpl
Normal file
4
templates/package/metadata/swift.tmpl
Normal 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}}
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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}}
|
||||
|
@ -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}}
|
||||
|
@ -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> {{$.locale.Tr "actions.runs.pushed_by"}} {{.TriggerUser.GetDisplayName | Escape}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="issue-item-right">
|
||||
<div>{{TimeSinceUnix .Updated $.locale}}</div>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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}}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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" .}}
|
||||
|
@ -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>
|
||||
|
@ -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"}}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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" .}}
|
||||
|
@ -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}}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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" .}}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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}}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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."
|
||||
},
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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}}
|
||||
|
@ -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>
|
||||
|
326
tests/integration/api_packages_swift_test.go
Normal file
326
tests/integration/api_packages_swift_test.go
Normal 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])
|
||||
})
|
||||
}
|
@ -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
|
||||
}))
|
||||
|
@ -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"/>
|
||||
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user