Compare commits

...

7 Commits

Author SHA1 Message Date
wxiaoguang
ced34bab1a
Detect ogg mime-type as audio or video (#26494)
"ogg" is just a "container" format for audio and video.

Golang's `DetectContentType` only reports "application/ogg" for
potential ogg files.

Actually it could do more "guess" to see whether it is a audio file or a
video file.
2023-08-15 10:31:25 +08:00
wxiaoguang
c91a7e8dbb
Use object-fit: contain for oauth2 custom icons (#26493) 2023-08-14 16:21:04 +00:00
a1012112796
19872063a3
add disable workflow feature (#26413)
As title, that's simmilar with github.


![image](https://github.com/go-gitea/gitea/assets/25342410/9e8b2444-63e0-4e87-80da-730c1e4d09d6)



![image](https://github.com/go-gitea/gitea/assets/25342410/6c3a3345-3ba7-48c9-9acd-3e621632491b)

---------

Signed-off-by: a1012112796 <1012112796@qq.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Jason Song <i@wolfogre.com>
2023-08-14 15:14:30 +00:00
wxiaoguang
253737eb36
Move dropzone progress bar to bottom to show filename when uploading (#26492)
1. Make the "filename" visible
2. Avoiding UI flicker when the uploading is completing
2023-08-14 22:36:53 +08:00
wxiaoguang
ed1be4ca68
Handle base64 decoding correctly to avoid panic (#26483)
Fix the panic if the "base64 secret" is too long.
2023-08-14 10:30:16 +00:00
puni9869
cafce3b4b5
Allow to archive labels (#26478)
## Archived labels 

This adds the structure to allow for archived labels.
Archived labels are, just like closed milestones or projects, a medium to hide information without deleting it.
It is especially useful if there are outdated labels that should no longer be used without deleting the label entirely.

## Changes

1. UI and API have been equipped with the support to mark a label as archived
2. The time when a label has been archived will be stored in the DB

## Outsourced for the future

There's no special handling for archived labels at the moment.
This will be done in the future.

## Screenshots

![image](https://github.com/go-gitea/gitea/assets/80308335/208f95cd-42e4-4ed7-9a1f-cd2050a645d4)

![image](https://github.com/go-gitea/gitea/assets/80308335/746428e0-40bb-45b3-b992-85602feb371d)

Part of https://github.com/go-gitea/gitea/issues/25237

---------

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2023-08-14 11:56:14 +02:00
CaiCandong
db7b0a1a4e
Update zh-cn documentation (#26406) 2023-08-14 01:35:49 -04:00
50 changed files with 1833 additions and 451 deletions

View File

@ -70,12 +70,12 @@ func runGenerateInternalToken(c *cli.Context) error {
}
func runGenerateLfsJwtSecret(c *cli.Context) error {
JWTSecretBase64, err := generate.NewJwtSecretBase64()
_, jwtSecretBase64, err := generate.NewJwtSecretBase64()
if err != nil {
return err
}
fmt.Printf("%s", JWTSecretBase64)
fmt.Printf("%s", jwtSecretBase64)
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Printf("\n")

File diff suppressed because it is too large Load Diff

View File

@ -9,42 +9,51 @@ draft: false
# 关于Gitea
Gitea 是一个自己托管的Git服务程序。他和GitHub, Bitbucket or Gitlab等比较类似。他是从 [Gogs](http://gogs.io) 发展而来不过我们已经Fork并且命名为Gitea。对于我们Fork的原因可以看 [这里](https://blog.gitea.com/welcome-to-gitea/)。
Gitea 是一个轻量级的 DevOps 平台软件。从开发计划到产品成型的整个软件生命周期,他都能够高效而轻松的帮助团队和开发者。包括 Git 托管、代码审查、团队协作、软件包注册和 CI/CD。它与 GitHub、Bitbucket 和 GitLab 等比较类似。
Gitea 最初是从 [Gogs](http://gogs.io) 分支而来几乎所有代码都已更改。对于我们Fork的原因可以看
[这里](https://blog.gitea.com/welcome-to-gitea/)。
## 目标
Gitea的首要目标是创建一个极易安装运行非常快速安装和使用体验良好的自建 Git 服务。我们采用Go作为后端语言这使我们只要生成一个可执行程序即可。并且他还支持跨平台支持 Linux, macOS 和 Windows 以及各种架构除了x86amd64还包括 ARM 和 PowerPC。
Gitea的首要目标是创建一个极易安装运行非常快速安装和使用体验良好
的自建 Git 服务。
采用Go作为后端语言只需生成一个可执行程序即可。
支持 Linux, macOS 和 Windows等多平台
支持主流的x86amd64、
ARM 和 PowerPC等架构。
## 功能特性
- 支持活动时间线
- 支持 SSH 以及 HTTP/HTTPS 协议
- 支持 SMTP、LDAP 和反向代理的用户认证
- 支持反向代理子路径
- 支持用户、组织和仓库管理系统
- 支持添加和删除仓库协作者
- 支持仓库和组织级别 Web 钩子(包括 Slack 集成)
- 支持仓库 Git 钩子和部署密钥
- 支持仓库工单Issue、合并请求Pull Request以及 Wiki
- 支持迁移和镜像仓库以及它的 Wiki
- 支持在线编辑仓库文件和 Wiki
- 支持自定义源的 Gravatar 和 Federated Avatar
- 支持邮件服务
- 支持后台管理面板
- 支持 MySQL、PostgreSQL、SQLite3、MSSQL 和 TiDB(MySQL) 数据库
- 支持多语言本地化21 种语言)
- 支持软件包注册中心Composer/Conan/Container/Generic/Helm/Maven/NPM/Nuget/PyPI/RubyGems
- 代码托管Gitea⽀持创建和管理仓库、浏览提交历史和代码⽂件、审查和合并代码提交、管理协作者、管理分⽀等。它还⽀持许多常见的Git特性⽐如标签、Cherry-pick、hook、集成协作⼯具等。
- 轻量级和快速: Gitea 的设计目标之一就是轻量级和快速响应。它不像一些大型的代码托管平台那样臃肿因此在性能方面表现出色适用于资源有限的服务器环境。由于其轻量级设计Gitea 在资源消耗方面相对较低,可以在资源有限的环境下运行良好。
- 易于部署和维护: 轻松地部署在各种服务器上,不需要复杂的配置和依赖。这使得个人开发者或小团队可以方便地设置和管理自己的 Git 服务。
- 安全性: Gitea 注重安全性,提供了用户权限管理、访问控制列表等功能,可以确保代码和数据的安全性。
- 代码评审:代码评审同时支持 Pull Request workflow 和 AGit workflow。评审⼈可以在线浏览代码并提交评审意见或问题。 提交者可以接收到评审意见,并在线回 复或修改代码。代码评审可以帮助用户和企业提⾼代码质量。
- CI/CD: Gitea Actions⽀持 CI/CD 功能,该功能兼容 GitHub Actions⽤⼾可以采用熟悉的YAML格式编写workflows也可以重⽤⼤量的已有的 Actions 插件。Actions 插件支持从任意的 Git 网站中下载。
- 项目管理Gitea 通过看板和⼯单来跟踪⼀个项⽬的需求功能和bug。⼯单⽀持分支标签、⾥程碑、 指派、时间跟踪、到期时间、依赖关系等功能。
- 制品库: Gitea支持超过 20 种不同种类的公有或私有软件包管理包括Cargo, Chef, Composer, Conan, Conda, Container, Helm, Maven, npm, NuGet, Pub, PyPI, RubyGems, Vagrant等
- 开源社区支持: Gitea 是一个基于 MIT 许可证的开源项目,Gitea 拥有一个活跃的开源社区,能够持续地进行开发和改进,同时也积极接受社区贡献,保持了平台的更新和创新。
- 多语言支持: Gitea 提供多种语言界面,适应全球范围内的用户,促进了国际化和本地化。
更多功能特性详见https://docs.gitea.com/installation/comparison#general-features
## 系统要求
- 最低的系统硬件要求为一个廉价的树莓派
- 如果用于团队项目,建议使用 2 核 CPU 及 1GB 内存
- 树莓派Pi3功能强大足以运行 Gitea 来处理小型工作负载。
- 对于小型团队/项目而言2 个 CPU 内核和 1GB 内存通常就足够了。
- 在 UNIX 系统上Gitea 应使用专用的非 root 系统账户运行。
- 注意Gitea 管理 `~/.ssh/authorized_keys` 文件。以普通用户身份运行 Gitea 可能会破坏该用户的登录能力。
- [Git](https://git-scm.com/) 需要 2.0.0 或更高版本。
- [Git Large File Storage](https://git-lfs.github.com/) 如果启用,且 Git 版本大于等于 2.1.2,则该选项可用
- 如果 Git 版本大于等于 2.18,将自动启用 Git 提交历史图形化展示功能
## 浏览器支持
- Chrome, Firefox, Safari, Edge
- Last 2 versions of Chrome, Firefox, Safari and Edge
- Firefox ESR
## 组件
## 技术栈
- Web框架 [Chi](http://github.com/go-chi/chi)
- ORM: [XORM](https://xorm.io)

View File

@ -29,53 +29,62 @@ _表格中的符号含义:_
* _✘ - 不支持_
* _? - 不确定_
* _⚙ - 由第三方服务或插件支持_
#### 主要特性
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
| --------------------- | -------------------------------------------------- | ---- | --------- | --------- | --------- | -------------- | ------------ |
| 开源免费 | ✓ | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ |
| 低资源开销 (RAM/CPU) | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ |
| 支持多种数据库 | ✓ | ✓ | ✘ | | | ✓ | ✓ |
| 支持多种操作系统 | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ |
| 升级简便 | ✓ | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ |
| 支持 Markdown | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 支持 Orgmode | ✓ | ✘ | ✓ | ✘ | ✘ | ✘ | ? |
| 支持 CSV | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ | ? |
| 支持第三方渲染工具 | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ | ? |
| Git 驱动的静态 pages | [⚙️][gitea-pages-server], [⚙️][gitea-caddy-plugin] | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| Git 驱动的集成化 wiki | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (cloud only) | ✘ |
| 部署令牌 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 仓库写权限令牌 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 内置容器 Registry | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| 外部 Git 镜像 | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ |
| WebAuthn (2FA) | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ? |
| 内置 CI/CD | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| 子组织:组织内的组织 | [](https://github.com/go-gitea/gitea/issues/1872) | ✘ | ✘ | ✓ | ✓ | ✘ | ✓ |
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
| ------------------------------- | -------------------------------------------------- | ---- | --------- | --------- | --------- | -------------- | ------------ |
| 开源免费 | ✓ | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ |
| 低资源开销 (RAM/CPU) | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ |
| 支持多种数据库 | ✓ | ✓ | ✘ | | | ✓ | ✓ |
| 支持多种操作系统 | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ |
| 升级简便 | ✓ | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ |
| 可观测性 | **✘** | ✘ | ✓ | ✓ | ✓ | ✓ | ? |
| 支持第三方渲染工具 | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ | ? |
| WebAuthn (2FA) | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ? |
| 扩展 API | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 内置软件包/容器注册中心 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| 同步提交到外部仓库 (push mirror) | ✓ | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ |
| 同步外部仓库的提交 (pull mirror) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ? |
| 浅色和深色主题 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ? |
| 自定义主题支持 | ✓ | ✓ | ✘ | ✘ | ✘ | ✓ | ✘ |
| 支持 Markdown | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 支持 CSV | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ | ? |
| Git 驱动的静态 pages | [⚙️][gitea-pages-server], [⚙️][gitea-caddy-plugin] | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| Git 驱动的集成化 wiki | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (cloud only) | ✘ |
| 部署令牌 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 仓库写权限令牌 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| RSS Feeds | ✓ | ✘ | ✓ | ✘ | ✘ | ✘ | ✘ |
| 内置 CI/CD | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| 子组织:组织内的组织 | [](https://github.com/go-gitea/gitea/issues/1872) | ✘ | ✘ | ✓ | ✓ | ✘ | ✓ |
| 多实例交互 | [/](https://github.com/go-gitea/gitea/issues/18240) | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ |
| Markdown绘图 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| Markdown数学公式 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
#### 代码管理
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
| ---------------------------------------- | ------------------------------------------------ | ---- | --------- | --------- | --------- | --------- | ------------ |
| 仓库主题描述 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| 仓库内代码搜索 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 全局代码搜索 | ✓ | ✘ | ✓ | ✘ | ✓ | ✓ | ✓ |
| Git LFS 2.0 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 组织里程碑 | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
| 细粒度用户角色 (例如 Code, Issues, Wiki) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
| 提交人的身份验证 | | ✘ | ? | ✓ | ✓ | ✓ | ✘ |
| GPG 签名的提交 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| SSH 签名的提交 | ✓ | ✘ | ✘ | ✘ | ✘ | ? | ? |
| 拒绝未用通过验证的提交 | [](https://github.com/go-gitea/gitea/pull/9708) | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 仓库活跃度页面 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 分支管理 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 建立新分支 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| 在线代码编辑 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 提交的统计图表 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 模板仓库 | [](https://github.com/go-gitea/gitea/pull/8768) | ✘ | ✓ | ✘ | ✓ | ✓ | ✘ |
| 特性 | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
| ------------------------------------ | --------------------------------------------------- | ---- | --------- | --------- | --------- | --------- | ------------ |
| 仓库主题描述 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| 仓库内代码搜索 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 全局代码搜索 | ✓ | ✘ | ✓ | ✘ | ✓ | ✓ | ✓ |
| Git LFS 2.0 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 组织里程碑 | [](https://github.com/go-gitea/gitea/issues/14622) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
| 细粒度用户角色 | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
| 提交人的身份验证 | | ✘ | ? | ✓ | ✓ | ✓ | ✘ |
| GPG 签名的提交 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| SSH 签名的提交 | ✓ | ✘ | ✓ | ✓ | ✓ | ? | ? |
| 拒绝未通过验证的提交 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 外部仓库迁移 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 仓库活跃度页面 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 分支管理 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 建立新分支 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| 在线代码编辑 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 提交的统计图表 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 模板仓库 | ✓ | ✘ | ✓ | ✘ | ✓ | ✓ | ✘ |
| Git Blame | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
| 可视化镜像变化 | ✓ | ✘ | ✓ | ? | ? | ? | ? |
#### 工单管理
@ -93,6 +102,7 @@ _表格中的符号含义:_
| 工单批处理 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| 工单看板 | [](https://github.com/go-gitea/gitea/pull/8346) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
| 从工单创建分支 | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
| 从评论创建工单 | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
| 工单搜索 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
| 工单全局搜索 | [](https://github.com/go-gitea/gitea/issues/2434) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
| 工单依赖关系 | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ |
@ -114,6 +124,7 @@ _表格中的符号含义:_
| Pull/Merge requests 模板 | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
| 查看 Cherry-picking 的更改 | [](https://github.com/go-gitea/gitea/issues/5158) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
| 下载 Patch | ✓ | ✘ | ✓ | ✓ | ✓ | [/](https://jira.atlassian.com/plugins/servlet/mobile#issue/BCLOUD-8323) | ✘ |
| Merge queues | ✘ | ✘ | ✓ | ✘ | ✓ | ✘ | ✘ |
#### 第三方集成

View File

@ -133,6 +133,14 @@ export GITEA_WORK_DIR=/var/lib/gitea/
cp gitea /usr/local/bin/gitea
```
### 添加 bash/zsh 自动补全(从 1.19 版本开始)
可以在 [`contrib/autocompletion/bash_autocomplete`](https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/autocompletion/bash_autocomplete) 找到启用 bash 自动补全的脚本。可以将其复制到 `/usr/share/bash-completion/completions/gitea`,或在 `.bashrc` 中引用。
同样地zsh 自动补全的脚本可以在 [`contrib/autocompletion/zsh_autocomplete`](https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/autocompletion/zsh_autocomplete) 找到。您可以将其复制到 `/usr/share/zsh/_gitea`,或在您的 `.zshrc` 中引用。
具体情况可能会有所不同,这些脚本可能需要进一步的改进。
## 运行 Gitea
完成以上步骤后,可以通过两种方式运行 Gitea
@ -153,6 +161,8 @@ GITEA_WORK_DIR=/var/lib/gitea/ /usr/local/bin/gitea web -c /etc/gitea/app.ini
建议您在更新之前进行[备份](administration/backup-and-restore.md)。
如果您按照上述描述执行了安装步骤,二进制文件的通用名称应为 gitea。请勿更改此名称即不要包含版本号。
### 1. 使用 systemd 重新启动 Gitea推荐
我们建议使用 systemd 作为服务管理器,使用 `systemctl restart gitea` 安全地重启程序。
@ -169,6 +179,36 @@ GITEA_WORK_DIR=/var/lib/gitea/ /usr/local/bin/gitea web -c /etc/gitea/app.ini
## 排查故障
### 旧版 glibc
旧版 Linux 发行版(例如 Debian 7 和 CentOS 6可能无法加载 Gitea 二进制文件,通常会产生类似于 `./gitea: /lib/x86_64-linux-gnu/libc.so.6:
version 'GLIBC\_2.14' not found (required by ./gitea)` 的错误。这是由于 dl.gitea.com 提供的二进制文件中集成了 SQLite 支持。在这种情况下,通常可以选择[从源代码安装](installation/from-source.md),而不包括 SQLite 支持。
### 在另一个端口上运行 Gitea
对于出现类似于 `702 runWeb()] [E] Failed to start server: listen tcp 0.0.0.0:3000:
bind: address already in use` 的错误,需要将 Gitea 启动在另一个空闲端口上。您可以使用 `./gitea web -p $PORT` 来实现。可能已经有另一个 Gitea 实例在运行。
### 在 Raspbian 上运行 Gitea
从 v1.8 版本开始arm7 版本的 Gitea 存在问题,无法在树莓派和类似设备上运行。
建议切换到 arm6 版本,该版本经过测试并已被证明可以在树莓派和类似设备上运行。
### 更新到新版本的 Gitea 后出现的 Git 错误
如果在更新过程中,二进制文件的名称已更改为新版本的 Gitea则现有仓库中的 Git 钩子将不再起作用。在这种情况下,当推送到仓库时,会显示 Git 错误。
```
remote: ./hooks/pre-receive.d/gitea: line 2: [...]: No such file or directory
```
错误信息中的 `[...]` 部分将包含您先前 Gitea 二进制文件的路径。
要解决此问题,请转到管理选项,并运行任务 `Resynchronize pre-receive, update and post-receive hooks of all repositories`,以将所有钩子更新为包含新的二进制文件路径。请注意,这将覆盖所有 Git 钩子,包括自定义的钩子。
如果您没有使用 Gitea 内置的 SSH 服务器,您还需要通过在管理选项中运行任务 `Update the '.ssh/authorized_keys' file with Gitea SSH keys.` 来重新编写授权密钥文件。
> 更多经验总结,请参考英文版 [Troubleshooting](/en-us/install-from-binary/#troubleshooting)
如果从本页中没有找到你需要的内容,请访问 [帮助页面](help/support.md)

View File

@ -52,6 +52,14 @@ pacman -S gitea
pacman -S gitea
```
## Gentoo Linux
滚动发布的发行版在其官方社区软件仓库中提供了 [Gitea](https://packages.gentoo.org/packages/www-apps/gitea),并且会随着新的 Gitea 发布提供软件包更新。
```sh
emerge gitea -va
```
## Canonical Snap
目前 Gitea 已在 Snap Store 中发布,名称为 [gitea](https://snapcraft.io/gitea)。

View File

@ -19,10 +19,83 @@ menu:
如果你正在运行Gogs 0.9.146以下版本你可以平滑的升级到Gitea。该升级需要如下的步骤
* 停止 Gogs 的运行
* 拷贝 Gogs 的配置文件 `custom/conf/app.ini` 到 Gitea 的相应位置。
* 拷贝 Gitea 的 `options/` 到 Home 目录下。
* 如果你还有更多的自定义内容比如templates和localization文件你需要手工合并你的修改到 Gitea 的 Options 下对应目录。
* 拷贝 Gogs 的数据目录 `data/` 到 Gitea 相应位置。这个目录包含附件和头像文件。
* 运行 Gitea
* 登录 Gitea 并进入 管理面板, 运行 `重新生成 '.ssh/authorized_keys' 文件(警告:不是 Gitea 的密钥也会被删除)``重新生成所有仓库的 Update 钩子(用于自定义配置文件被修改)`
* 使用 `gogs backup` 创建 Gogs 备份。这会创建一个名为 `gogs-backup-[时间戳].zip` 的文件,其中包含所有重要的 Gogs 数据。如果您将来想要返回到 `gogs`,您会需要这个备份文件。
* 从 [下载页面](https://dl.gitea.com/gitea/) 下载适用于目标平台的文件。应该选择 `1.0.x` 版本。从 `gogs` 迁移到其他任何版本是不可能的。
* 将二进制文件放置在所需的安装位置。
* 将 `gogs/custom/conf/app.ini` 复制到 `gitea/custom/conf/app.ini`
* 将 `gogs/custom/` 中的自定义 `templates, public` 复制到 `gitea/custom/`
* 对于其他自定义文件夹,例如 `gogs/custom/conf` 中的 `gitignore, label, license, locale, readme`,将它们复制到 `gitea/custom/options`
* 将 `gogs/data/` 复制到 `gitea/data/`。其中包含问题附件和头像。
* 使用 `gitea web` 启动 Gitea 进行验证。
* 在 UI 上进入 Gitea 管理面板,运行 `Rewrite '.ssh/authorized_keys' file`
* 启动每个主要版本的二进制文件(例如 `1.1.4``1.2.3``1.3.4``1.4.2` → 等)以迁移数据库。
* 如果自定义或配置路径已更改,请运行 `Rewrite all update hook of repositories`
## 更改特定于 Gogs 的信息
* 将 `gogs-repositories/` 重命名为 `gitea-repositories/`
* 将 `gogs-data/` 重命名为 `gitea-data/`
* 在 `gitea/custom/conf/app.ini` 中进行更改:
从:
```ini
[database]
PATH = /home/:USER/gogs/data/:DATABASE.db
[attachment]
PATH = /home/:USER/gogs-data/attachments
[picture]
AVATAR_UPLOAD_PATH = /home/:USER/gogs-data/avatars
[log]
ROOT_PATH = /home/:USER/gogs/log
```
到:
```ini
[database]
PATH = /home/:USER/gitea/data/:DATABASE.db
[attachment]
PATH = /home/:USER/gitea-data/attachments
[picture]
AVATAR_UPLOAD_PATH = /home/:USER/gitea-data/avatars
[log]
ROOT_PATH = /home/:USER/gitea/log
```
* 使用 `gitea web` 启动 Gitea 进行验证
## 升级到最新版本的 `gitea`
在成功从 `gogs` 迁移到 `gitea 1.0.x` 之后,可以通过两步过程将 `gitea` 升级到现代版本。
首先升级到 [`gitea 1.6.4`](https://dl.gitea.com/gitea/1.6.4/)。从 [下载页面](https://dl.gitea.com/gitea/1.6.4/) 下载适用于目标平台的文件,并替换二进制文件。至少运行一次 Gitea 并检查是否一切正常。
然后重复这个过程,但这次使用 [最新版本](https://dl.gitea.com/gitea/@version@/)。
## 从较新的 Gogs 版本升级
从较新的 Gogs 版本(最高到 `0.11.x`)可能也是可能的,但需要更多的工作。
请参见 [#4286](https://github.com/go-gitea/gitea/issues/4286),其中包括各种 Gogs `0.11.x` 版本。
从 Gogs `0.12.x` 及更高版本升级将变得越来越困难,因为项目在配置和架构上逐渐分歧。
## 故障排除
* 如果在 `gitea/custom/templates` 文件夹中遇到与自定义模板相关的错误,请尝试逐个移除引发错误的模板。
它们可能与 Gitea 或更新不兼容。
## 将 Gitea 添加到 Unix 的启动项
从 [gitea/contrib](https://github.com/go-gitea/gitea/tree/main/contrib) 更新适当的文件,确保正确的环境变量。
对于使用 systemd 的发行版:
* 将更新后的脚本复制到 `/etc/systemd/system/gitea.service`
* 使用以下命令将服务添加到启动项:`sudo systemctl enable gitea`
* 禁用旧的 gogs 启动脚本:`sudo systemctl disable gogs`
对于使用 SysVinit 的发行版:
* 将更新后的脚本复制到 `/etc/init.d/gitea`
* 使用以下命令将服务添加到启动项:`sudo rc-update add gitea`
* 禁用旧的 gogs 启动脚本:`sudo rc-update del gogs`

View File

@ -15,22 +15,53 @@ menu:
identifier: "windows-service"
---
# 准备工作
在 C:\gitea\custom\conf\app.ini 中进行了以下更改:
```
RUN_USER = COMPUTERNAME$
```
将 Gitea 设置为以本地系统用户运行。
COMPUTERNAME 是从命令行中运行 `echo %COMPUTERNAME%` 后得到的响应。如果响应是 `USER-PC`,那么 `RUN_USER = USER-PC$`
## 使用绝对路径
如果您使用 SQLite3请将 `PATH` 更改为包含完整路径:
```
[database]
PATH = c:/gitea/data/gitea.db
```
# 注册为Windows服务
要注册为Windows服务首先以Administrator身份运行 `cmd`,然后执行以下命令:
```
sc create gitea start= auto binPath= "\"C:\gitea\gitea.exe\" web --config \"C:\gitea\custom\conf\app.ini\""
sc.exe create gitea start= auto binPath= "\"C:\gitea\gitea.exe\" web --config \"C:\gitea\custom\conf\app.ini\""
```
别忘了将 `C:\gitea` 替换成你的 Gitea 安装目录。
之后在控制面板打开 "Windows Services",搜索 "gitea",右键选择 "Run"。在浏览器打开 `http://localhost:3000` 就可以访问了。如果你修改了端口请访问对应的端口3000是默认端口
## 添加启动依赖项
要将启动依赖项添加到 Gitea Windows 服务(例如 Mysql、Mariadb作为管理员然后运行以下命令
```
sc.exe config gitea depend= mariadb
```
这将确保在 Windows 计算机重新启动时,将延迟自动启动 Gitea直到数据库准备就绪从而减少启动失败的情况。
## 从Windows服务中删除
以Administrator身份运行 `cmd`,然后执行以下命令:
```
sc delete gitea
sc.exe delete gitea
```

View File

@ -29,10 +29,12 @@ Gitea 支持对仓库进行权限管理,这样您就可以为不同的人员
| 工单 | 组织缺陷报告、任务和里程碑。 | 读取 写入 |
| 合并请求 | 启用合并请求和代码审核。 | 读取 写入 |
| 发布 | 跟踪项目版本和下载。 | 读取 写入 |
| 维基 | 与协作者编写和共享文档。 | 读取 写入 |
| 外部维基 | 链接到外部维基。 | 读取 |
| 百科 | 与协作者编写和共享文档。 | 读取 写入 |
| 外部百科 | 链接到外部维基。 | 读取 |
| 外部工单跟踪器 | 链接到外部工单跟踪器。 | 读取 |
| 项目 | 模板仓库的 URL。 | 读取 写入 |
| 包 | 链接到仓库 | 读取 写入 |
| Actions | 审查Actions日志或重启/取消工作流 | 读取 写入 |
| 设置 | 管理仓库。 | 管理员 |
通过不同的权限,用户可以在这些单元上执行不同的操作。
@ -43,25 +45,42 @@ Gitea 支持对仓库进行权限管理,这样您就可以为不同的人员
| 工单 | 查看工单并创建新工单。 | 添加标签、分配、关闭工单。 | - |
| 合并请求 | 查看合并请求并创建新合并请求。 | 添加标签、分配、关闭合并请求。 | - |
| 发布 | 查看发布和下载文件。 | 创建/编辑发布。 | - |
| 维基 | 查看维基页面。克隆维基仓库。 | 创建/编辑维基页面,推送更改。 | - |
| 外部维基 | 链接到外部维基。 | - | - |
| 百科 | 查看百科页面。克隆百科仓库。 | 创建/编辑百科页面,推送更改。 | - |
| 外部百科 | 链接到外部百科。 | - | - |
| 外部工单跟踪器 | 链接到外部工单跟踪器。 | - | - |
| 项目 | 查看面板。 | 在面板之间移动工单。 | - |
| 包 | 查看包 | 上传/删除包 | - |
| Actions | 查看 Actions日志 | 同意 / 取消 / 重启 | - |
| 设置 | - | - | 管理仓库 |
个人仓库和组织仓库之间的权限存在一些差异。
## 个人仓库
对于个人仓库,创建者是仓库的唯一所有者,对于该仓库的任何更改或删除没有限制。仓库所有者可以添加协作者来帮助维护仓库。协作者可以拥有 `读取Read``写入Write``管理员Admin` 权限。
对于个人仓库,创建者是仓库的唯一所有者,对于该仓库的任何更改或删除没有限制。
仓库所有者可以添加协作者来帮助维护仓库。协作者可以拥有 `读取Read``写入Write``管理员Admin` 权限。
访问私有仓库的体验与访问匿名公共仓库类似。您可以访问仓库中的所有可用内容,包括克隆代码、创建工单、回复工单评论、提交拉取请求等。如果你有 "写"权限,只要分支保护规则允许,你就可以向仓库的特定分支推送代码。此外,你还可以修改百科页面。有了 "管理"权限,你就可以修改仓库的设置。
但如果你不是该仓库的所有者,就不能删除或转移该仓库。
## 组织仓库
与个人仓库不同,组织仓库的所有者是组织的所有者团队。
对于个人仓库,所有者是创建它的用户。而对于组织仓库,所有者是该组织中的所有者团队成员。对该组织仓库的所有权限都取决于团队权限设置
### 团队
### 所有者团队
组织中的一个团队具有单元权限设置。它可以拥有成员和仓库的范围。团队可以访问组织中的所有仓库或者由所有者团队授权访问的特定仓库。团队也可以被允许创建新的仓库
创建组织时将自动创建所有者团队,创建者将成为所有者团队的第一名成员。所有者团队不可删除,且至少有一名成员
所有者团队Owners将在创建组织时自动创建并且创建者将成为所有者团队的第一个成员。
组织的每个成员必须至少属于一个团队。所有者团队不能被删除,只有所有者团队的成员可以创建新的团队。可以创建一个管理员团队来管理某些仓库,该团队的成员可以对这些仓库进行任何操作。可以由所有者团队创建一个生成团队来执行其权限允许的操作。
### 管理员团队
创建团队时,有两种类型的团队。一种是管理员团队,另一种是普通团队。可以创建一个管理员团队来管理某些版本库,其成员可以对这些版本库做任何事情。只有所有者或管理员团队的成员才能创建新团队。
### 普通团队
组织中的普通团队具有可以根据`单元(Unit)`进行权限设置。它可以有成员和存储库范围。
- 一个团队可以访问所属组织的所有仓库或特殊仓库。
- 也可以设置该团队是否有创建新仓库的权限。
可以通过创建 "普通团队",并通过权限控制对其行为进行限制。一名成员可以加入多个团队。

View File

@ -17,9 +17,45 @@ menu:
# 合并请求
## 在`合并请求`中使用“Work In Progress”标记
合并请求(PR)是一种提出对仓库进行更改的方式。
它是一种将一个分支合并到另一个分支的请求,附带有对所做更改的描述。
合并请求通常用作贡献者对仓库贡献代码的方式,仓库的维护者可以通过对合并请求进行审查来决定是否接受这些更改。
您可以通过在一个进行中的 pull request 的标题上添加前缀 `WIP:` 或者 `[WIP]`(此处大小写敏感)来防止它被意外合并,具体的前缀设置可以在配置文件 `app.ini` 中找到:
## 创建合并请求
要创建合并请求,您需要遵循以下步骤:
1. **Fork 仓库** - 如果您没有直接对仓库进行更改的权限,您需要将仓库 fork 到您自己的账户中。
这将创建一个您可以对其进行更改的仓库副本。
2. **创建分支(可选)** - 在 fork 的仓库中创建一个新分支,该分支包含您要提出的更改。
给分支取一个描述性的名称,以指示更改的内容。
3. **进行更改** - 进行您想要的更改,提交并将其推送到 fork 的仓库中。
4. **创建合并请求** - 转到原始仓库并转到“合并请求”选项卡。单击“新建合并请求”按钮,并将您的新分支选择为源分支。
为您的合并请求输入描述性标题和描述,然后单击“创建合并请求”。
## 评审合并请求
创建合并请求后,将触发评审流程。仓库的维护者将收到合并请求的通知,并可以审查所做的更改。
他们可以留下评论、请求更改或批准更改。
如果维护者请求更改,您需要在分支中进行这些更改,并将更改推送到 fork 的仓库中。
合并请求将自动使用新更改进行更新。
如果维护者批准更改,他们可以将合并请求合并到仓库中。
## 关闭合并请求
如果您不接受该合并请求,您可以关闭它。
要关闭合并请求,请转到打开的合并请求并单击“关闭合并请求”按钮。这将关闭合并请求并且不会将其合并。
## 使用“Work In Progress”标记
在合并请求中使用“Work In Progress”标记可以防止合并请求被意外合并。
要将合并请求标记为“Work In Progress”您必须在其标题中添加前缀`WIP:``[WIP]`(不区分大小写)。
标记前缀可以在您的`app.ini`文件中进行配置:
```
[repository.pull-request]

View File

@ -88,3 +88,16 @@ menu:
4. 选择**添加推送镜像**以保存配置。
仓库会很快进行推送。要强制推送,请选择**立即同步**按钮。
### 镜像现有的 ssh 仓库
当前Gitea 不支持从 ssh 仓库进行镜像。如果您想要镜像一个 ssh 仓库,您需要将其转换为 http 仓库。您可以使用以下命令将现有的 ssh 仓库转换为 http 仓库:
1. 确保运行 gitea 的用户有权限访问您试图从 shell 镜像到的 git 仓库。
2. 在 Web 界面的版本库设置 > git 钩子中为镜像添加一个接收后钩子。
```
#!/usr/bin/env bash
git push --mirror --quiet git@github.com:username/repository.git &>/dev/null &
echo "GitHub mirror initiated .."
```

View File

@ -47,6 +47,8 @@ a/b/c/d.json
在与上述通配符匹配的任何文件中,将会扩展某些变量。
文件名和路径的匹配也可以被扩展,并且会经过谨慎的清理处理,以支持跨平台的文件系统。
所有变量都必须采用`$VAR``${VAR}`的形式。要转义扩展,使用双重`$$`,例如`$$VAR``$${VAR}`
| 变量 | 扩展为 | 可转换 |

View File

@ -7,6 +7,7 @@
exclusive: false
num_issues: 2
num_closed_issues: 0
archived_unix: 0
-
id: 2
@ -17,6 +18,7 @@
exclusive: false
num_issues: 1
num_closed_issues: 1
archived_unix: 0
-
id: 3
@ -27,6 +29,7 @@
exclusive: false
num_issues: 0
num_closed_issues: 0
archived_unix: 0
-
id: 4
@ -37,6 +40,7 @@
exclusive: false
num_issues: 1
num_closed_issues: 0
archived_unix: 0
-
id: 5
@ -47,6 +51,7 @@
exclusive: false
num_issues: 0
num_closed_issues: 0
archived_unix: 0
-
id: 6
@ -57,6 +62,7 @@
exclusive: false
num_issues: 0
num_closed_issues: 0
archived_unix: 0
-
id: 7
@ -67,6 +73,7 @@
exclusive: true
num_issues: 0
num_closed_issues: 0
archived_unix: 0
-
id: 8
@ -77,6 +84,7 @@
exclusive: true
num_issues: 0
num_closed_issues: 0
archived_unix: 0
-
id: 9
@ -87,3 +95,4 @@
exclusive: true
num_issues: 0
num_closed_issues: 0
archived_unix: 0

View File

@ -97,6 +97,8 @@ type Label struct {
QueryString string `xorm:"-"`
IsSelected bool `xorm:"-"`
IsExcluded bool `xorm:"-"`
ArchivedUnix timeutil.TimeStamp `xorm:"DEFAULT NULL"`
}
func init() {
@ -109,6 +111,15 @@ func (l *Label) CalOpenIssues() {
l.NumOpenIssues = l.NumIssues - l.NumClosedIssues
}
// SetArchived set the label as archived
func (l *Label) SetArchived(isArchived bool) {
if isArchived && l.ArchivedUnix.IsZero() {
l.ArchivedUnix = timeutil.TimeStampNow()
} else {
l.ArchivedUnix = timeutil.TimeStamp(0)
}
}
// CalOpenOrgIssues calculates the open issues of a label for a specific repo
func (l *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64) {
counts, _ := CountIssuesByRepo(ctx, &IssuesOptions{
@ -153,6 +164,11 @@ func (l *Label) BelongsToOrg() bool {
return l.OrgID > 0
}
// IsArchived returns true if label is an archived
func (l *Label) IsArchived() bool {
return l.ArchivedUnix > 0
}
// BelongsToRepo returns true if label is a repository label
func (l *Label) BelongsToRepo() bool {
return l.RepoID > 0
@ -211,7 +227,7 @@ func UpdateLabel(l *Label) error {
}
l.Color = color
return updateLabelCols(db.DefaultContext, l, "name", "description", "color", "exclusive")
return updateLabelCols(db.DefaultContext, l, "name", "description", "color", "exclusive", "archived_unix")
}
// DeleteLabel delete a label

View File

@ -11,6 +11,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
)
@ -259,11 +260,12 @@ func TestUpdateLabel(t *testing.T) {
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
// make sure update wont overwrite it
update := &issues_model.Label{
ID: label.ID,
Color: "#ffff00",
Name: "newLabelName",
Description: label.Description,
Exclusive: false,
ID: label.ID,
Color: "#ffff00",
Name: "newLabelName",
Description: label.Description,
Exclusive: false,
ArchivedUnix: timeutil.TimeStamp(0),
}
label.Color = update.Color
label.Name = update.Name
@ -273,6 +275,7 @@ func TestUpdateLabel(t *testing.T) {
assert.EqualValues(t, label.Color, newLabel.Color)
assert.EqualValues(t, label.Name, newLabel.Name)
assert.EqualValues(t, label.Description, newLabel.Description)
assert.EqualValues(t, newLabel.ArchivedUnix, 0)
unittest.CheckConsistencyFor(t, &issues_model.Label{}, &repo_model.Repository{})
}

View File

@ -522,6 +522,8 @@ var migrations = []Migration{
NewMigration("Drop deleted branch table", v1_21.DropDeletedBranchTable),
// v270 -> v271
NewMigration("Fix PackageProperty typo", v1_21.FixPackagePropertyTypo),
// v271 -> v272
NewMigration("Allow archiving labels", v1_21.AddArchivedUnixColumInLabelTable),
}
// GetCurrentDBVersion returns the current db version

View File

@ -0,0 +1,16 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_21 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func AddArchivedUnixColumInLabelTable(x *xorm.Engine) error {
type Label struct {
ArchivedUnix timeutil.TimeStamp `xorm:"DEFAULT NULL"`
}
return x.Sync(new(Label))
}

View File

@ -391,7 +391,13 @@ func (repo *Repository) MustGetUnit(ctx context.Context, tp unit.Type) *RepoUnit
Type: tp,
Config: new(IssuesConfig),
}
} else if tp == unit.TypeActions {
return &RepoUnit{
Type: tp,
Config: new(ActionsConfig),
}
}
return &RepoUnit{
Type: tp,
Config: new(UnitConfig),

View File

@ -6,6 +6,7 @@ package repo
import (
"context"
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unit"
@ -162,6 +163,42 @@ func (cfg *PullRequestsConfig) GetDefaultMergeStyle() MergeStyle {
return MergeStyleMerge
}
type ActionsConfig struct {
DisabledWorkflows []string
}
func (cfg *ActionsConfig) EnableWorkflow(file string) {
cfg.DisabledWorkflows = util.SliceRemoveAll(cfg.DisabledWorkflows, file)
}
func (cfg *ActionsConfig) ToString() string {
return strings.Join(cfg.DisabledWorkflows, ",")
}
func (cfg *ActionsConfig) IsWorkflowDisabled(file string) bool {
return util.SliceContains(cfg.DisabledWorkflows, file)
}
func (cfg *ActionsConfig) DisableWorkflow(file string) {
for _, workflow := range cfg.DisabledWorkflows {
if file == workflow {
return
}
}
cfg.DisabledWorkflows = append(cfg.DisabledWorkflows, file)
}
// FromDB fills up a ActionsConfig from serialized format.
func (cfg *ActionsConfig) FromDB(bs []byte) error {
return json.UnmarshalHandleDoubleEncode(bs, &cfg)
}
// ToDB exports a ActionsConfig to a serialized format.
func (cfg *ActionsConfig) ToDB() ([]byte, error) {
return json.Marshal(cfg)
}
// BeforeSet is invoked from XORM before setting the value of a field of this object.
func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
switch colName {
@ -175,7 +212,9 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
r.Config = new(PullRequestsConfig)
case unit.TypeIssues:
r.Config = new(IssuesConfig)
case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects, unit.TypePackages, unit.TypeActions:
case unit.TypeActions:
r.Config = new(ActionsConfig)
case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects, unit.TypePackages:
fallthrough
default:
r.Config = new(UnitConfig)
@ -218,6 +257,11 @@ func (r *RepoUnit) ExternalTrackerConfig() *ExternalTrackerConfig {
return r.Config.(*ExternalTrackerConfig)
}
// ActionsConfig returns config for unit.ActionsConfig
func (r *RepoUnit) ActionsConfig() *ActionsConfig {
return r.Config.(*ActionsConfig)
}
func getUnitsByRepoID(ctx context.Context, repoID int64) (units []*RepoUnit, err error) {
var tmpUnits []*RepoUnit
if err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Find(&tmpUnits); err != nil {

View File

@ -0,0 +1,30 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestActionsConfig(t *testing.T) {
cfg := &ActionsConfig{}
cfg.DisableWorkflow("test1.yaml")
assert.EqualValues(t, []string{"test1.yaml"}, cfg.DisabledWorkflows)
cfg.DisableWorkflow("test1.yaml")
assert.EqualValues(t, []string{"test1.yaml"}, cfg.DisabledWorkflows)
cfg.EnableWorkflow("test1.yaml")
assert.EqualValues(t, []string{}, cfg.DisabledWorkflows)
cfg.EnableWorkflow("test1.yaml")
assert.EqualValues(t, []string{}, cfg.DisabledWorkflows)
cfg.DisableWorkflow("test1.yaml")
cfg.DisableWorkflow("test2.yaml")
cfg.DisableWorkflow("test3.yaml")
assert.EqualValues(t, "test1.yaml,test2.yaml,test3.yaml", cfg.ToString())
}

View File

@ -49,12 +49,12 @@ func NewJwtSecret() ([]byte, error) {
}
// NewJwtSecretBase64 generates a new base64 encoded value intended to be used for JWT secrets.
func NewJwtSecretBase64() (string, error) {
func NewJwtSecretBase64() ([]byte, string, error) {
bytes, err := NewJwtSecret()
if err != nil {
return "", err
return nil, "", err
}
return base64.RawURLEncoding.EncodeToString(bytes), nil
return bytes, base64.RawURLEncoding.EncodeToString(bytes), nil
}
// NewSecretKey generate a new value intended to be used by SECRET_KEY.

View File

@ -9,6 +9,7 @@ import (
"time"
"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/util"
)
// LFS represents the configuration for Git LFS
@ -56,17 +57,14 @@ func loadLFSFrom(rootCfg ConfigProvider) error {
LFS.HTTPAuthExpiry = sec.Key("LFS_HTTP_AUTH_EXPIRY").MustDuration(24 * time.Hour)
if !LFS.StartServer {
if !LFS.StartServer || !InstallLock {
return nil
}
LFS.JWTSecretBase64 = loadSecret(rootCfg.Section("server"), "LFS_JWT_SECRET_URI", "LFS_JWT_SECRET")
LFS.JWTSecretBytes = make([]byte, 32)
n, err := base64.RawURLEncoding.Decode(LFS.JWTSecretBytes, []byte(LFS.JWTSecretBase64))
if (err != nil || n != 32) && InstallLock {
LFS.JWTSecretBase64, err = generate.NewJwtSecretBase64()
LFS.JWTSecretBytes, err = util.Base64FixedDecode(base64.RawURLEncoding, []byte(LFS.JWTSecretBase64), 32)
if err != nil {
LFS.JWTSecretBytes, LFS.JWTSecretBase64, err = generate.NewJwtSecretBase64()
if err != nil {
return fmt.Errorf("error generating JWT Secret for custom config: %v", err)
}

View File

@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
)
// OAuth2UsernameType is enum describing the way gitea 'name' should be generated from oauth2 data
@ -129,21 +130,19 @@ func loadOAuth2From(rootCfg ConfigProvider) {
}
if InstallLock {
key := make([]byte, 32)
n, err := base64.RawURLEncoding.Decode(key, []byte(OAuth2.JWTSecretBase64))
if err != nil || n != 32 {
key, err = generate.NewJwtSecret()
if _, err := util.Base64FixedDecode(base64.RawURLEncoding, []byte(OAuth2.JWTSecretBase64), 32); err != nil {
key, err := generate.NewJwtSecret()
if err != nil {
log.Fatal("error generating JWT secret: %v", err)
}
secretBase64 := base64.RawURLEncoding.EncodeToString(key)
OAuth2.JWTSecretBase64 = base64.RawURLEncoding.EncodeToString(key)
saveCfg, err := rootCfg.PrepareSaving()
if err != nil {
log.Fatal("save oauth2.JWT_SECRET failed: %v", err)
}
rootCfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
saveCfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
rootCfg.Section("oauth2").Key("JWT_SECRET").SetValue(OAuth2.JWTSecretBase64)
saveCfg.Section("oauth2").Key("JWT_SECRET").SetValue(OAuth2.JWTSecretBase64)
if err := saveCfg.Save(); err != nil {
log.Fatal("save oauth2.JWT_SECRET failed: %v", err)
}

View File

@ -11,6 +11,8 @@ type Label struct {
Name string `json:"name"`
// example: false
Exclusive bool `json:"exclusive"`
// example: false
IsArchived bool `json:"is_archived"`
// example: 00aabb
Color string `json:"color"`
Description string `json:"description"`
@ -27,6 +29,8 @@ type CreateLabelOption struct {
// example: #00aabb
Color string `json:"color" binding:"Required"`
Description string `json:"description"`
// example: false
IsArchived bool `json:"is_archived"`
}
// EditLabelOption options for editing a label
@ -37,6 +41,8 @@ type EditLabelOption struct {
// example: #00aabb
Color *string `json:"color"`
Description *string `json:"description"`
// example: false
IsArchived *bool `json:"is_archived"`
}
// IssueLabelsOption a collection of labels

View File

@ -71,7 +71,7 @@ func (ct SniffedType) IsRepresentableAsText() bool {
return ct.IsText() || ct.IsSvgImage()
}
// IsBrowsableType returns whether a non-text type can be displayed in a browser
// IsBrowsableBinaryType returns whether a non-text type can be displayed in a browser
func (ct SniffedType) IsBrowsableBinaryType() bool {
return ct.IsImage() || ct.IsSvgImage() || ct.IsPDF() || ct.IsVideo() || ct.IsAudio()
}
@ -116,6 +116,17 @@ func DetectContentType(data []byte) SniffedType {
}
}
if ct == "application/ogg" {
dataHead := data
if len(dataHead) > 256 {
dataHead = dataHead[:256] // only need to do a quick check for the file header
}
if bytes.Contains(dataHead, []byte("theora")) || bytes.Contains(dataHead, []byte("dirac")) {
ct = "video/ogg" // ogg is only used for some video formats, and it's not popular
} else {
ct = "audio/ogg" // for most cases, it is used as an audio container
}
}
return SniffedType{ct}
}

View File

@ -6,6 +6,7 @@ package typesniffer
import (
"bytes"
"encoding/base64"
"encoding/hex"
"strings"
"testing"
@ -121,3 +122,15 @@ func TestDetectContentTypeFromReader(t *testing.T) {
assert.NoError(t, err)
assert.True(t, st.IsAudio())
}
func TestDetectContentTypeOgg(t *testing.T) {
oggAudio, _ := hex.DecodeString("4f67675300020000000000000000352f0000000000007dc39163011e01766f72626973000000000244ac0000000000000071020000000000b8014f6767530000")
st, err := DetectContentTypeFromReader(bytes.NewReader(oggAudio))
assert.NoError(t, err)
assert.True(t, st.IsAudio())
oggVideo, _ := hex.DecodeString("4f676753000200000000000000007d9747ef000000009b59daf3012a807468656f7261030201001e00110001e000010e00020000001e00000001000001000001")
st, err = DetectContentTypeFromReader(bytes.NewReader(oggVideo))
assert.NoError(t, err)
assert.True(t, st.IsVideo())
}

View File

@ -6,6 +6,7 @@ package util
import (
"bytes"
"crypto/rand"
"encoding/base64"
"fmt"
"math/big"
"strconv"
@ -261,3 +262,13 @@ func ToFloat64(number any) (float64, error) {
func ToPointer[T any](val T) *T {
return &val
}
func Base64FixedDecode(encoding *base64.Encoding, src []byte, length int) ([]byte, error) {
decoded := make([]byte, encoding.DecodedLen(len(src))+3)
if n, err := encoding.Decode(decoded, src); err != nil {
return nil, err
} else if n != length {
return nil, fmt.Errorf("invalid base64 decoded length: %d, expects: %d", n, length)
}
return decoded[:length], nil
}

View File

@ -4,6 +4,7 @@
package util
import (
"encoding/base64"
"regexp"
"strings"
"testing"
@ -233,3 +234,16 @@ func TestToPointer(t *testing.T) {
val123 := 123
assert.False(t, &val123 == ToPointer(val123))
}
func TestBase64FixedDecode(t *testing.T) {
_, err := Base64FixedDecode(base64.RawURLEncoding, []byte("abcd"), 32)
assert.ErrorContains(t, err, "invalid base64 decoded length")
_, err = Base64FixedDecode(base64.RawURLEncoding, []byte(strings.Repeat("a", 64)), 32)
assert.ErrorContains(t, err, "invalid base64 decoded length")
str32 := strings.Repeat("x", 32)
encoded32 := base64.RawURLEncoding.EncodeToString([]byte(str32))
decoded32, err := Base64FixedDecode(base64.RawURLEncoding, []byte(encoded32), 32)
assert.NoError(t, err)
assert.Equal(t, str32, string(decoded32))
}

View File

@ -1491,6 +1491,8 @@ issues.label_title = Name
issues.label_description = Description
issues.label_color = Color
issues.label_exclusive = Exclusive
issues.label_archive = Archive Label
issues.label_archive_tooltip= Archived labels are excluded from the label search when applying labels to an issue. Existing labels on issues remain unaffected, allowing you to retire obsolete labels without losing information.
issues.label_exclusive_desc = Name the label <code>scope/item</code> to make it mutually exclusive with other <code>scope/</code> labels.
issues.label_exclusive_warning = Any conflicting scoped labels will be removed when editing the labels of an issue or pull request.
issues.label_count = %d labels
@ -3489,6 +3491,11 @@ runs.status_no_select = All status
runs.no_results = No results matched.
runs.no_runs = The workflow has no runs yet.
workflow.disable = Disable Workflow
workflow.disable_success = Workflow '%s' disabled successfully.
workflow.enable = Enable Workflow
workflow.enable_success = Workflow '%s' enabled successfully.
need_approval_desc = Need approval to run workflows for fork pull request.
variables = Variables

View File

@ -209,6 +209,7 @@ func EditLabel(ctx *context.APIContext) {
if form.Description != nil {
l.Description = *form.Description
}
l.SetArchived(form.IsArchived != nil && *form.IsArchived)
if err := issues_model.UpdateLabel(l); err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateLabel", err)
return

View File

@ -151,7 +151,6 @@ func CreateLabel(ctx *context.APIContext) {
return
}
form.Color = color
l := &issues_model.Label{
Name: form.Name,
Exclusive: form.Exclusive,
@ -159,6 +158,7 @@ func CreateLabel(ctx *context.APIContext) {
RepoID: ctx.Repo.Repository.ID,
Description: form.Description,
}
l.SetArchived(form.IsArchived)
if err := issues_model.NewLabel(ctx, l); err != nil {
ctx.Error(http.StatusInternalServerError, "NewLabel", err)
return
@ -231,6 +231,7 @@ func EditLabel(ctx *context.APIContext) {
if form.Description != nil {
l.Description = *form.Description
}
l.SetArchived(form.IsArchived != nil && *form.IsArchived)
if err := issues_model.UpdateLabel(l); err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateLabel", err)
return

View File

@ -415,7 +415,7 @@ func SubmitInstall(ctx *context.Context) {
cfg.Section("server").Key("LFS_START_SERVER").SetValue("true")
cfg.Section("lfs").Key("PATH").SetValue(form.LFSRootPath)
var lfsJwtSecret string
if lfsJwtSecret, err = generate.NewJwtSecretBase64(); err != nil {
if _, lfsJwtSecret, err = generate.NewJwtSecretBase64(); err != nil {
ctx.RenderWithErr(ctx.Tr("install.lfs_jwt_secret_failed", err), tplInstall, &form)
return
}

View File

@ -75,6 +75,7 @@ func UpdateLabel(ctx *context.Context) {
l.Exclusive = form.Exclusive
l.Description = form.Description
l.Color = form.Color
l.SetArchived(form.IsArchived)
if err := issues_model.UpdateLabel(l); err != nil {
ctx.ServerError("UpdateLabel", err)
return

View File

@ -137,6 +137,15 @@ func List(ctx *context.Context) {
actorID := ctx.FormInt64("actor")
status := ctx.FormInt("status")
ctx.Data["CurWorkflow"] = workflow
actionsConfig := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions).ActionsConfig()
ctx.Data["ActionsConfig"] = actionsConfig
if len(workflow) > 0 && ctx.Repo.IsAdmin() {
ctx.Data["AllowDisableOrEnableWorkflow"] = true
ctx.Data["CurWorkflowDisabled"] = actionsConfig.IsWorkflowDisabled(workflow)
}
// if status or actor query param is not given to frontend href, (href="/<repoLink>/actions")
// they will be 0 by default, which indicates get all status or actors
ctx.Data["CurActor"] = actorID

View File

@ -17,6 +17,7 @@ import (
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/base"
@ -572,3 +573,43 @@ func ArtifactsDownloadView(ctx *context_module.Context) {
}
}
}
func DisableWorkflowFile(ctx *context_module.Context) {
disableOrEnableWorkflowFile(ctx, false)
}
func EnableWorkflowFile(ctx *context_module.Context) {
disableOrEnableWorkflowFile(ctx, true)
}
func disableOrEnableWorkflowFile(ctx *context_module.Context, isEnable bool) {
workflow := ctx.FormString("workflow")
if len(workflow) == 0 {
ctx.ServerError("workflow", nil)
return
}
cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
cfg := cfgUnit.ActionsConfig()
if isEnable {
cfg.EnableWorkflow(workflow)
} else {
cfg.DisableWorkflow(workflow)
}
if err := repo_model.UpdateRepoUnit(cfgUnit); err != nil {
ctx.ServerError("UpdateRepoUnit", err)
return
}
if isEnable {
ctx.Flash.Success(ctx.Tr("actions.workflow.enable_success", workflow))
} else {
ctx.Flash.Success(ctx.Tr("actions.workflow.disable_success", workflow))
}
redirectURL := fmt.Sprintf("%s/actions?workflow=%s&actor=%s&status=%s", ctx.Repo.RepoLink, url.QueryEscape(workflow),
url.QueryEscape(ctx.FormString("actor")), url.QueryEscape(ctx.FormString("status")))
ctx.JSONRedirect(redirectURL)
}

View File

@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/label"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/forms"
issue_service "code.gitea.io/gitea/services/issue"
@ -111,11 +112,12 @@ func NewLabel(ctx *context.Context) {
}
l := &issues_model.Label{
RepoID: ctx.Repo.Repository.ID,
Name: form.Title,
Exclusive: form.Exclusive,
Description: form.Description,
Color: form.Color,
RepoID: ctx.Repo.Repository.ID,
Name: form.Title,
Exclusive: form.Exclusive,
Description: form.Description,
Color: form.Color,
ArchivedUnix: timeutil.TimeStamp(0),
}
if err := issues_model.NewLabel(ctx, l); err != nil {
ctx.ServerError("NewLabel", err)
@ -137,11 +139,12 @@ func UpdateLabel(ctx *context.Context) {
}
return
}
l.Name = form.Title
l.Exclusive = form.Exclusive
l.Description = form.Description
l.Color = form.Color
l.SetArchived(form.IsArchived)
if err := issues_model.UpdateLabel(l); err != nil {
ctx.ServerError("UpdateLabel", err)
return

View File

@ -97,9 +97,10 @@ func TestUpdateLabel(t *testing.T) {
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
web.SetForm(ctx, &forms.CreateLabelForm{
ID: 2,
Title: "newnameforlabel",
Color: "#abcdef",
ID: 2,
Title: "newnameforlabel",
Color: "#abcdef",
IsArchived: true,
})
UpdateLabel(ctx)
assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())

View File

@ -1200,6 +1200,8 @@ func registerRoutes(m *web.Route) {
m.Group("/actions", func() {
m.Get("", actions.List)
m.Post("/disable", reqRepoAdmin, actions.DisableWorkflowFile)
m.Post("/enable", reqRepoAdmin, actions.EnableWorkflowFile)
m.Group("/runs/{run}", func() {
m.Combo("").

View File

@ -150,7 +150,14 @@ func notify(ctx context.Context, input *notifyInput) error {
if len(workflows) == 0 {
log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RepoPath(), commit.ID)
} else {
actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig()
for _, wf := range workflows {
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
log.Trace("repo %s has disable workflows %s", input.Repo.RepoPath(), wf.EntryName)
continue
}
if wf.TriggerEvent != actions_module.GithubEventPullRequestTarget {
detectedWorkflows = append(detectedWorkflows, wf)
}

View File

@ -336,16 +336,7 @@ func InitSigningKey() error {
// loadSymmetricKey checks if the configured secret is valid.
// If it is not valid, it will return an error.
func loadSymmetricKey() (any, error) {
key := make([]byte, 32)
n, err := base64.RawURLEncoding.Decode(key, []byte(setting.OAuth2.JWTSecretBase64))
if err != nil {
return nil, err
}
if n != 32 {
return nil, fmt.Errorf("JWT secret must be 32 bytes long")
}
return key, nil
return util.Base64FixedDecode(base64.RawURLEncoding, []byte(setting.OAuth2.JWTSecretBase64), 32)
}
// loadOrCreateAsymmetricKey checks if the configured private key exists.

View File

@ -56,7 +56,7 @@ func (p *AuthSourceProvider) DisplayName() string {
func (p *AuthSourceProvider) IconHTML() template.HTML {
if p.iconURL != "" {
img := fmt.Sprintf(`<img class="gt-mr-3" width="20" height="20" src="%s" alt="%s">`,
img := fmt.Sprintf(`<img class="gt-object-contain gt-mr-3" width="20" height="20" src="%s" alt="%s">`,
html.EscapeString(p.iconURL), html.EscapeString(p.DisplayName()),
)
return template.HTML(img)

View File

@ -208,6 +208,7 @@ func ToLabel(label *issues_model.Label, repo *repo_model.Repository, org *user_m
Exclusive: label.Exclusive,
Color: strings.TrimLeft(label.Color, "#"),
Description: label.Description,
IsArchived: label.IsArchived(),
}
// calculate URL

View File

@ -569,6 +569,7 @@ type CreateLabelForm struct {
ID int64
Title string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_title"`
Exclusive bool `form:"exclusive"`
IsArchived bool `form:"is_archived"`
Description string `binding:"MaxSize(200)" locale:"repo.issues.label_description"`
Color string `binding:"Required;MaxSize(7)" locale:"repo.issues.label_color"`
}

View File

@ -2,6 +2,8 @@
<div class="page-content repository actions">
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
<div class="ui stackable grid">
<div class="four wide column">
<div class="ui fluid vertical menu">
@ -13,12 +15,16 @@
{{svg "octicon-alert" 16 "text red"}}
</span>
{{end}}
{{if $.ActionsConfig.IsWorkflowDisabled .Entry.Name}}
<div class="ui red label">{{$.locale.Tr "disabled"}}</div>
{{end}}
</a>
{{end}}
</div>
</div>
<div class="twelve wide column content">
<div class="ui secondary filter stackable menu gt-je">
<div class="ui secondary filter menu gt-je gt-df gt-ac">
<!-- Actor -->
<div class="ui{{if not .Actors}} disabled{{end}} dropdown jump item">
<span class="text">{{.locale.Tr "actions.runs.actor"}}</span>
@ -57,6 +63,17 @@
{{end}}
</div>
</div>
{{if .AllowDisableOrEnableWorkflow}}
<button class="ui jump dropdown btn interact-bg gt-p-3">
{{svg "octicon-kebab-horizontal"}}
<div class="menu">
<a class="item link-action" data-url="{{$.Link}}/{{if .CurWorkflowDisabled}}enable{{else}}disable{{end}}?workflow={{$.CurWorkflow}}&actor={{.CurActor}}&status={{$.CurStatus}}">
{{if .CurWorkflowDisabled}}{{.locale.Tr "actions.workflow.enable"}}{{else}}{{.locale.Tr "actions.workflow.disable"}}{{end}}
</a>
</div>
</button>
{{end}}
</div>
{{template "repo/actions/runs_list" .}}
</div>

View File

@ -33,6 +33,16 @@
<div class="desc gt-ml-2 gt-mt-3 gt-hidden label-exclusive-warning">
{{svg "octicon-alert"}} {{.locale.Tr "repo.issues.label_exclusive_warning" | Safe}}
</div>
<br>
</div>
<div class="field label-is-archived-input-field">
<div class="ui checkbox">
<input class="label-is-archived-input" name="is_archived" type="checkbox">
<label>{{.locale.Tr "repo.issues.label_archive"}}</label>
</div>
<i class="gt-ml-2" data-tooltip-content={{.locale.Tr "repo.issues.label_archive_tooltip"}}>
{{svg "octicon-info"}}
</i>
</div>
<div class="field">
<label for="description">{{.locale.Tr "repo.issues.label_description"}}</label>

View File

@ -44,10 +44,10 @@
</div>
<div class="label-operation">
{{if and (not $.PageIsOrgSettingsLabels) (not $.Repository.IsArchived) (or $.CanWriteIssues $.CanWritePulls)}}
<a class="edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
<a class="edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} {{if gt .ArchivedUnix 0}}data-is-archived{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
<a class="delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a>
{{else if $.PageIsOrgSettingsLabels}}
<a class="edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
<a class="edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" {{if .Exclusive}}data-exclusive{{end}} {{if gt .ArchivedUnix 0}}data-is-archived{{end}} data-num-issues="{{.NumIssues}}" data-description="{{.Description}}" data-color={{.Color}}>{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}}</a>
<a class="delete-button" href="#" data-url="{{$.Link}}/delete" data-id="{{.ID}}">{{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}</a>
{{end}}
</div>

View File

@ -17057,6 +17057,11 @@
"x-go-name": "Exclusive",
"example": false
},
"is_archived": {
"type": "boolean",
"x-go-name": "IsArchived",
"example": false
},
"name": {
"type": "string",
"x-go-name": "Name"
@ -18001,6 +18006,11 @@
"x-go-name": "Exclusive",
"example": false
},
"is_archived": {
"type": "boolean",
"x-go-name": "IsArchived",
"example": false
},
"name": {
"type": "string",
"x-go-name": "Name"
@ -19479,6 +19489,11 @@
"format": "int64",
"x-go-name": "ID"
},
"is_archived": {
"type": "boolean",
"x-go-name": "IsArchived",
"example": false
},
"name": {
"type": "string",
"x-go-name": "Name"

View File

@ -653,6 +653,18 @@ a.label,
color: var(--color-text);
}
/* replace item margin on secondary menu items with gap and remove both the
negative margins on the menu as well as margin on the items */
.ui.secondary.menu {
margin-left: 0;
margin-right: 0;
gap: .35714286em;
}
.ui.secondary.menu .item {
margin-left: 0;
margin-right: 0;
}
.ui.secondary.menu .dropdown.item:hover,
.ui.secondary.menu a.item:hover {
color: var(--color-text);
@ -670,6 +682,11 @@ a.label,
padding-right: 0.85714286em;
}
/* remove the menu clearfix so that it won't add undesired gaps when using "gap" */
.ui.menu::after {
content: normal;
}
.ui.menu .dropdown.item .menu {
background: var(--color-body);
}

View File

@ -51,3 +51,9 @@
.dropzone .dz-preview:hover .dz-image img {
filter: opacity(0.5) !important;
}
.ui .field .dropzone .dz-preview .dz-progress {
/* by default the progress-bar is vertically centered (top: 50%), it's better to put it after the "details (size, filename)",
then the layout from top to bottom is: size, filename, progress */
top: 7em;
}

View File

@ -36,7 +36,7 @@ export function initCompLabelEdit(selector) {
$('.new-label.modal').modal({
onApprove() {
$('.new-label.form').trigger('submit');
}
},
}).modal('show');
return false;
});
@ -49,6 +49,9 @@ export function initCompLabelEdit(selector) {
const nameInput = $('.edit-label .label-name-input');
nameInput.val($(this).data('title'));
const isArchivedCheckbox = $('.edit-label .label-is-archived-input');
isArchivedCheckbox.prop('checked', this.hasAttribute('data-is-archived'));
const exclusiveCheckbox = $('.edit-label .label-exclusive-input');
exclusiveCheckbox.prop('checked', this.hasAttribute('data-exclusive'));
// Warn when label was previously not exclusive and used in issues
@ -64,7 +67,7 @@ export function initCompLabelEdit(selector) {
$('.edit-label.modal').modal({
onApprove() {
$('.edit-label.form').trigger('submit');
}
},
}).modal('show');
return false;
});