diff --git a/.gitattributes b/.gitattributes
index 52695f70c2..e218bbe25d 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -4,6 +4,7 @@
/assets/*.json linguist-generated
/public/assets/img/svg/*.svg linguist-generated
/templates/swagger/v1_json.tmpl linguist-generated
+/options/fileicon/** linguist-generated
/vendor/** -text -eol linguist-vendored
/web_src/js/vendor/** -text -eol linguist-vendored
Dockerfile.* linguist-language=Dockerfile
diff --git a/README.md b/README.md
index 5ae65cd2ac..017ca629d0 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
[](https://translate.gitea.com "Crowdin")
-[View this document in Chinese](./README_ZH.md)
+[繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md)
## Purpose
diff --git a/README.zh-cn.md b/README.zh-cn.md
new file mode 100644
index 0000000000..f34b25b945
--- /dev/null
+++ b/README.zh-cn.md
@@ -0,0 +1,206 @@
+# Gitea
+
+[](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
+[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+[](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
+[](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
+[](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
+[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
+[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
+[](https://opensource.org/licenses/MIT "License: MIT")
+[](https://gitpod.io/#https://github.com/go-gitea/gitea)
+[](https://translate.gitea.com "Crowdin")
+
+[English](./README.md) | [繁體中文](./README.zh-tw.md)
+
+## 目的
+
+这个项目的目标是提供最简单、最快速、最无痛的方式来设置自托管的 Git 服务。
+
+由于 Gitea 是用 Go 语言编写的,它可以在 Go 支持的所有平台和架构上运行,包括 Linux、macOS 和 Windows 的 x86、amd64、ARM 和 PowerPC 架构。这个项目自 2016 年 11 月从 [Gogs](https://gogs.io) [分叉](https://blog.gitea.com/welcome-to-gitea/) 而来,但已经有了很多变化。
+
+在线演示可以访问 [demo.gitea.com](https://demo.gitea.com)。
+
+要访问免费的 Gitea 服务(有一定数量的仓库限制),可以访问 [gitea.com](https://gitea.com/user/login)。
+
+要快速部署您自己的专用 Gitea 实例,可以在 [cloud.gitea.com](https://cloud.gitea.com) 开始免费试用。
+
+## 文件
+
+您可以在我们的官方 [文件网站](https://docs.gitea.com/) 上找到全面的文件。
+
+它包括安装、管理、使用、开发、贡献指南等,帮助您快速入门并有效地探索所有功能。
+
+如果您有任何建议或想要贡献,可以访问 [文件仓库](https://gitea.com/gitea/docs)
+
+## 构建
+
+从源代码树的根目录运行:
+
+ TAGS="bindata" make build
+
+如果需要 SQLite 支持:
+
+ TAGS="bindata sqlite sqlite_unlock_notify" make build
+
+`build` 目标分为两个子目标:
+
+- `make backend` 需要 [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) 中定义。
+- `make frontend` 需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
+
+需要互联网连接来下载 go 和 npm 模块。从包含预构建前端文件的官方源代码压缩包构建时,不会触发 `frontend` 目标,因此可以在没有 Node.js 的情况下构建。
+
+更多信息:https://docs.gitea.com/installation/install-from-source
+
+## 使用
+
+构建后,默认情况下会在源代码树的根目录生成一个名为 `gitea` 的二进制文件。要运行它,请使用:
+
+ ./gitea web
+
+> [!注意]
+> 如果您对使用我们的 API 感兴趣,我们提供了实验性支持,并附有 [文件](https://docs.gitea.com/api)。
+
+## 贡献
+
+预期的工作流程是:Fork -> Patch -> Push -> Pull Request
+
+> [!注意]
+>
+> 1. **在开始进行 Pull Request 之前,您必须阅读 [贡献者指南](CONTRIBUTING.md)。**
+> 2. 如果您在项目中发现了漏洞,请私下写信给 **security@gitea.io**。谢谢!
+
+## 翻译
+
+[](https://translate.gitea.com)
+
+翻译通过 [Crowdin](https://translate.gitea.com) 进行。如果您想翻译成新的语言,请在 Crowdin 项目中请求管理员添加新语言。
+
+您也可以创建一个 issue 来添加语言,或者在 discord 的 #translation 频道上询问。如果您需要上下文或发现一些翻译问题,可以在字符串上留言或在 Discord 上询问。对于一般的翻译问题,文档中有一个部分。目前有点空,但我们希望随着问题的出现而填充它。
+
+更多信息请参阅 [文件](https://docs.gitea.com/contributing/localization)。
+
+## 官方和第三方项目
+
+我们提供了一个官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一个名为 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一个 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
+
+我们在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 维护了一个 Gitea 相关项目的列表,您可以在那里发现更多的第三方项目,包括 SDK、插件、主题等。
+
+## 通讯
+
+[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+
+如果您有任何文件未涵盖的问题,可以在我们的 [Discord 服务器](https://discord.gg/Gitea) 上与我们联系,或者在 [discourse 论坛](https://forum.gitea.com/) 上创建帖子。
+
+## 作者
+
+- [维护者](https://github.com/orgs/go-gitea/people)
+- [贡献者](https://github.com/go-gitea/gitea/graphs/contributors)
+- [翻译者](options/locale/TRANSLATORS)
+
+## 支持者
+
+感谢所有支持者! 🙏 [[成为支持者](https://opencollective.com/gitea#backer)]
+
+
+
+## 赞助商
+
+通过成为赞助商来支持这个项目。您的标志将显示在这里,并带有链接到您的网站。 [[成为赞助商](https://opencollective.com/gitea#sponsor)]
+
+
+
+
+
+
+
+
+
+
+
+
+## 常见问题
+
+**Gitea 怎么发音?**
+
+Gitea 的发音是 [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY),就像 "gi-tea" 一样,g 是硬音。
+
+**为什么这个项目没有托管在 Gitea 实例上?**
+
+我们正在 [努力](https://github.com/go-gitea/gitea/issues/1029)。
+
+**在哪里可以找到安全补丁?**
+
+在 [发布日志](https://github.com/go-gitea/gitea/releases) 或 [变更日志](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,搜索关键词 `SECURITY` 以找到安全补丁。
+
+## 许可证
+
+这个项目是根据 MIT 许可证授权的。
+请参阅 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以获取完整的许可证文本。
+
+## 进一步信息
+
+
+寻找界面概述?查看这里!
+
+### 登录/注册页面
+
+
+
+
+### 用户仪表板
+
+
+
+
+
+
+### 用户资料
+
+
+
+### 探索
+
+
+
+
+
+### 仓库
+
+
+
+
+
+
+
+
+
+#### 仓库问题
+
+
+
+
+#### 仓库拉取请求
+
+
+
+
+
+
+#### 仓库操作
+
+
+
+
+#### 仓库活动
+
+
+
+
+
+
+### 组织
+
+
+
+
diff --git a/README.zh-tw.md b/README.zh-tw.md
new file mode 100644
index 0000000000..9de3f85dd5
--- /dev/null
+++ b/README.zh-tw.md
@@ -0,0 +1,206 @@
+# Gitea
+
+[](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
+[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+[](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
+[](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
+[](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
+[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
+[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
+[](https://opensource.org/licenses/MIT "License: MIT")
+[](https://gitpod.io/#https://github.com/go-gitea/gitea)
+[](https://translate.gitea.com "Crowdin")
+
+[English](./README.md) | [简体中文](./README.zh-cn.md)
+
+## 目的
+
+這個項目的目標是提供最簡單、最快速、最無痛的方式來設置自託管的 Git 服務。
+
+由於 Gitea 是用 Go 語言編寫的,它可以在 Go 支援的所有平台和架構上運行,包括 Linux、macOS 和 Windows 的 x86、amd64、ARM 和 PowerPC 架構。這個項目自 2016 年 11 月從 [Gogs](https://gogs.io) [分叉](https://blog.gitea.com/welcome-to-gitea/) 而來,但已經有了很多變化。
+
+在線演示可以訪問 [demo.gitea.com](https://demo.gitea.com)。
+
+要訪問免費的 Gitea 服務(有一定數量的倉庫限制),可以訪問 [gitea.com](https://gitea.com/user/login)。
+
+要快速部署您自己的專用 Gitea 實例,可以在 [cloud.gitea.com](https://cloud.gitea.com) 開始免費試用。
+
+## 文件
+
+您可以在我們的官方 [文件網站](https://docs.gitea.com/) 上找到全面的文件。
+
+它包括安裝、管理、使用、開發、貢獻指南等,幫助您快速入門並有效地探索所有功能。
+
+如果您有任何建議或想要貢獻,可以訪問 [文件倉庫](https://gitea.com/gitea/docs)
+
+## 構建
+
+從源代碼樹的根目錄運行:
+
+ TAGS="bindata" make build
+
+如果需要 SQLite 支援:
+
+ TAGS="bindata sqlite sqlite_unlock_notify" make build
+
+`build` 目標分為兩個子目標:
+
+- `make backend` 需要 [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) 中定義。
+- `make frontend` 需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
+
+需要互聯網連接來下載 go 和 npm 模塊。從包含預構建前端文件的官方源代碼壓縮包構建時,不會觸發 `frontend` 目標,因此可以在沒有 Node.js 的情況下構建。
+
+更多信息:https://docs.gitea.com/installation/install-from-source
+
+## 使用
+
+構建後,默認情況下會在源代碼樹的根目錄生成一個名為 `gitea` 的二進制文件。要運行它,請使用:
+
+ ./gitea web
+
+> [!注意]
+> 如果您對使用我們的 API 感興趣,我們提供了實驗性支援,並附有 [文件](https://docs.gitea.com/api)。
+
+## 貢獻
+
+預期的工作流程是:Fork -> Patch -> Push -> Pull Request
+
+> [!注意]
+>
+> 1. **在開始進行 Pull Request 之前,您必須閱讀 [貢獻者指南](CONTRIBUTING.md)。**
+> 2. 如果您在項目中發現了漏洞,請私下寫信給 **security@gitea.io**。謝謝!
+
+## 翻譯
+
+[](https://translate.gitea.com)
+
+翻譯通過 [Crowdin](https://translate.gitea.com) 進行。如果您想翻譯成新的語言,請在 Crowdin 項目中請求管理員添加新語言。
+
+您也可以創建一個 issue 來添加語言,或者在 discord 的 #translation 頻道上詢問。如果您需要上下文或發現一些翻譯問題,可以在字符串上留言或在 Discord 上詢問。對於一般的翻譯問題,文檔中有一個部分。目前有點空,但我們希望隨著問題的出現而填充它。
+
+更多信息請參閱 [文件](https://docs.gitea.com/contributing/localization)。
+
+## 官方和第三方項目
+
+我們提供了一個官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一個名為 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一個 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
+
+我們在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 維護了一個 Gitea 相關項目的列表,您可以在那裡發現更多的第三方項目,包括 SDK、插件、主題等。
+
+## 通訊
+
+[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
+
+如果您有任何文件未涵蓋的問題,可以在我們的 [Discord 服務器](https://discord.gg/Gitea) 上與我們聯繫,或者在 [discourse 論壇](https://forum.gitea.com/) 上創建帖子。
+
+## 作者
+
+- [維護者](https://github.com/orgs/go-gitea/people)
+- [貢獻者](https://github.com/go-gitea/gitea/graphs/contributors)
+- [翻譯者](options/locale/TRANSLATORS)
+
+## 支持者
+
+感謝所有支持者! 🙏 [[成為支持者](https://opencollective.com/gitea#backer)]
+
+
+
+## 贊助商
+
+通過成為贊助商來支持這個項目。您的標誌將顯示在這裡,並帶有鏈接到您的網站。 [[成為贊助商](https://opencollective.com/gitea#sponsor)]
+
+
+
+
+
+
+
+
+
+
+
+
+## 常見問題
+
+**Gitea 怎麼發音?**
+
+Gitea 的發音是 [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY),就像 "gi-tea" 一樣,g 是硬音。
+
+**為什麼這個項目沒有託管在 Gitea 實例上?**
+
+我們正在 [努力](https://github.com/go-gitea/gitea/issues/1029)。
+
+**在哪裡可以找到安全補丁?**
+
+在 [發佈日誌](https://github.com/go-gitea/gitea/releases) 或 [變更日誌](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,搜索關鍵詞 `SECURITY` 以找到安全補丁。
+
+## 許可證
+
+這個項目是根據 MIT 許可證授權的。
+請參閱 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以獲取完整的許可證文本。
+
+## 進一步信息
+
+
+尋找界面概述?查看這裡!
+
+### 登錄/註冊頁面
+
+
+
+
+### 用戶儀表板
+
+
+
+
+
+
+### 用戶資料
+
+
+
+### 探索
+
+
+
+
+
+### 倉庫
+
+
+
+
+
+
+
+
+
+#### 倉庫問題
+
+
+
+
+#### 倉庫拉取請求
+
+
+
+
+
+
+#### 倉庫操作
+
+
+
+
+#### 倉庫活動
+
+
+
+
+
+
+### 組織
+
+
+
+
diff --git a/README_ZH.md b/README_ZH.md
deleted file mode 100644
index 89c34f6b63..0000000000
--- a/README_ZH.md
+++ /dev/null
@@ -1,156 +0,0 @@
-# Gitea
-
-[](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
-[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
-[](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
-[](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
-[](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
-[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
-[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
-[](https://opensource.org/licenses/MIT "License: MIT")
-[](https://gitpod.io/#https://github.com/go-gitea/gitea)
-[](https://translate.gitea.com "Crowdin")
-
-[View this document in English](./README.md)
-
-## 目标
-
-Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux、macOS 和 Windows 以及各种架构,除了 x86 和 amd64,还包括 ARM 和 PowerPC。
-
-如果你想试用在线演示和报告问题,请访问 [demo.gitea.com](https://demo.gitea.com/)。
-
-如果你想使用免费的 Gitea 服务(有仓库数量限制),请访问 [gitea.com](https://gitea.com/user/login)。
-
-如果你想在 Gitea Cloud 上快速部署你自己独享的 Gitea 实例,请访问 [cloud.gitea.com](https://cloud.gitea.com) 开始免费试用。
-
-## 文档
-
-关于如何安装请访问我们的 [文档站](https://docs.gitea.com/zh-cn/category/installation),如果没有找到对应的文档,你也可以通过 [Discord - 英文](https://discord.gg/gitea) 和 QQ群 328432459 来和我们交流。
-
-## 编译
-
-在源代码的根目录下执行:
-
- TAGS="bindata" make build
-
-或者如果需要SQLite支持:
-
- TAGS="bindata sqlite sqlite_unlock_notify" make build
-
-编译过程会分成2个子任务:
-
-- `make backend`,需要 [Go Stable](https://go.dev/dl/),最低版本需求可查看 [go.mod](/go.mod)。
-- `make frontend`,需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
-
-你需要连接网络来下载 go 和 npm modules。当从 tar 格式的源文件编译时,其中包含了预编译的前端文件,因此 `make frontend` 将不会被执行。这允许编译时不需要 Node.js。
-
-更多信息: https://docs.gitea.com/installation/install-from-source
-
-## 使用
-
-编译之后,默认会在根目录下生成一个名为 `gitea` 的文件。你可以这样执行它:
-
- ./gitea web
-
-> [!注意]
-> 如果你要使用API,请参见 [API 文档](https://godoc.org/code.gitea.io/sdk/gitea)。
-
-## 贡献
-
-贡献流程:Fork -> Patch -> Push -> Pull Request
-
-> [!注意]
->
-> 1. **开始贡献代码之前请确保你已经看过了 [贡献者向导(英文)](CONTRIBUTING.md)**。
-> 2. 所有的安全问题,请私下发送邮件给 **security@gitea.io**。 谢谢!
-
-## 翻译
-
-[](https://translate.gitea.com)
-
-多语言翻译是基于Crowdin进行的。
-
-从 [文档](https://docs.gitea.com/contributing/localization) 中获取更多信息。
-
-## 官方和第三方项目
-
-Gitea 提供官方的 [go-sdk](https://gitea.com/gitea/go-sdk),以及名为 [tea](https://gitea.com/gitea/tea) 的 CLI 工具 和 用于 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
-
-[gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 是一个 Gitea 相关项目的列表,你可以在这里找到更多的第三方项目,包括 SDK、插件、主题等等。
-
-## 作者
-
-- [Maintainers](https://github.com/orgs/go-gitea/people)
-- [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
-- [Translators](options/locale/TRANSLATORS)
-
-## 授权许可
-
-本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件中。
-
-## 更多信息
-
-
-截图
-
-### 登录界面
-
-
-
-
-### 用户首页
-
-
-
-
-
-
-### 用户资料
-
-
-
-### 探索
-
-
-
-
-
-### 仓库
-
-
-
-
-
-
-
-
-
-#### 仓库工单
-
-
-
-
-#### 仓库合并请求
-
-
-
-
-
-
-#### 仓库 Actions
-
-
-
-
-#### 仓库动态
-
-
-
-
-
-
-### 组织
-
-
-
-
diff --git a/cmd/web.go b/cmd/web.go
index dc5c6de48a..e47b171455 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -213,6 +213,10 @@ func serveInstalled(ctx *cli.Context) error {
log.Fatal("Can not find APP_DATA_PATH %q", setting.AppDataPath)
}
+ // the AppDataTempDir is fully managed by us with a safe sub-path
+ // so it's safe to automatically remove the outdated files
+ setting.AppDataTempDir("").RemoveOutdated(3 * 24 * time.Hour)
+
// Override the provided port number within the configuration
if ctx.IsSet("port") {
if err := setPort(ctx.String("port")); err != nil {
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 07a6ebdcf2..8d39551168 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -197,13 +197,6 @@ RUN_USER = ; git
;; relative paths are made absolute relative to the APP_DATA_PATH
;SSH_SERVER_HOST_KEYS=ssh/gitea.rsa, ssh/gogs.rsa
;;
-;; Directory to create temporary files in when testing public keys using ssh-keygen,
-;; default is the system temporary directory.
-;SSH_KEY_TEST_PATH =
-;;
-;; Use `ssh-keygen` to parse public SSH keys. The value is passed to the shell. By default, Gitea does the parsing itself.
-;SSH_KEYGEN_PATH =
-;;
;; Enable SSH Authorized Key Backup when rewriting all keys, default is false
;SSH_AUTHORIZED_KEYS_BACKUP = false
;;
@@ -294,6 +287,9 @@ RUN_USER = ; git
;; Default path for App data
;APP_DATA_PATH = data ; relative paths will be made absolute with _`AppWorkPath`_
;;
+;; Base path for App's temp files, leave empty to use the managed tmp directory in APP_DATA_PATH
+;APP_TEMP_PATH =
+;;
;; Enable gzip compression for runtime-generated content, static resources excluded
;ENABLE_GZIP = false
;;
@@ -1069,15 +1065,6 @@ LEVEL = Info
;; Separate extensions with a comma. To line wrap files without an extension, just put a comma
;LINE_WRAP_EXTENSIONS = .txt,.md,.markdown,.mdown,.mkd,.livemd,
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;[repository.local]
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;
-;; Path for local repository copy. Defaults to `tmp/local-repo` (content gets deleted on gitea restart)
-;LOCAL_COPY_PATH = tmp/local-repo
-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[repository.upload]
@@ -1087,9 +1074,6 @@ LEVEL = Info
;; Whether repository file uploads are enabled. Defaults to `true`
;ENABLED = true
;;
-;; Path for uploads. Defaults to `data/tmp/uploads` (content gets deleted on gitea restart)
-;TEMP_PATH = data/tmp/uploads
-;;
;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
;ALLOWED_TYPES =
;;
@@ -2473,7 +2457,7 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set the maximum number of characters in a mermaid source. (Set to -1 to disable limits)
-;MERMAID_MAX_SOURCE_CHARACTERS = 5000
+;MERMAID_MAX_SOURCE_CHARACTERS = 50000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -2594,9 +2578,6 @@ LEVEL = Info
;; Currently, only `minio` and `azureblob` is supported.
;SERVE_DIRECT = false
;;
-;; Path for chunked uploads. Defaults to APP_DATA_PATH + `tmp/package-upload`
-;CHUNKED_UPLOAD_PATH = tmp/package-upload
-;;
;; Maximum count of package versions a single owner can have (`-1` means no limits)
;LIMIT_TOTAL_OWNER_COUNT = -1
;; Maximum size of packages a single owner can use (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
diff --git a/models/asymkey/ssh_key_fingerprint.go b/models/asymkey/ssh_key_fingerprint.go
index 1ed3b5df2a..4dcfe1f279 100644
--- a/models/asymkey/ssh_key_fingerprint.go
+++ b/models/asymkey/ssh_key_fingerprint.go
@@ -6,27 +6,13 @@ package asymkey
import (
"context"
"fmt"
- "strings"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
"golang.org/x/crypto/ssh"
"xorm.io/builder"
)
-// ___________.__ .__ __
-// \_ _____/|__| ____ ____ ________________________|__| _____/ |_
-// | __) | |/ \ / ___\_/ __ \_ __ \____ \_ __ \ |/ \ __\
-// | \ | | | \/ /_/ > ___/| | \/ |_> > | \/ | | \ |
-// \___ / |__|___| /\___ / \___ >__| | __/|__| |__|___| /__|
-// \/ \//_____/ \/ |__| \/
-//
-// This file contains functions for fingerprinting SSH keys
-//
// The database is used in checkKeyFingerprint however most of these functions probably belong in a module
// checkKeyFingerprint only checks if key fingerprint has been used as public key,
@@ -41,29 +27,6 @@ func checkKeyFingerprint(ctx context.Context, fingerprint string) error {
return nil
}
-func calcFingerprintSSHKeygen(publicKeyContent string) (string, error) {
- // Calculate fingerprint.
- tmpPath, err := writeTmpKeyFile(publicKeyContent)
- if err != nil {
- return "", err
- }
- defer func() {
- if err := util.Remove(tmpPath); err != nil {
- log.Warn("Unable to remove temporary key file: %s: Error: %v", tmpPath, err)
- }
- }()
- stdout, stderr, err := process.GetManager().Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath)
- if err != nil {
- if strings.Contains(stderr, "is not a public key file") {
- return "", ErrKeyUnableVerify{stderr}
- }
- return "", util.NewInvalidArgumentErrorf("'ssh-keygen -lf %s' failed with error '%s': %s", tmpPath, err, stderr)
- } else if len(stdout) < 2 {
- return "", util.NewInvalidArgumentErrorf("not enough output for calculating fingerprint: %s", stdout)
- }
- return strings.Split(stdout, " ")[1], nil
-}
-
func calcFingerprintNative(publicKeyContent string) (string, error) {
// Calculate fingerprint.
pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publicKeyContent))
@@ -75,15 +38,12 @@ func calcFingerprintNative(publicKeyContent string) (string, error) {
// CalcFingerprint calculate public key's fingerprint
func CalcFingerprint(publicKeyContent string) (string, error) {
- // Call the method based on configuration
- useNative := setting.SSH.KeygenPath == ""
- calcFn := util.Iif(useNative, calcFingerprintNative, calcFingerprintSSHKeygen)
- fp, err := calcFn(publicKeyContent)
+ fp, err := calcFingerprintNative(publicKeyContent)
if err != nil {
if IsErrKeyUnableVerify(err) {
return "", err
}
- return "", fmt.Errorf("CalcFingerprint(%s): %w", util.Iif(useNative, "native", "ssh-keygen"), err)
+ return "", fmt.Errorf("CalcFingerprint: %w", err)
}
return fp, nil
}
diff --git a/models/asymkey/ssh_key_parse.go b/models/asymkey/ssh_key_parse.go
index c843525718..46dcf4d894 100644
--- a/models/asymkey/ssh_key_parse.go
+++ b/models/asymkey/ssh_key_parse.go
@@ -13,12 +13,9 @@ import (
"errors"
"fmt"
"math/big"
- "os"
- "strconv"
"strings"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -175,20 +172,9 @@ func CheckPublicKeyString(content string) (_ string, err error) {
return content, nil
}
- var (
- fnName string
- keyType string
- length int
- )
- if len(setting.SSH.KeygenPath) == 0 {
- fnName = "SSHNativeParsePublicKey"
- keyType, length, err = SSHNativeParsePublicKey(content)
- } else {
- fnName = "SSHKeyGenParsePublicKey"
- keyType, length, err = SSHKeyGenParsePublicKey(content)
- }
+ keyType, length, err := SSHNativeParsePublicKey(content)
if err != nil {
- return "", fmt.Errorf("%s: %w", fnName, err)
+ return "", fmt.Errorf("SSHNativeParsePublicKey: %w", err)
}
log.Trace("Key info [native: %v]: %s-%d", setting.SSH.StartBuiltinServer, keyType, length)
@@ -258,56 +244,3 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) {
}
return "", 0, fmt.Errorf("unsupported key length detection for type: %s", pkey.Type())
}
-
-// writeTmpKeyFile writes key content to a temporary file
-// and returns the name of that file, along with any possible errors.
-func writeTmpKeyFile(content string) (string, error) {
- tmpFile, err := os.CreateTemp(setting.SSH.KeyTestPath, "gitea_keytest")
- if err != nil {
- return "", fmt.Errorf("TempFile: %w", err)
- }
- defer tmpFile.Close()
-
- if _, err = tmpFile.WriteString(content); err != nil {
- return "", fmt.Errorf("WriteString: %w", err)
- }
- return tmpFile.Name(), nil
-}
-
-// SSHKeyGenParsePublicKey extracts key type and length using ssh-keygen.
-func SSHKeyGenParsePublicKey(key string) (string, int, error) {
- tmpName, err := writeTmpKeyFile(key)
- if err != nil {
- return "", 0, fmt.Errorf("writeTmpKeyFile: %w", err)
- }
- defer func() {
- if err := util.Remove(tmpName); err != nil {
- log.Warn("Unable to remove temporary key file: %s: Error: %v", tmpName, err)
- }
- }()
-
- keygenPath := setting.SSH.KeygenPath
- if len(keygenPath) == 0 {
- keygenPath = "ssh-keygen"
- }
-
- stdout, stderr, err := process.GetManager().Exec("SSHKeyGenParsePublicKey", keygenPath, "-lf", tmpName)
- if err != nil {
- return "", 0, fmt.Errorf("fail to parse public key: %s - %s", err, stderr)
- }
- if strings.Contains(stdout, "is not a public key file") {
- return "", 0, ErrKeyUnableVerify{stdout}
- }
-
- fields := strings.Split(stdout, " ")
- if len(fields) < 4 {
- return "", 0, fmt.Errorf("invalid public key line: %s", stdout)
- }
-
- keyType := strings.Trim(fields[len(fields)-1], "()\r\n")
- length, err := strconv.ParseInt(fields[0], 10, 32)
- if err != nil {
- return "", 0, err
- }
- return strings.ToLower(keyType), int(length), nil
-}
diff --git a/models/asymkey/ssh_key_test.go b/models/asymkey/ssh_key_test.go
index b33d16030d..21e4ddf62e 100644
--- a/models/asymkey/ssh_key_test.go
+++ b/models/asymkey/ssh_key_test.go
@@ -18,7 +18,6 @@ import (
"github.com/42wim/sshsig"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
func Test_SSHParsePublicKey(t *testing.T) {
@@ -45,27 +44,6 @@ func Test_SSHParsePublicKey(t *testing.T) {
assert.Equal(t, tc.keyType, keyTypeN)
assert.Equal(t, tc.length, lengthN)
})
- if tc.skipSSHKeygen {
- return
- }
- t.Run("SSHKeygen", func(t *testing.T) {
- keyTypeK, lengthK, err := SSHKeyGenParsePublicKey(tc.content)
- if err != nil {
- // Some servers do not support ecdsa format.
- if !strings.Contains(err.Error(), "line 1 too long:") {
- require.NoError(t, err)
- }
- }
- assert.Equal(t, tc.keyType, keyTypeK)
- assert.Equal(t, tc.length, lengthK)
- })
- t.Run("SSHParseKeyNative", func(t *testing.T) {
- keyTypeK, lengthK, err := SSHNativeParsePublicKey(tc.content)
- require.NoError(t, err)
-
- assert.Equal(t, tc.keyType, keyTypeK)
- assert.Equal(t, tc.length, lengthK)
- })
})
}
}
@@ -186,14 +164,6 @@ func Test_calcFingerprint(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, tc.fp, fpN)
})
- if tc.skipSSHKeygen {
- return
- }
- t.Run("SSHKeygen", func(t *testing.T) {
- fpK, err := calcFingerprintSSHKeygen(tc.content)
- assert.NoError(t, err)
- assert.Equal(t, tc.fp, fpK)
- })
})
}
}
diff --git a/models/git/branch.go b/models/git/branch.go
index 9ac6c45578..beeb7c0689 100644
--- a/models/git/branch.go
+++ b/models/git/branch.go
@@ -235,6 +235,11 @@ func GetDeletedBranchByID(ctx context.Context, repoID, branchID int64) (*Branch,
return &branch, nil
}
+func DeleteRepoBranches(ctx context.Context, repoID int64) error {
+ _, err := db.GetEngine(ctx).Where("repo_id=?", repoID).Delete(new(Branch))
+ return err
+}
+
func DeleteBranches(ctx context.Context, repoID, doerID int64, branchIDs []int64) error {
return db.WithTx(ctx, func(ctx context.Context) error {
branches := make([]*Branch, 0, len(branchIDs))
diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go
index a05a84ddfc..1b179389e6 100644
--- a/models/issues/issue_update.go
+++ b/models/issues/issue_update.go
@@ -11,7 +11,6 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo"
@@ -611,7 +610,7 @@ func ResolveIssueMentionsByVisibility(ctx context.Context, issue *Issue, doer *u
unittype = unit.TypePullRequests
}
for _, team := range teams {
- if team.AccessMode >= perm.AccessModeAdmin {
+ if team.HasAdminAccess() {
checked = append(checked, team.ID)
resolved[issue.Repo.Owner.LowerName+"/"+team.LowerName] = true
continue
diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go
index fe6de9c517..7da426fef0 100644
--- a/models/migrations/base/tests.go
+++ b/models/migrations/base/tests.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/tempdir"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/testlogger"
@@ -114,15 +115,16 @@ func MainTest(m *testing.M) {
setting.CustomConf = giteaConf
}
- tmpDataPath, err := os.MkdirTemp("", "data")
+ tmpDataPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("data")
if err != nil {
testlogger.Fatalf("Unable to create temporary data path %v\n", err)
}
+ defer cleanup()
setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
setting.AppDataPath = tmpDataPath
- unittest.InitSettings()
+ unittest.InitSettingsForTesting()
if err = git.InitFull(context.Background()); err != nil {
testlogger.Fatalf("Unable to InitFull: %v\n", err)
}
@@ -134,8 +136,5 @@ func MainTest(m *testing.M) {
if err := removeAllWithRetry(setting.RepoRootPath); err != nil {
fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
}
- if err := removeAllWithRetry(tmpDataPath); err != nil {
- fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
- }
os.Exit(exitStatus)
}
diff --git a/models/organization/org.go b/models/organization/org.go
index 3e55a36758..dc889ea17f 100644
--- a/models/organization/org.go
+++ b/models/organization/org.go
@@ -178,12 +178,6 @@ func (org *Organization) HomeLink() string {
return org.AsUser().HomeLink()
}
-// CanCreateRepo returns if user login can create a repository
-// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised
-func (org *Organization) CanCreateRepo() bool {
- return org.AsUser().CanCreateRepo()
-}
-
// FindOrgMembersOpts represensts find org members conditions
type FindOrgMembersOpts struct {
db.ListOptions
diff --git a/models/organization/org_user.go b/models/organization/org_user.go
index 08d936d922..4d7527c15f 100644
--- a/models/organization/org_user.go
+++ b/models/organization/org_user.go
@@ -78,7 +78,7 @@ func IsOrganizationAdmin(ctx context.Context, orgID, uid int64) (bool, error) {
return false, err
}
for _, t := range teams {
- if t.AccessMode >= perm.AccessModeAdmin {
+ if t.HasAdminAccess() {
return true, nil
}
}
diff --git a/models/organization/team.go b/models/organization/team.go
index 96666da39a..7f3a9b3829 100644
--- a/models/organization/team.go
+++ b/models/organization/team.go
@@ -113,7 +113,7 @@ func (t *Team) LoadUnits(ctx context.Context) (err error) {
// GetUnitNames returns the team units names
func (t *Team) GetUnitNames() (res []string) {
- if t.AccessMode >= perm.AccessModeAdmin {
+ if t.HasAdminAccess() {
return unit.AllUnitKeyNames()
}
@@ -126,7 +126,7 @@ func (t *Team) GetUnitNames() (res []string) {
// GetUnitsMap returns the team units permissions
func (t *Team) GetUnitsMap() map[string]string {
m := make(map[string]string)
- if t.AccessMode >= perm.AccessModeAdmin {
+ if t.HasAdminAccess() {
for _, u := range unit.Units {
m[u.NameKey] = t.AccessMode.ToString()
}
@@ -153,6 +153,10 @@ func (t *Team) IsMember(ctx context.Context, userID int64) bool {
return isMember
}
+func (t *Team) HasAdminAccess() bool {
+ return t.AccessMode >= perm.AccessModeAdmin
+}
+
// LoadMembers returns paginated members in team of organization.
func (t *Team) LoadMembers(ctx context.Context) (err error) {
t.Members, err = GetTeamMembers(ctx, &SearchMembersOptions{
@@ -238,22 +242,6 @@ func GetTeamByID(ctx context.Context, teamID int64) (*Team, error) {
return t, nil
}
-// GetTeamNamesByID returns team's lower name from a list of team ids.
-func GetTeamNamesByID(ctx context.Context, teamIDs []int64) ([]string, error) {
- if len(teamIDs) == 0 {
- return []string{}, nil
- }
-
- var teamNames []string
- err := db.GetEngine(ctx).Table("team").
- Select("lower_name").
- In("id", teamIDs).
- Asc("name").
- Find(&teamNames)
-
- return teamNames, err
-}
-
// IncrTeamRepoNum increases the number of repos for the given team by 1
func IncrTeamRepoNum(ctx context.Context, teamID int64) error {
_, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team))
diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go
index f42c96bbe2..9d0c9f0077 100644
--- a/models/perm/access/repo_permission.go
+++ b/models/perm/access/repo_permission.go
@@ -331,7 +331,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
// if user in an owner team
for _, team := range teams {
- if team.AccessMode >= perm_model.AccessModeAdmin {
+ if team.HasAdminAccess() {
perm.AccessMode = perm_model.AccessModeOwner
perm.unitsMode = nil
return perm, nil
@@ -399,7 +399,7 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use
}
for _, team := range teams {
- if team.AccessMode >= perm_model.AccessModeAdmin {
+ if team.HasAdminAccess() {
return true, nil
}
}
diff --git a/models/repo/release.go b/models/repo/release.go
index 1c2e4a48e3..663d310bc0 100644
--- a/models/repo/release.go
+++ b/models/repo/release.go
@@ -558,3 +558,8 @@ func FindTagsByCommitIDs(ctx context.Context, repoID int64, commitIDs ...string)
}
return res, nil
}
+
+func DeleteRepoReleases(ctx context.Context, repoID int64) error {
+ _, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Delete(new(Release))
+ return err
+}
diff --git a/models/repo/update.go b/models/repo/update.go
index fce357a1ac..15c8c48d5b 100644
--- a/models/repo/update.go
+++ b/models/repo/update.go
@@ -111,31 +111,31 @@ func (err ErrRepoFilesAlreadyExist) Unwrap() error {
return util.ErrAlreadyExist
}
-// CheckCreateRepository check if could created a repository
-func CheckCreateRepository(ctx context.Context, doer, u *user_model.User, name string, overwriteOrAdopt bool) error {
- if !doer.CanCreateRepo() {
- return ErrReachLimitOfRepo{u.MaxRepoCreation}
+// CheckCreateRepository check if doer could create a repository in new owner
+func CheckCreateRepository(ctx context.Context, doer, owner *user_model.User, name string, overwriteOrAdopt bool) error {
+ if !doer.CanCreateRepoIn(owner) {
+ return ErrReachLimitOfRepo{owner.MaxRepoCreation}
}
if err := IsUsableRepoName(name); err != nil {
return err
}
- has, err := IsRepositoryModelOrDirExist(ctx, u, name)
+ has, err := IsRepositoryModelOrDirExist(ctx, owner, name)
if err != nil {
return fmt.Errorf("IsRepositoryExist: %w", err)
} else if has {
- return ErrRepoAlreadyExist{u.Name, name}
+ return ErrRepoAlreadyExist{owner.Name, name}
}
- repoPath := RepoPath(u.Name, name)
+ repoPath := RepoPath(owner.Name, name)
isExist, err := util.IsExist(repoPath)
if err != nil {
log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
return err
}
if !overwriteOrAdopt && isExist {
- return ErrRepoFilesAlreadyExist{u.Name, name}
+ return ErrRepoFilesAlreadyExist{owner.Name, name}
}
return nil
}
diff --git a/models/repo/upload.go b/models/repo/upload.go
index cae11df96a..fb57fb6c51 100644
--- a/models/repo/upload.go
+++ b/models/repo/upload.go
@@ -51,14 +51,10 @@ func init() {
db.RegisterModel(new(Upload))
}
-// UploadLocalPath returns where uploads is stored in local file system based on given UUID.
-func UploadLocalPath(uuid string) string {
- return filepath.Join(setting.Repository.Upload.TempPath, uuid[0:1], uuid[1:2], uuid)
-}
-
-// LocalPath returns where uploads are temporarily stored in local file system.
+// LocalPath returns where uploads are temporarily stored in local file system based on given UUID.
func (upload *Upload) LocalPath() string {
- return UploadLocalPath(upload.UUID)
+ uuid := upload.UUID
+ return setting.AppDataTempDir("repo-uploads").JoinPath(uuid[0:1], uuid[1:2], uuid)
}
// NewUpload creates a new upload object.
diff --git a/models/unit/unit.go b/models/unit/unit.go
index c816fc6c68..4ca676802f 100644
--- a/models/unit/unit.go
+++ b/models/unit/unit.go
@@ -20,17 +20,21 @@ type Type int
// Enumerate all the unit types
const (
- TypeInvalid Type = iota // 0 invalid
- TypeCode // 1 code
- TypeIssues // 2 issues
- TypePullRequests // 3 PRs
- TypeReleases // 4 Releases
- TypeWiki // 5 Wiki
- TypeExternalWiki // 6 ExternalWiki
- TypeExternalTracker // 7 ExternalTracker
- TypeProjects // 8 Projects
- TypePackages // 9 Packages
- TypeActions // 10 Actions
+ TypeInvalid Type = iota // 0 invalid
+
+ TypeCode // 1 code
+ TypeIssues // 2 issues
+ TypePullRequests // 3 PRs
+ TypeReleases // 4 Releases
+ TypeWiki // 5 Wiki
+ TypeExternalWiki // 6 ExternalWiki
+ TypeExternalTracker // 7 ExternalTracker
+ TypeProjects // 8 Projects
+ TypePackages // 9 Packages
+ TypeActions // 10 Actions
+
+ // FIXME: TEAM-UNIT-PERMISSION: the team unit "admin" permission's design is not right, when a new unit is added in the future,
+ // admin team won't inherit the correct admin permission for the new unit, need to have a complete fix before adding any new unit.
)
// Value returns integer value for unit type (used by template)
@@ -380,20 +384,3 @@ func AllUnitKeyNames() []string {
}
return res
}
-
-// MinUnitAccessMode returns the minial permission of the permission map
-func MinUnitAccessMode(unitsMap map[Type]perm.AccessMode) perm.AccessMode {
- res := perm.AccessModeNone
- for t, mode := range unitsMap {
- // Don't allow `TypeExternal{Tracker,Wiki}` to influence this as they can only be set to READ perms.
- if t == TypeExternalTracker || t == TypeExternalWiki {
- continue
- }
-
- // get the minial permission great than AccessModeNone except all are AccessModeNone
- if mode > perm.AccessModeNone && (res == perm.AccessModeNone || mode < res) {
- res = mode
- }
- }
- return res
-}
diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go
index 7a9ca9698d..cb60cf5f85 100644
--- a/models/unittest/testdb.go
+++ b/models/unittest/testdb.go
@@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/setting/config"
"code.gitea.io/gitea/modules/storage"
+ "code.gitea.io/gitea/modules/tempdir"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/util"
@@ -35,8 +36,8 @@ func fatalTestError(fmtStr string, args ...any) {
os.Exit(1)
}
-// InitSettings initializes config provider and load common settings for tests
-func InitSettings() {
+// InitSettingsForTesting initializes config provider and load common settings for tests
+func InitSettingsForTesting() {
setting.IsInTesting = true
log.OsExiter = func(code int) {
if code != 0 {
@@ -75,7 +76,7 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) {
testOpts := util.OptionalArg(testOptsArg, &TestOptions{})
giteaRoot = test.SetupGiteaRoot()
setting.CustomPath = filepath.Join(giteaRoot, "custom")
- InitSettings()
+ InitSettingsForTesting()
fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles}
if err := CreateTestEngine(fixturesOpts); err != nil {
@@ -92,15 +93,19 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) {
setting.SSH.Domain = "try.gitea.io"
setting.Database.Type = "sqlite3"
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
- repoRootPath, err := os.MkdirTemp(os.TempDir(), "repos")
+ repoRootPath, cleanup1, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("repos")
if err != nil {
fatalTestError("TempDir: %v\n", err)
}
+ defer cleanup1()
+
setting.RepoRootPath = repoRootPath
- appDataPath, err := os.MkdirTemp(os.TempDir(), "appdata")
+ appDataPath, cleanup2, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("appdata")
if err != nil {
fatalTestError("TempDir: %v\n", err)
}
+ defer cleanup2()
+
setting.AppDataPath = appDataPath
setting.AppWorkPath = giteaRoot
setting.StaticRootPath = giteaRoot
@@ -153,13 +158,6 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) {
fatalTestError("tear down failed: %v\n", err)
}
}
-
- if err = util.RemoveAll(repoRootPath); err != nil {
- fatalTestError("util.RemoveAll: %v\n", err)
- }
- if err = util.RemoveAll(appDataPath); err != nil {
- fatalTestError("util.RemoveAll: %v\n", err)
- }
os.Exit(exitStatus)
}
diff --git a/models/user/user.go b/models/user/user.go
index 3b268a6f41..5989be74f0 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -247,19 +247,20 @@ func (u *User) MaxCreationLimit() int {
return u.MaxRepoCreation
}
-// CanCreateRepo returns if user login can create a repository
-// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised
-func (u *User) CanCreateRepo() bool {
+// CanCreateRepoIn checks whether the doer(u) can create a repository in the owner
+// NOTE: functions calling this assume a failure due to repository count limit; it ONLY checks the repo number LIMIT, if new checks are added, those functions should be revised
+func (u *User) CanCreateRepoIn(owner *User) bool {
if u.IsAdmin {
return true
}
- if u.MaxRepoCreation <= -1 {
- if setting.Repository.MaxCreationLimit <= -1 {
+ const noLimit = -1
+ if owner.MaxRepoCreation == noLimit {
+ if setting.Repository.MaxCreationLimit == noLimit {
return true
}
- return u.NumRepos < setting.Repository.MaxCreationLimit
+ return owner.NumRepos < setting.Repository.MaxCreationLimit
}
- return u.NumRepos < u.MaxRepoCreation
+ return owner.NumRepos < owner.MaxRepoCreation
}
// CanCreateOrganization returns true if user can create organisation.
@@ -272,13 +273,12 @@ func (u *User) CanEditGitHook() bool {
return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook)
}
-// CanForkRepo returns if user login can fork a repository
-// It checks especially that the user can create repos, and potentially more
-func (u *User) CanForkRepo() bool {
+// CanForkRepoIn ONLY checks repository count limit
+func (u *User) CanForkRepoIn(owner *User) bool {
if setting.Repository.AllowForkWithoutMaximumLimit {
return true
}
- return u.CanCreateRepo()
+ return u.CanCreateRepoIn(owner)
}
// CanImportLocal returns true if user can migrate repository by local path.
diff --git a/models/user/user_test.go b/models/user/user_test.go
index 2d5b6a405c..90e8bf13a8 100644
--- a/models/user/user_test.go
+++ b/models/user/user_test.go
@@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
@@ -616,3 +617,37 @@ func TestGetInactiveUsers(t *testing.T) {
assert.NoError(t, err)
assert.Empty(t, users)
}
+
+func TestCanCreateRepo(t *testing.T) {
+ defer test.MockVariableValue(&setting.Repository.MaxCreationLimit)()
+ const noLimit = -1
+ doerNormal := &user_model.User{}
+ doerAdmin := &user_model.User{IsAdmin: true}
+ t.Run("NoGlobalLimit", func(t *testing.T) {
+ setting.Repository.MaxCreationLimit = noLimit
+
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
+ assert.False(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
+
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
+ })
+
+ t.Run("GlobalLimit50", func(t *testing.T) {
+ setting.Repository.MaxCreationLimit = 50
+
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
+ assert.False(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: noLimit})) // limited by global limit
+ assert.False(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
+ assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: 100}))
+
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: noLimit}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
+ assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: 100}))
+ })
+}
diff --git a/modules/git/blame.go b/modules/git/blame.go
index d1d732c716..6eb583a6b9 100644
--- a/modules/git/blame.go
+++ b/modules/git/blame.go
@@ -11,7 +11,7 @@ import (
"os"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/setting"
)
// BlamePart represents block of blame - continuous lines with one sha
@@ -29,12 +29,13 @@ type BlameReader struct {
bufferedReader *bufio.Reader
done chan error
lastSha *string
- ignoreRevsFile *string
+ ignoreRevsFile string
objectFormat ObjectFormat
+ cleanupFuncs []func()
}
func (r *BlameReader) UsesIgnoreRevs() bool {
- return r.ignoreRevsFile != nil
+ return r.ignoreRevsFile != ""
}
// NextPart returns next part of blame (sequential code lines with the same commit)
@@ -122,36 +123,37 @@ func (r *BlameReader) Close() error {
r.bufferedReader = nil
_ = r.reader.Close()
_ = r.output.Close()
- if r.ignoreRevsFile != nil {
- _ = util.Remove(*r.ignoreRevsFile)
+ for _, cleanup := range r.cleanupFuncs {
+ if cleanup != nil {
+ cleanup()
+ }
}
return err
}
// CreateBlameReader creates reader for given repository, commit and file
func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (*BlameReader, error) {
- var ignoreRevsFile *string
- if DefaultFeatures().CheckVersionAtLeast("2.23") && !bypassBlameIgnore {
- ignoreRevsFile = tryCreateBlameIgnoreRevsFile(commit)
- }
-
- cmd := NewCommandNoGlobals("blame", "--porcelain")
- if ignoreRevsFile != nil {
- // Possible improvement: use --ignore-revs-file /dev/stdin on unix
- // There is no equivalent on Windows. May be implemented if Gitea uses an external git backend.
- cmd.AddOptionValues("--ignore-revs-file", *ignoreRevsFile)
- }
- cmd.AddDynamicArguments(commit.ID.String()).AddDashesAndList(file)
reader, stdout, err := os.Pipe()
if err != nil {
- if ignoreRevsFile != nil {
- _ = util.Remove(*ignoreRevsFile)
- }
return nil, err
}
- done := make(chan error, 1)
+ cmd := NewCommandNoGlobals("blame", "--porcelain")
+ var ignoreRevsFileName string
+ var ignoreRevsFileCleanup func() // TODO: maybe it should check the returned err in a defer func to make sure the cleanup could always be executed correctly
+ if DefaultFeatures().CheckVersionAtLeast("2.23") && !bypassBlameIgnore {
+ ignoreRevsFileName, ignoreRevsFileCleanup = tryCreateBlameIgnoreRevsFile(commit)
+ if ignoreRevsFileName != "" {
+ // Possible improvement: use --ignore-revs-file /dev/stdin on unix
+ // There is no equivalent on Windows. May be implemented if Gitea uses an external git backend.
+ cmd.AddOptionValues("--ignore-revs-file", ignoreRevsFileName)
+ }
+ }
+
+ cmd.AddDynamicArguments(commit.ID.String()).AddDashesAndList(file)
+
+ done := make(chan error, 1)
go func() {
stderr := bytes.Buffer{}
// TODO: it doesn't work for directories (the directories shouldn't be "blamed"), and the "err" should be returned by "Read" but not by "Close"
@@ -169,40 +171,44 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath
}()
bufferedReader := bufio.NewReader(reader)
-
return &BlameReader{
output: stdout,
reader: reader,
bufferedReader: bufferedReader,
done: done,
- ignoreRevsFile: ignoreRevsFile,
+ ignoreRevsFile: ignoreRevsFileName,
objectFormat: objectFormat,
+ cleanupFuncs: []func(){ignoreRevsFileCleanup},
}, nil
}
-func tryCreateBlameIgnoreRevsFile(commit *Commit) *string {
+func tryCreateBlameIgnoreRevsFile(commit *Commit) (string, func()) {
entry, err := commit.GetTreeEntryByPath(".git-blame-ignore-revs")
if err != nil {
- return nil
+ log.Error("Unable to get .git-blame-ignore-revs file: GetTreeEntryByPath: %v", err)
+ return "", nil
}
r, err := entry.Blob().DataAsync()
if err != nil {
- return nil
+ log.Error("Unable to get .git-blame-ignore-revs file data: DataAsync: %v", err)
+ return "", nil
}
defer r.Close()
- f, err := os.CreateTemp("", "gitea_git-blame-ignore-revs")
+ f, cleanup, err := setting.AppDataTempDir("git-repo-content").CreateTempFileRandom("git-blame-ignore-revs")
if err != nil {
- return nil
+ log.Error("Unable to get .git-blame-ignore-revs file data: CreateTempFileRandom: %v", err)
+ return "", nil
}
-
+ filename := f.Name()
_, err = io.Copy(f, r)
_ = f.Close()
if err != nil {
- _ = util.Remove(f.Name())
- return nil
+ cleanup()
+ log.Error("Unable to get .git-blame-ignore-revs file data: Copy: %v", err)
+ return "", nil
}
- return util.ToPointer(f.Name())
+ return filename, cleanup
}
diff --git a/modules/git/blame_sha256_test.go b/modules/git/blame_sha256_test.go
index 99c23429e2..c0a97bed3b 100644
--- a/modules/git/blame_sha256_test.go
+++ b/modules/git/blame_sha256_test.go
@@ -7,10 +7,13 @@ import (
"context"
"testing"
+ "code.gitea.io/gitea/modules/setting"
+
"github.com/stretchr/testify/assert"
)
func TestReadingBlameOutputSha256(t *testing.T) {
+ setting.AppDataPath = t.TempDir()
ctx, cancel := context.WithCancel(t.Context())
defer cancel()
diff --git a/modules/git/blame_test.go b/modules/git/blame_test.go
index 36b5fb9349..809d6fbcf7 100644
--- a/modules/git/blame_test.go
+++ b/modules/git/blame_test.go
@@ -7,10 +7,13 @@ import (
"context"
"testing"
+ "code.gitea.io/gitea/modules/setting"
+
"github.com/stretchr/testify/assert"
)
func TestReadingBlameOutput(t *testing.T) {
+ setting.AppDataPath = t.TempDir()
ctx, cancel := context.WithCancel(t.Context())
defer cancel()
diff --git a/modules/git/git_test.go b/modules/git/git_test.go
index 5472842b76..58ba01cabc 100644
--- a/modules/git/git_test.go
+++ b/modules/git/git_test.go
@@ -10,18 +10,19 @@ import (
"testing"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/tempdir"
"github.com/hashicorp/go-version"
"github.com/stretchr/testify/assert"
)
func testRun(m *testing.M) error {
- gitHomePath, err := os.MkdirTemp(os.TempDir(), "git-home")
+ gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home")
if err != nil {
return fmt.Errorf("unable to create temp dir: %w", err)
}
- defer util.RemoveAll(gitHomePath)
+ defer cleanup()
+
setting.Git.HomePath = gitHomePath
if err = InitFull(context.Background()); err != nil {
diff --git a/modules/git/repo.go b/modules/git/repo.go
index 6459adf851..45937a8d5f 100644
--- a/modules/git/repo.go
+++ b/modules/git/repo.go
@@ -18,6 +18,7 @@ import (
"time"
"code.gitea.io/gitea/modules/proxy"
+ "code.gitea.io/gitea/modules/setting"
)
// GPGSettings represents the default GPG settings for this repository
@@ -266,11 +267,11 @@ func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch
// CreateBundle create bundle content to the target path
func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io.Writer) error {
- tmp, err := os.MkdirTemp(os.TempDir(), "gitea-bundle")
+ tmp, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-bundle")
if err != nil {
return err
}
- defer os.RemoveAll(tmp)
+ defer cleanup()
env := append(os.Environ(), "GIT_OBJECT_DIRECTORY="+filepath.Join(repo.Path, "objects"))
_, _, err = NewCommand("init", "--bare").RunStdString(ctx, &RunOpts{Dir: tmp, Env: env})
diff --git a/modules/git/repo_index.go b/modules/git/repo_index.go
index 1c7fcc063e..443a3a20d1 100644
--- a/modules/git/repo_index.go
+++ b/modules/git/repo_index.go
@@ -10,8 +10,7 @@ import (
"path/filepath"
"strings"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/setting"
)
// ReadTreeToIndex reads a treeish to the index
@@ -59,26 +58,18 @@ func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (tmpIndexFilena
}
}()
- removeDirFn := func(dir string) func() { // it can't use the return value "tmpDir" directly because it is empty when error occurs
- return func() {
- if err := util.RemoveAll(dir); err != nil {
- log.Error("failed to remove tmp index dir: %v", err)
- }
- }
- }
-
- tmpDir, err = os.MkdirTemp("", "index")
+ tmpDir, cancel, err = setting.AppDataTempDir("git-repo-content").MkdirTempRandom("index")
if err != nil {
return "", "", nil, err
}
tmpIndexFilename = filepath.Join(tmpDir, ".tmp-index")
- cancel = removeDirFn(tmpDir)
+
err = repo.ReadTreeToIndex(treeish, tmpIndexFilename)
if err != nil {
return "", "", cancel, err
}
- return tmpIndexFilename, tmpDir, cancel, err
+ return tmpIndexFilename, tmpDir, cancel, nil
}
// EmptyIndex empties the index
diff --git a/modules/git/repo_language_stats_test.go b/modules/git/repo_language_stats_test.go
index 81f130bacb..12ce958c6e 100644
--- a/modules/git/repo_language_stats_test.go
+++ b/modules/git/repo_language_stats_test.go
@@ -9,11 +9,14 @@ import (
"path/filepath"
"testing"
+ "code.gitea.io/gitea/modules/setting"
+
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRepository_GetLanguageStats(t *testing.T) {
+ setting.AppDataPath = t.TempDir()
repoPath := filepath.Join(testReposDir, "language_stats_repo")
gitRepo, err := openRepositoryWithDefaultContext(repoPath)
require.NoError(t, err)
diff --git a/modules/markup/external/external.go b/modules/markup/external/external.go
index f708457853..39861ade12 100644
--- a/modules/markup/external/external.go
+++ b/modules/markup/external/external.go
@@ -12,11 +12,9 @@ import (
"runtime"
"strings"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
)
// RegisterRenderers registers all supported third part renderers according settings
@@ -88,16 +86,11 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
if p.IsInputFile {
// write to temp file
- f, err := os.CreateTemp("", "gitea_input")
+ f, cleanup, err := setting.AppDataTempDir("git-repo-content").CreateTempFileRandom("gitea_input")
if err != nil {
return fmt.Errorf("%s create temp file when rendering %s failed: %w", p.Name(), p.Command, err)
}
- tmpPath := f.Name()
- defer func() {
- if err := util.Remove(tmpPath); err != nil {
- log.Warn("Unable to remove temporary file: %s: Error: %v", tmpPath, err)
- }
- }()
+ defer cleanup()
_, err = io.Copy(f, input)
if err != nil {
diff --git a/modules/packages/hashed_buffer.go b/modules/packages/hashed_buffer.go
index 4ab45edcec..0cd657cd44 100644
--- a/modules/packages/hashed_buffer.go
+++ b/modules/packages/hashed_buffer.go
@@ -6,6 +6,7 @@ package packages
import (
"io"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util/filebuffer"
)
@@ -34,11 +35,11 @@ func NewHashedBuffer() (*HashedBuffer, error) {
// NewHashedBufferWithSize creates a hashed buffer with a specific memory size
func NewHashedBufferWithSize(maxMemorySize int) (*HashedBuffer, error) {
- b, err := filebuffer.New(maxMemorySize)
+ tempDir, err := setting.AppDataTempDir("package-hashed-buffer").MkdirAllSub("")
if err != nil {
return nil, err
}
-
+ b := filebuffer.New(maxMemorySize, tempDir)
hash := NewMultiHasher()
combinedWriter := io.MultiWriter(b, hash)
diff --git a/modules/packages/hashed_buffer_test.go b/modules/packages/hashed_buffer_test.go
index 564e782f18..5104c1fb25 100644
--- a/modules/packages/hashed_buffer_test.go
+++ b/modules/packages/hashed_buffer_test.go
@@ -9,10 +9,13 @@ import (
"strings"
"testing"
+ "code.gitea.io/gitea/modules/setting"
+
"github.com/stretchr/testify/assert"
)
func TestHashedBuffer(t *testing.T) {
+ setting.AppDataPath = t.TempDir()
cases := []struct {
MaxMemorySize int
Data string
diff --git a/modules/packages/nuget/symbol_extractor_test.go b/modules/packages/nuget/symbol_extractor_test.go
index fa1b80ee82..711ad6d096 100644
--- a/modules/packages/nuget/symbol_extractor_test.go
+++ b/modules/packages/nuget/symbol_extractor_test.go
@@ -9,6 +9,8 @@ import (
"encoding/base64"
"testing"
+ "code.gitea.io/gitea/modules/setting"
+
"github.com/stretchr/testify/assert"
)
@@ -17,6 +19,7 @@ fgAA3AEAAAQAAAAjU3RyaW5ncwAAAADgAQAABAAAACNVUwDkAQAAMAAAACNHVUlEAAAAFAIAACgB
AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`
func TestExtractPortablePdb(t *testing.T) {
+ setting.AppDataPath = t.TempDir()
createArchive := func(name string, content []byte) []byte {
var buf bytes.Buffer
archive := zip.NewWriter(&buf)
diff --git a/modules/repository/init.go b/modules/repository/init.go
index ace21254ba..e6331966ba 100644
--- a/modules/repository/init.go
+++ b/modules/repository/init.go
@@ -11,11 +11,7 @@ import (
"strings"
issues_model "code.gitea.io/gitea/models/issues"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/label"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -121,29 +117,6 @@ func LoadRepoConfig() error {
return nil
}
-func CheckInitRepository(ctx context.Context, repo *repo_model.Repository) (err error) {
- // Somehow the directory could exist.
- isExist, err := gitrepo.IsRepositoryExist(ctx, repo)
- if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
- return err
- }
- if isExist {
- return repo_model.ErrRepoFilesAlreadyExist{
- Uname: repo.OwnerName,
- Name: repo.Name,
- }
- }
-
- // Init git bare new repository.
- if err = git.InitRepository(ctx, repo.RepoPath(), true, repo.ObjectFormatName); err != nil {
- return fmt.Errorf("git.InitRepository: %w", err)
- } else if err = gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
- return fmt.Errorf("createDelegateHooks: %w", err)
- }
- return nil
-}
-
// InitializeLabels adds a label set to a repository using a template
func InitializeLabels(ctx context.Context, id int64, labelTemplate string, isOrg bool) error {
list, err := LoadTemplateLabelsByDisplayName(labelTemplate)
diff --git a/modules/repository/temp.go b/modules/repository/temp.go
index 76b9bda4ad..d7253d9e02 100644
--- a/modules/repository/temp.go
+++ b/modules/repository/temp.go
@@ -4,41 +4,19 @@
package repository
import (
+ "context"
"fmt"
- "os"
- "path/filepath"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
)
-// LocalCopyPath returns the local repository temporary copy path.
-func LocalCopyPath() string {
- if filepath.IsAbs(setting.Repository.Local.LocalCopyPath) {
- return setting.Repository.Local.LocalCopyPath
- }
- return filepath.Join(setting.AppDataPath, setting.Repository.Local.LocalCopyPath)
-}
-
// CreateTemporaryPath creates a temporary path
-func CreateTemporaryPath(prefix string) (string, error) {
- if err := os.MkdirAll(LocalCopyPath(), os.ModePerm); err != nil {
- log.Error("Unable to create localcopypath directory: %s (%v)", LocalCopyPath(), err)
- return "", fmt.Errorf("Failed to create localcopypath directory %s: %w", LocalCopyPath(), err)
- }
- basePath, err := os.MkdirTemp(LocalCopyPath(), prefix+".git")
+func CreateTemporaryPath(prefix string) (string, context.CancelFunc, error) {
+ basePath, cleanup, err := setting.AppDataTempDir("local-repo").MkdirTempRandom(prefix + ".git")
if err != nil {
log.Error("Unable to create temporary directory: %s-*.git (%v)", prefix, err)
- return "", fmt.Errorf("Failed to create dir %s-*.git: %w", prefix, err)
+ return "", nil, fmt.Errorf("failed to create dir %s-*.git: %w", prefix, err)
}
- return basePath, nil
-}
-
-// RemoveTemporaryPath removes the temporary path
-func RemoveTemporaryPath(basePath string) error {
- if _, err := os.Stat(basePath); !os.IsNotExist(err) {
- return util.RemoveAll(basePath)
- }
- return nil
+ return basePath, cleanup, nil
}
diff --git a/modules/setting/markup.go b/modules/setting/markup.go
index 3bd368f831..365af05fcf 100644
--- a/modules/setting/markup.go
+++ b/modules/setting/markup.go
@@ -127,7 +127,7 @@ func loadMarkupFrom(rootCfg ConfigProvider) {
}
}
- MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(5000)
+ MermaidMaxSourceCharacters = rootCfg.Section("markup").Key("MERMAID_MAX_SOURCE_CHARACTERS").MustInt(50000)
ExternalMarkupRenderers = make([]*MarkupRenderer, 0, 10)
ExternalSanitizerRules = make([]MarkupSanitizerRule, 0, 10)
diff --git a/modules/setting/packages.go b/modules/setting/packages.go
index 3f618cfd64..845f6d4e12 100644
--- a/modules/setting/packages.go
+++ b/modules/setting/packages.go
@@ -6,8 +6,6 @@ package setting
import (
"fmt"
"math"
- "os"
- "path/filepath"
"github.com/dustin/go-humanize"
)
@@ -67,14 +65,10 @@ func loadPackagesFrom(rootCfg ConfigProvider) (err error) {
return err
}
- Packages.ChunkedUploadPath = filepath.ToSlash(sec.Key("CHUNKED_UPLOAD_PATH").MustString("tmp/package-upload"))
- if !filepath.IsAbs(Packages.ChunkedUploadPath) {
- Packages.ChunkedUploadPath = filepath.ToSlash(filepath.Join(AppDataPath, Packages.ChunkedUploadPath))
- }
-
if HasInstallLock(rootCfg) {
- if err := os.MkdirAll(Packages.ChunkedUploadPath, os.ModePerm); err != nil {
- return fmt.Errorf("unable to create chunked upload directory: %s (%v)", Packages.ChunkedUploadPath, err)
+ Packages.ChunkedUploadPath, err = AppDataTempDir("package-upload").MkdirAllSub("")
+ if err != nil {
+ return fmt.Errorf("unable to create chunked upload directory: %w", err)
}
}
diff --git a/modules/setting/path.go b/modules/setting/path.go
index 0fdc305aa1..f51457a620 100644
--- a/modules/setting/path.go
+++ b/modules/setting/path.go
@@ -11,6 +11,7 @@ import (
"strings"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/tempdir"
)
var (
@@ -196,3 +197,18 @@ func InitWorkPathAndCfgProvider(getEnvFn func(name string) string, args ArgWorkP
CustomPath = tmpCustomPath.Value
CustomConf = tmpCustomConf.Value
}
+
+// AppDataTempDir returns a managed temporary directory for the application data.
+// Using empty sub will get the managed base temp directory, and it's safe to delete it.
+// Gitea only creates subdirectories under it, but not the APP_TEMP_PATH directory itself.
+// * When APP_TEMP_PATH="/tmp": the managed temp directory is "/tmp/gitea-tmp"
+// * When APP_TEMP_PATH is not set: the managed temp directory is "/{APP_DATA_PATH}/tmp"
+func AppDataTempDir(sub string) *tempdir.TempDir {
+ if appTempPathInternal != "" {
+ return tempdir.New(appTempPathInternal, "gitea-tmp/"+sub)
+ }
+ if AppDataPath == "" {
+ panic("setting.AppDataPath is not set")
+ }
+ return tempdir.New(AppDataPath, "tmp/"+sub)
+}
diff --git a/modules/setting/repository.go b/modules/setting/repository.go
index 43bfb3256d..f99c854e4f 100644
--- a/modules/setting/repository.go
+++ b/modules/setting/repository.go
@@ -62,17 +62,11 @@ var (
// Repository upload settings
Upload struct {
Enabled bool
- TempPath string
AllowedTypes string
FileMaxSize int64
MaxFiles int
} `ini:"-"`
- // Repository local settings
- Local struct {
- LocalCopyPath string
- } `ini:"-"`
-
// Pull request settings
PullRequest struct {
WorkInProgressPrefixes []string
@@ -181,25 +175,16 @@ var (
// Repository upload settings
Upload: struct {
Enabled bool
- TempPath string
AllowedTypes string
FileMaxSize int64
MaxFiles int
}{
Enabled: true,
- TempPath: "data/tmp/uploads",
AllowedTypes: "",
FileMaxSize: 50,
MaxFiles: 5,
},
- // Repository local settings
- Local: struct {
- LocalCopyPath string
- }{
- LocalCopyPath: "tmp/local-repo",
- },
-
// Pull request settings
PullRequest: struct {
WorkInProgressPrefixes []string
@@ -308,8 +293,6 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
log.Fatal("Failed to map Repository.Editor settings: %v", err)
} else if err = rootCfg.Section("repository.upload").MapTo(&Repository.Upload); err != nil {
log.Fatal("Failed to map Repository.Upload settings: %v", err)
- } else if err = rootCfg.Section("repository.local").MapTo(&Repository.Local); err != nil {
- log.Fatal("Failed to map Repository.Local settings: %v", err)
} else if err = rootCfg.Section("repository.pull-request").MapTo(&Repository.PullRequest); err != nil {
log.Fatal("Failed to map Repository.PullRequest settings: %v", err)
}
@@ -361,10 +344,6 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
}
}
- if !filepath.IsAbs(Repository.Upload.TempPath) {
- Repository.Upload.TempPath = filepath.Join(AppWorkPath, Repository.Upload.TempPath)
- }
-
if err := loadRepoArchiveFrom(rootCfg); err != nil {
log.Fatal("loadRepoArchiveFrom: %v", err)
}
diff --git a/modules/setting/server.go b/modules/setting/server.go
index e15b790906..ca635c8abe 100644
--- a/modules/setting/server.go
+++ b/modules/setting/server.go
@@ -7,6 +7,7 @@ import (
"encoding/base64"
"net"
"net/url"
+ "os"
"path/filepath"
"strconv"
"strings"
@@ -59,6 +60,8 @@ var (
// AssetVersion holds a opaque value that is used for cache-busting assets
AssetVersion string
+ appTempPathInternal string // the temporary path for the app, it is only an internal variable, do not use it, always use AppDataTempDir
+
Protocol Scheme
UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"`
ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"`
@@ -330,6 +333,19 @@ func loadServerFrom(rootCfg ConfigProvider) {
if !filepath.IsAbs(AppDataPath) {
AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath))
}
+ if IsInTesting && HasInstallLock(rootCfg) {
+ // FIXME: in testing, the "app data" directory is not correctly initialized before loading settings
+ if _, err := os.Stat(AppDataPath); err != nil {
+ _ = os.MkdirAll(AppDataPath, os.ModePerm)
+ }
+ }
+
+ appTempPathInternal = sec.Key("APP_TEMP_PATH").String()
+ if appTempPathInternal != "" {
+ if _, err := os.Stat(appTempPathInternal); err != nil {
+ log.Fatal("APP_TEMP_PATH %q is not accessible: %v", appTempPathInternal, err)
+ }
+ }
EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
diff --git a/modules/setting/ssh.go b/modules/setting/ssh.go
index 46eb49bfd4..da8cdf58d2 100644
--- a/modules/setting/ssh.go
+++ b/modules/setting/ssh.go
@@ -4,7 +4,6 @@
package setting
import (
- "os"
"path/filepath"
"strings"
"text/template"
@@ -31,8 +30,6 @@ var SSH = struct {
ServerKeyExchanges []string `ini:"SSH_SERVER_KEY_EXCHANGES"`
ServerMACs []string `ini:"SSH_SERVER_MACS"`
ServerHostKeys []string `ini:"SSH_SERVER_HOST_KEYS"`
- KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
- KeygenPath string `ini:"SSH_KEYGEN_PATH"`
AuthorizedKeysBackup bool `ini:"SSH_AUTHORIZED_KEYS_BACKUP"`
AuthorizedPrincipalsBackup bool `ini:"SSH_AUTHORIZED_PRINCIPALS_BACKUP"`
AuthorizedKeysCommandTemplate string `ini:"SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE"`
@@ -57,7 +54,6 @@ var SSH = struct {
ServerCiphers: []string{"chacha20-poly1305@openssh.com", "aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "aes256-gcm@openssh.com"},
ServerKeyExchanges: []string{"curve25519-sha256", "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1"},
ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"},
- KeygenPath: "",
MinimumKeySizeCheck: true,
MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 3071},
ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"},
@@ -123,7 +119,6 @@ func loadSSHFrom(rootCfg ConfigProvider) {
if len(serverMACs) > 0 {
SSH.ServerMACs = serverMACs
}
- SSH.KeyTestPath = os.TempDir()
if err = sec.MapTo(&SSH); err != nil {
log.Fatal("Failed to map SSH settings: %v", err)
}
@@ -133,7 +128,6 @@ func loadSSHFrom(rootCfg ConfigProvider) {
}
}
- SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").String()
SSH.Port = sec.Key("SSH_PORT").MustInt(22)
SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port)
SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false)
diff --git a/modules/ssh/init.go b/modules/ssh/init.go
index 21d4f89936..fdc11632e2 100644
--- a/modules/ssh/init.go
+++ b/modules/ssh/init.go
@@ -32,11 +32,6 @@ func Init() error {
builtinUnused()
- // FIXME: why 0o644 for a directory .....
- if err := os.MkdirAll(setting.SSH.KeyTestPath, 0o644); err != nil {
- return fmt.Errorf("failed to create directory %q for ssh key test: %w", setting.SSH.KeyTestPath, err)
- }
-
if len(setting.SSH.TrustedUserCAKeys) > 0 && setting.SSH.AuthorizedPrincipalsEnabled {
caKeysFileName := setting.SSH.TrustedUserCAKeysFile
caKeysFileDir := filepath.Dir(caKeysFileName)
diff --git a/modules/tempdir/tempdir.go b/modules/tempdir/tempdir.go
new file mode 100644
index 0000000000..22c2e4ea16
--- /dev/null
+++ b/modules/tempdir/tempdir.go
@@ -0,0 +1,112 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package tempdir
+
+import (
+ "os"
+ "path/filepath"
+ "time"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/util"
+)
+
+type TempDir struct {
+ // base is the base directory for temporary files, it must exist before accessing and won't be created automatically.
+ // for example: base="/system-tmpdir", sub="gitea-tmp"
+ base, sub string
+}
+
+func (td *TempDir) JoinPath(elems ...string) string {
+ return filepath.Join(append([]string{td.base, td.sub}, elems...)...)
+}
+
+// MkdirAllSub works like os.MkdirAll, but the base directory must exist
+func (td *TempDir) MkdirAllSub(dir string) (string, error) {
+ if _, err := os.Stat(td.base); err != nil {
+ return "", err
+ }
+ full := filepath.Join(td.base, td.sub, dir)
+ if err := os.MkdirAll(full, os.ModePerm); err != nil {
+ return "", err
+ }
+ return full, nil
+}
+
+func (td *TempDir) prepareDirWithPattern(elems ...string) (dir, pattern string, err error) {
+ if _, err = os.Stat(td.base); err != nil {
+ return "", "", err
+ }
+ dir, pattern = filepath.Split(filepath.Join(append([]string{td.base, td.sub}, elems...)...))
+ if err = os.MkdirAll(dir, os.ModePerm); err != nil {
+ return "", "", err
+ }
+ return dir, pattern, nil
+}
+
+// MkdirTempRandom works like os.MkdirTemp, the last path field is the "pattern"
+func (td *TempDir) MkdirTempRandom(elems ...string) (string, func(), error) {
+ dir, pattern, err := td.prepareDirWithPattern(elems...)
+ if err != nil {
+ return "", nil, err
+ }
+ dir, err = os.MkdirTemp(dir, pattern)
+ if err != nil {
+ return "", nil, err
+ }
+ return dir, func() {
+ if err := util.RemoveAll(dir); err != nil {
+ log.Error("Failed to remove temp directory %s: %v", dir, err)
+ }
+ }, nil
+}
+
+// CreateTempFileRandom works like os.CreateTemp, the last path field is the "pattern"
+func (td *TempDir) CreateTempFileRandom(elems ...string) (*os.File, func(), error) {
+ dir, pattern, err := td.prepareDirWithPattern(elems...)
+ if err != nil {
+ return nil, nil, err
+ }
+ f, err := os.CreateTemp(dir, pattern)
+ if err != nil {
+ return nil, nil, err
+ }
+ filename := f.Name()
+ return f, func() {
+ _ = f.Close()
+ if err := util.Remove(filename); err != nil {
+ log.Error("Unable to remove temporary file: %s: Error: %v", filename, err)
+ }
+ }, err
+}
+
+func (td *TempDir) RemoveOutdated(d time.Duration) {
+ var remove func(path string)
+ remove = func(path string) {
+ entries, _ := os.ReadDir(path)
+ for _, entry := range entries {
+ full := filepath.Join(path, entry.Name())
+ if entry.IsDir() {
+ remove(full)
+ _ = os.Remove(full)
+ continue
+ }
+ info, err := entry.Info()
+ if err == nil && time.Since(info.ModTime()) > d {
+ _ = os.Remove(full)
+ }
+ }
+ }
+ remove(td.JoinPath(""))
+}
+
+// New create a new TempDir instance, "base" must be an existing directory,
+// "sub" could be a multi-level directory and will be created if not exist
+func New(base, sub string) *TempDir {
+ return &TempDir{base: base, sub: sub}
+}
+
+func OsTempDir(sub string) *TempDir {
+ return New(os.TempDir(), sub)
+}
diff --git a/modules/tempdir/tempdir_test.go b/modules/tempdir/tempdir_test.go
new file mode 100644
index 0000000000..d6afcb7bed
--- /dev/null
+++ b/modules/tempdir/tempdir_test.go
@@ -0,0 +1,75 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package tempdir
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTempDir(t *testing.T) {
+ base := t.TempDir()
+
+ t.Run("Create", func(t *testing.T) {
+ td := New(base, "sub1/sub2") // make sure the sub dir supports "/" in the path
+ assert.Equal(t, filepath.Join(base, "sub1", "sub2"), td.JoinPath())
+ assert.Equal(t, filepath.Join(base, "sub1", "sub2/test"), td.JoinPath("test"))
+
+ t.Run("MkdirTempRandom", func(t *testing.T) {
+ s, cleanup, err := td.MkdirTempRandom("foo")
+ assert.NoError(t, err)
+ assert.True(t, strings.HasPrefix(s, filepath.Join(base, "sub1/sub2", "foo")))
+
+ _, err = os.Stat(s)
+ assert.NoError(t, err)
+ cleanup()
+ _, err = os.Stat(s)
+ assert.ErrorIs(t, err, os.ErrNotExist)
+ })
+
+ t.Run("CreateTempFileRandom", func(t *testing.T) {
+ f, cleanup, err := td.CreateTempFileRandom("foo", "bar")
+ filename := f.Name()
+ assert.NoError(t, err)
+ assert.True(t, strings.HasPrefix(filename, filepath.Join(base, "sub1/sub2", "foo", "bar")))
+ _, err = os.Stat(filename)
+ assert.NoError(t, err)
+ cleanup()
+ _, err = os.Stat(filename)
+ assert.ErrorIs(t, err, os.ErrNotExist)
+ })
+
+ t.Run("RemoveOutDated", func(t *testing.T) {
+ fa1, _, err := td.CreateTempFileRandom("dir-a", "f1")
+ assert.NoError(t, err)
+ fa2, _, err := td.CreateTempFileRandom("dir-a", "f2")
+ assert.NoError(t, err)
+ _ = os.Chtimes(fa2.Name(), time.Now().Add(-time.Hour), time.Now().Add(-time.Hour))
+ fb1, _, err := td.CreateTempFileRandom("dir-b", "f1")
+ assert.NoError(t, err)
+ _ = os.Chtimes(fb1.Name(), time.Now().Add(-time.Hour), time.Now().Add(-time.Hour))
+ _, _, _ = fa1.Close(), fa2.Close(), fb1.Close()
+
+ td.RemoveOutdated(time.Minute)
+
+ _, err = os.Stat(fa1.Name())
+ assert.NoError(t, err)
+ _, err = os.Stat(fa2.Name())
+ assert.ErrorIs(t, err, os.ErrNotExist)
+ _, err = os.Stat(fb1.Name())
+ assert.ErrorIs(t, err, os.ErrNotExist)
+ })
+ })
+
+ t.Run("BaseNotExist", func(t *testing.T) {
+ td := New(filepath.Join(base, "not-exist"), "sub")
+ _, _, err := td.MkdirTempRandom("foo")
+ assert.ErrorIs(t, err, os.ErrNotExist)
+ })
+}
diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go
index 26cd1eb348..460b9dc190 100644
--- a/modules/templates/util_render_test.go
+++ b/modules/templates/util_render_test.go
@@ -56,7 +56,7 @@ var testMetas = map[string]string{
}
func TestMain(m *testing.M) {
- unittest.InitSettings()
+ unittest.InitSettingsForTesting()
if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err)
}
diff --git a/modules/util/filebuffer/file_backed_buffer.go b/modules/util/filebuffer/file_backed_buffer.go
index 739543e297..0731ba30c8 100644
--- a/modules/util/filebuffer/file_backed_buffer.go
+++ b/modules/util/filebuffer/file_backed_buffer.go
@@ -7,16 +7,10 @@ import (
"bytes"
"errors"
"io"
- "math"
"os"
)
-var (
- // ErrInvalidMemorySize occurs if the memory size is not in a valid range
- ErrInvalidMemorySize = errors.New("Memory size must be greater 0 and lower math.MaxInt32")
- // ErrWriteAfterRead occurs if Write is called after a read operation
- ErrWriteAfterRead = errors.New("Write is unsupported after a read operation")
-)
+var ErrWriteAfterRead = errors.New("write is unsupported after a read operation") // occurs if Write is called after a read operation
type readAtSeeker interface {
io.ReadSeeker
@@ -30,34 +24,17 @@ type FileBackedBuffer struct {
maxMemorySize int64
size int64
buffer bytes.Buffer
+ tempDir string
file *os.File
reader readAtSeeker
}
// New creates a file backed buffer with a specific maximum memory size
-func New(maxMemorySize int) (*FileBackedBuffer, error) {
- if maxMemorySize < 0 || maxMemorySize > math.MaxInt32 {
- return nil, ErrInvalidMemorySize
- }
-
+func New(maxMemorySize int, tempDir string) *FileBackedBuffer {
return &FileBackedBuffer{
maxMemorySize: int64(maxMemorySize),
- }, nil
-}
-
-// CreateFromReader creates a file backed buffer and copies the provided reader data into it.
-func CreateFromReader(r io.Reader, maxMemorySize int) (*FileBackedBuffer, error) {
- b, err := New(maxMemorySize)
- if err != nil {
- return nil, err
+ tempDir: tempDir,
}
-
- _, err = io.Copy(b, r)
- if err != nil {
- return nil, err
- }
-
- return b, nil
}
// Write implements io.Writer
@@ -73,7 +50,7 @@ func (b *FileBackedBuffer) Write(p []byte) (int, error) {
n, err = b.file.Write(p)
} else {
if b.size+int64(len(p)) > b.maxMemorySize {
- b.file, err = os.CreateTemp("", "gitea-buffer-")
+ b.file, err = os.CreateTemp(b.tempDir, "gitea-buffer-")
if err != nil {
return 0, err
}
@@ -148,7 +125,7 @@ func (b *FileBackedBuffer) Seek(offset int64, whence int) (int64, error) {
func (b *FileBackedBuffer) Close() error {
if b.file != nil {
err := b.file.Close()
- os.Remove(b.file.Name())
+ _ = os.Remove(b.file.Name())
b.file = nil
return err
}
diff --git a/modules/util/filebuffer/file_backed_buffer_test.go b/modules/util/filebuffer/file_backed_buffer_test.go
index 16d5a1965f..3f13c6ac7b 100644
--- a/modules/util/filebuffer/file_backed_buffer_test.go
+++ b/modules/util/filebuffer/file_backed_buffer_test.go
@@ -21,7 +21,8 @@ func TestFileBackedBuffer(t *testing.T) {
}
for _, c := range cases {
- buf, err := CreateFromReader(strings.NewReader(c.Data), c.MaxMemorySize)
+ buf := New(c.MaxMemorySize, t.TempDir())
+ _, err := io.Copy(buf, strings.NewReader(c.Data))
assert.NoError(t, err)
assert.EqualValues(t, len(c.Data), buf.Size())
diff --git a/options/fileicon/material-icon-rules.json b/options/fileicon/material-icon-rules.json
index fd058c82dc..d097399252 100644
--- a/options/fileicon/material-icon-rules.json
+++ b/options/fileicon/material-icon-rules.json
@@ -1112,6 +1112,18 @@
".routers": "folder-routes",
"_routers": "folder-routes",
"__routers__": "folder-routes",
+ "navigation": "folder-routes",
+ ".navigation": "folder-routes",
+ "_navigation": "folder-routes",
+ "__navigation__": "folder-routes",
+ "navigations": "folder-routes",
+ ".navigations": "folder-routes",
+ "_navigations": "folder-routes",
+ "__navigations__": "folder-routes",
+ "routing": "folder-routes",
+ ".routing": "folder-routes",
+ "_routing": "folder-routes",
+ "__routing__": "folder-routes",
"ci": "folder-ci",
".ci": "folder-ci",
"_ci": "folder-ci",
@@ -2092,6 +2104,34 @@
".DS_Store": "folder-macos",
"_DS_Store": "folder-macos",
"__DS_Store__": "folder-macos",
+ "iPhone": "folder-macos",
+ ".iPhone": "folder-macos",
+ "_iPhone": "folder-macos",
+ "__iPhone__": "folder-macos",
+ "iPad": "folder-macos",
+ ".iPad": "folder-macos",
+ "_iPad": "folder-macos",
+ "__iPad__": "folder-macos",
+ "iPod": "folder-macos",
+ ".iPod": "folder-macos",
+ "_iPod": "folder-macos",
+ "__iPod__": "folder-macos",
+ "macbook": "folder-macos",
+ ".macbook": "folder-macos",
+ "_macbook": "folder-macos",
+ "__macbook__": "folder-macos",
+ "macbook-air": "folder-macos",
+ ".macbook-air": "folder-macos",
+ "_macbook-air": "folder-macos",
+ "__macbook-air__": "folder-macos",
+ "macosx": "folder-macos",
+ ".macosx": "folder-macos",
+ "_macosx": "folder-macos",
+ "__macosx__": "folder-macos",
+ "apple": "folder-macos",
+ ".apple": "folder-macos",
+ "_apple": "folder-macos",
+ "__apple__": "folder-macos",
"error": "folder-error",
".error": "folder-error",
"_error": "folder-error",
@@ -2428,6 +2468,34 @@
".firebase": "folder-firebase",
"_firebase": "folder-firebase",
"__firebase__": "folder-firebase",
+ "firestore": "folder-firestore",
+ ".firestore": "folder-firestore",
+ "_firestore": "folder-firestore",
+ "__firestore__": "folder-firestore",
+ "cloud-firestore": "folder-firestore",
+ ".cloud-firestore": "folder-firestore",
+ "_cloud-firestore": "folder-firestore",
+ "__cloud-firestore__": "folder-firestore",
+ "firebase-firestore": "folder-firestore",
+ ".firebase-firestore": "folder-firestore",
+ "_firebase-firestore": "folder-firestore",
+ "__firebase-firestore__": "folder-firestore",
+ "cloud-functions": "folder-cloud-functions",
+ ".cloud-functions": "folder-cloud-functions",
+ "_cloud-functions": "folder-cloud-functions",
+ "__cloud-functions__": "folder-cloud-functions",
+ "cloudfunctions": "folder-cloud-functions",
+ ".cloudfunctions": "folder-cloud-functions",
+ "_cloudfunctions": "folder-cloud-functions",
+ "__cloudfunctions__": "folder-cloud-functions",
+ "firebase-cloud-functions": "folder-cloud-functions",
+ ".firebase-cloud-functions": "folder-cloud-functions",
+ "_firebase-cloud-functions": "folder-cloud-functions",
+ "__firebase-cloud-functions__": "folder-cloud-functions",
+ "firebase-cloudfunctions": "folder-cloud-functions",
+ ".firebase-cloudfunctions": "folder-cloud-functions",
+ "_firebase-cloudfunctions": "folder-cloud-functions",
+ "__firebase-cloudfunctions__": "folder-cloud-functions",
"svelte": "folder-svelte",
".svelte": "folder-svelte",
"_svelte": "folder-svelte",
@@ -2975,6 +3043,14 @@
".zeabur": "folder-zeabur",
"_zeabur": "folder-zeabur",
"__zeabur__": "folder-zeabur",
+ "kusto": "folder-kusto",
+ ".kusto": "folder-kusto",
+ "_kusto": "folder-kusto",
+ "__kusto__": "folder-kusto",
+ "kql": "folder-kusto",
+ ".kql": "folder-kusto",
+ "_kql": "folder-kusto",
+ "__kql__": "folder-kusto",
"meta-inf": "folder-config",
".meta-inf": "folder-config",
"_meta-inf": "folder-config",
@@ -2990,7 +3066,19 @@
"ds_store": "folder-macos",
".ds_store": "folder-macos",
"_ds_store": "folder-macos",
- "__ds_store__": "folder-macos"
+ "__ds_store__": "folder-macos",
+ "iphone": "folder-macos",
+ ".iphone": "folder-macos",
+ "_iphone": "folder-macos",
+ "__iphone__": "folder-macos",
+ "ipad": "folder-macos",
+ ".ipad": "folder-macos",
+ "_ipad": "folder-macos",
+ "__ipad__": "folder-macos",
+ "ipod": "folder-macos",
+ ".ipod": "folder-macos",
+ "_ipod": "folder-macos",
+ "__ipod__": "folder-macos"
},
"folderNamesExpanded": {
"rust": "folder-rust-open",
@@ -4105,6 +4193,18 @@
".routers": "folder-routes-open",
"_routers": "folder-routes-open",
"__routers__": "folder-routes-open",
+ "navigation": "folder-routes-open",
+ ".navigation": "folder-routes-open",
+ "_navigation": "folder-routes-open",
+ "__navigation__": "folder-routes-open",
+ "navigations": "folder-routes-open",
+ ".navigations": "folder-routes-open",
+ "_navigations": "folder-routes-open",
+ "__navigations__": "folder-routes-open",
+ "routing": "folder-routes-open",
+ ".routing": "folder-routes-open",
+ "_routing": "folder-routes-open",
+ "__routing__": "folder-routes-open",
"ci": "folder-ci-open",
".ci": "folder-ci-open",
"_ci": "folder-ci-open",
@@ -5085,6 +5185,34 @@
".DS_Store": "folder-macos-open",
"_DS_Store": "folder-macos-open",
"__DS_Store__": "folder-macos-open",
+ "iPhone": "folder-macos-open",
+ ".iPhone": "folder-macos-open",
+ "_iPhone": "folder-macos-open",
+ "__iPhone__": "folder-macos-open",
+ "iPad": "folder-macos-open",
+ ".iPad": "folder-macos-open",
+ "_iPad": "folder-macos-open",
+ "__iPad__": "folder-macos-open",
+ "iPod": "folder-macos-open",
+ ".iPod": "folder-macos-open",
+ "_iPod": "folder-macos-open",
+ "__iPod__": "folder-macos-open",
+ "macbook": "folder-macos-open",
+ ".macbook": "folder-macos-open",
+ "_macbook": "folder-macos-open",
+ "__macbook__": "folder-macos-open",
+ "macbook-air": "folder-macos-open",
+ ".macbook-air": "folder-macos-open",
+ "_macbook-air": "folder-macos-open",
+ "__macbook-air__": "folder-macos-open",
+ "macosx": "folder-macos-open",
+ ".macosx": "folder-macos-open",
+ "_macosx": "folder-macos-open",
+ "__macosx__": "folder-macos-open",
+ "apple": "folder-macos-open",
+ ".apple": "folder-macos-open",
+ "_apple": "folder-macos-open",
+ "__apple__": "folder-macos-open",
"error": "folder-error-open",
".error": "folder-error-open",
"_error": "folder-error-open",
@@ -5421,6 +5549,34 @@
".firebase": "folder-firebase-open",
"_firebase": "folder-firebase-open",
"__firebase__": "folder-firebase-open",
+ "firestore": "folder-firestore-open",
+ ".firestore": "folder-firestore-open",
+ "_firestore": "folder-firestore-open",
+ "__firestore__": "folder-firestore-open",
+ "cloud-firestore": "folder-firestore-open",
+ ".cloud-firestore": "folder-firestore-open",
+ "_cloud-firestore": "folder-firestore-open",
+ "__cloud-firestore__": "folder-firestore-open",
+ "firebase-firestore": "folder-firestore-open",
+ ".firebase-firestore": "folder-firestore-open",
+ "_firebase-firestore": "folder-firestore-open",
+ "__firebase-firestore__": "folder-firestore-open",
+ "cloud-functions": "folder-cloud-functions-open",
+ ".cloud-functions": "folder-cloud-functions-open",
+ "_cloud-functions": "folder-cloud-functions-open",
+ "__cloud-functions__": "folder-cloud-functions-open",
+ "cloudfunctions": "folder-cloud-functions-open",
+ ".cloudfunctions": "folder-cloud-functions-open",
+ "_cloudfunctions": "folder-cloud-functions-open",
+ "__cloudfunctions__": "folder-cloud-functions-open",
+ "firebase-cloud-functions": "folder-cloud-functions-open",
+ ".firebase-cloud-functions": "folder-cloud-functions-open",
+ "_firebase-cloud-functions": "folder-cloud-functions-open",
+ "__firebase-cloud-functions__": "folder-cloud-functions-open",
+ "firebase-cloudfunctions": "folder-cloud-functions-open",
+ ".firebase-cloudfunctions": "folder-cloud-functions-open",
+ "_firebase-cloudfunctions": "folder-cloud-functions-open",
+ "__firebase-cloudfunctions__": "folder-cloud-functions-open",
"svelte": "folder-svelte-open",
".svelte": "folder-svelte-open",
"_svelte": "folder-svelte-open",
@@ -5967,7 +6123,15 @@
"zeabur": "folder-zeabur-open",
".zeabur": "folder-zeabur-open",
"_zeabur": "folder-zeabur-open",
- "__zeabur__": "folder-zeabur-open"
+ "__zeabur__": "folder-zeabur-open",
+ "kusto": "folder-kusto-open",
+ ".kusto": "folder-kusto-open",
+ "_kusto": "folder-kusto-open",
+ "__kusto__": "folder-kusto-open",
+ "kql": "folder-kusto-open",
+ ".kql": "folder-kusto-open",
+ "_kql": "folder-kusto-open",
+ "__kql__": "folder-kusto-open"
},
"rootFolderNames": {},
"rootFolderNamesExpanded": {},
@@ -6032,8 +6196,6 @@
"ico": "image",
"tif": "image",
"tiff": "image",
- "psd": "image",
- "psb": "image",
"ami": "image",
"apx": "image",
"avif": "image",
@@ -6128,6 +6290,10 @@
"routing.tsx": "routing",
"routing.js": "routing",
"routing.jsx": "routing",
+ "route.ts": "routing",
+ "route.tsx": "routing",
+ "route.js": "routing",
+ "route.jsx": "routing",
"routes.ts": "routing",
"routes.tsx": "routing",
"routes.js": "routing",
@@ -6288,6 +6454,7 @@
"py": "python",
"pyc": "python-misc",
"whl": "python-misc",
+ "egg": "python-misc",
"url": "url",
"sh": "console",
"ksh": "console",
@@ -6780,6 +6947,11 @@
"gltf": "3d",
"glb": "3d",
"svg": "svg",
+ "ai": "adobe-illustrator",
+ "ait": "adobe-illustrator",
+ "psd": "adobe-photoshop",
+ "psb": "adobe-photoshop",
+ "psdt": "adobe-photoshop",
"svelte": "svelte",
"svelte.js": "svelte_js",
"svelte.ts": "svelte_ts",
@@ -7031,6 +7203,7 @@
"dfxp": "subtitles",
"vtt": "subtitles",
"sub": "subtitles",
+ "ass": "subtitles",
"beancount": "beancount",
"bean": "beancount",
"epub": "epub",
@@ -7272,6 +7445,10 @@
"router.jsx": "routing",
"router.ts": "routing",
"router.tsx": "routing",
+ "route.js": "routing",
+ "route.jsx": "routing",
+ "route.ts": "routing",
+ "route.tsx": "routing",
"routes.js": "routing",
"routes.jsx": "routing",
"routes.ts": "routing",
@@ -7310,6 +7487,11 @@
".pylintrc": "python-misc",
"pyproject.toml": "python-misc",
"py.typed": "python-misc",
+ ".coveragerc": "python-misc",
+ ".coverage": "python-misc",
+ ".scrapy": "python-misc",
+ "celerybeat-schedule": "python-misc",
+ "celerybeat.pid": "python-misc",
"ruff.toml": "ruff",
".ruff.toml": "ruff",
"uv.toml": "uv",
@@ -9317,6 +9499,11 @@
"drone.yml": "drone_light",
".wakatime-project": "wakatime_light",
"hcl": "hcl_light",
+ "ai": "adobe-illustrator_light",
+ "ait": "adobe-illustrator_light",
+ "psd": "adobe-photoshop_light",
+ "psb": "adobe-photoshop_light",
+ "psdt": "adobe-photoshop_light",
"iuml": "uml_light",
"pu": "uml_light",
"puml": "uml_light",
diff --git a/options/fileicon/material-icon-svgs.json b/options/fileicon/material-icon-svgs.json
index dbda90665b..50bc2d2b8a 100644
--- a/options/fileicon/material-icon-svgs.json
+++ b/options/fileicon/material-icon-svgs.json
@@ -4,6 +4,10 @@
"abc": " ",
"actionscript": " ",
"ada": " ",
+ "adobe-illustrator": " ",
+ "adobe-illustrator_light": " ",
+ "adobe-photoshop": " ",
+ "adobe-photoshop_light": " ",
"adobe-swc": " ",
"adonis": " ",
"advpl-include.clone": " ",
@@ -163,8 +167,8 @@
"fastlane": " ",
"favicon": " ",
"figma": " ",
- "file": " ",
- "firebase": " ",
+ "file": " ",
+ "firebase": " ",
"flash": " ",
"flow": " ",
"folder-admin-open": " ",
@@ -223,6 +227,8 @@
"folder-client": " ",
"folder-cline-open": " ",
"folder-cline": " ",
+ "folder-cloud-functions-open": " ",
+ "folder-cloud-functions": " ",
"folder-cloudflare-open": " ",
"folder-cloudflare": " ",
"folder-cluster-open": " ",
@@ -273,8 +279,8 @@
"folder-delta": " ",
"folder-desktop-open": " ",
"folder-desktop": " ",
- "folder-development-open.clone": " ",
- "folder-development.clone": " ",
+ "folder-development-open.clone": " ",
+ "folder-development.clone": " ",
"folder-directive-open": " ",
"folder-directive": " ",
"folder-dist-open": " ",
@@ -309,8 +315,10 @@
"folder-fastlane": " ",
"folder-favicon-open": " ",
"folder-favicon": " ",
- "folder-firebase-open": " ",
- "folder-firebase": " ",
+ "folder-firebase-open": " ",
+ "folder-firebase": " ",
+ "folder-firestore-open": " ",
+ "folder-firestore": " ",
"folder-flow-open": " ",
"folder-flow": " ",
"folder-flutter-open": " ",
@@ -391,6 +399,8 @@
"folder-keys": " ",
"folder-kubernetes-open": " ",
"folder-kubernetes": " ",
+ "folder-kusto-open": " ",
+ "folder-kusto": " ",
"folder-layout-open": " ",
"folder-layout": " ",
"folder-lefthook-open": " ",
@@ -443,10 +453,10 @@
"folder-next": " ",
"folder-ngrx-actions-open.clone": " ",
"folder-ngrx-actions.clone": " ",
- "folder-ngrx-effects-open.clone": " ",
- "folder-ngrx-effects.clone": " ",
- "folder-ngrx-entities-open.clone": " ",
- "folder-ngrx-entities.clone": " ",
+ "folder-ngrx-effects-open.clone": " ",
+ "folder-ngrx-effects.clone": " ",
+ "folder-ngrx-entities-open.clone": " ",
+ "folder-ngrx-entities.clone": " ",
"folder-ngrx-reducer-open.clone": " ",
"folder-ngrx-reducer.clone": " ",
"folder-ngrx-selectors-open.clone": " ",
@@ -461,7 +471,7 @@
"folder-nuxt": " ",
"folder-obsidian-open": " ",
"folder-obsidian": " ",
- "folder-open": " ",
+ "folder-open": " ",
"folder-other-open": " ",
"folder-other": " ",
"folder-packages-open": " ",
@@ -500,8 +510,8 @@
"folder-queue": " ",
"folder-react-components-open": " ",
"folder-react-components": " ",
- "folder-redux-actions-open.clone": " ",
- "folder-redux-actions.clone": " ",
+ "folder-redux-actions-open.clone": " ",
+ "folder-redux-actions.clone": " ",
"folder-redux-reducer-open": " ",
"folder-redux-reducer": " ",
"folder-redux-selector-open.clone": " ",
@@ -518,8 +528,8 @@
"folder-review": " ",
"folder-robot-open": " ",
"folder-robot": " ",
- "folder-root-open": " ",
- "folder-root": " ",
+ "folder-root-open": " ",
+ "folder-root": " ",
"folder-routes-open": " ",
"folder-routes": " ",
"folder-rules-open": " ",
@@ -644,7 +654,7 @@
"folder-yarn": " ",
"folder-zeabur-open": " ",
"folder-zeabur": " ",
- "folder": " ",
+ "folder": " ",
"font": " ",
"forth": " ",
"fortran": " ",
@@ -738,7 +748,7 @@
"knip": " ",
"kotlin": " ",
"kubernetes": " ",
- "kusto": " ",
+ "kusto": " ",
"label": " ",
"laravel": " ",
"lefthook": " ",
diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index af366868b1..da7aef22d5 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -3238,8 +3238,6 @@ config.ssh_domain=Doména SSH serveru
config.ssh_port=Port
config.ssh_listen_port=Port pro naslouchání
config.ssh_root_path=Kořenová cesta
-config.ssh_key_test_path=Cesta testu klíčů
-config.ssh_keygen_path=Cesta ke generátoru klíčů ('ssh-keygen')
config.ssh_minimum_key_size_check=Kontrola minimální velikosti klíčů
config.ssh_minimum_key_sizes=Minimální velikost klíčů
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index 50ade526ff..57f1c01404 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -3235,8 +3235,6 @@ config.ssh_domain=SSH-Server-Domain
config.ssh_port=Port
config.ssh_listen_port=Listen-Port
config.ssh_root_path=Wurzelverzeichnis
-config.ssh_key_test_path=Schlüssel-Test-Pfad
-config.ssh_keygen_path=Keygen-Pfad („ssh-keygen“)
config.ssh_minimum_key_size_check=Prüfung der Mindestschlüssellänge
config.ssh_minimum_key_sizes=Mindestschlüssellängen
diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini
index 960e3b1581..c1e2f35abc 100644
--- a/options/locale/locale_el-GR.ini
+++ b/options/locale/locale_el-GR.ini
@@ -2927,8 +2927,6 @@ config.ssh_domain=Domain Διακομιστή SSH
config.ssh_port=Θύρα
config.ssh_listen_port=Θύρα Ακρόασης
config.ssh_root_path=Ριζική Διαδρομή
-config.ssh_key_test_path=Διαδρομή Δοκιμής Κλειδιού
-config.ssh_keygen_path=Διαδρομή Keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Έλεγχος Ελάχιστου Μεγέθους Κλειδιού
config.ssh_minimum_key_sizes=Ελάχιστα Μεγέθη Κλειδιών
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 96c99615f5..d7da975a21 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -3287,8 +3287,6 @@ config.ssh_domain = SSH Server Domain
config.ssh_port = Port
config.ssh_listen_port = Listen Port
config.ssh_root_path = Root Path
-config.ssh_key_test_path = Key Test Path
-config.ssh_keygen_path = Keygen ('ssh-keygen') Path
config.ssh_minimum_key_size_check = Minimum Key Size Check
config.ssh_minimum_key_sizes = Minimum Key Sizes
diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini
index 280c735c79..82d2e2a3b9 100644
--- a/options/locale/locale_es-ES.ini
+++ b/options/locale/locale_es-ES.ini
@@ -2907,8 +2907,6 @@ config.ssh_domain=Dominio del servidor SSH
config.ssh_port=Puerto
config.ssh_listen_port=Puerto de escucha
config.ssh_root_path=Ruta raíz
-config.ssh_key_test_path=Ruta de la clave de prueba
-config.ssh_keygen_path=Ruta del generador de claves ('ssh-keygen')
config.ssh_minimum_key_size_check=Tamaño mínimo de la clave de verificación
config.ssh_minimum_key_sizes=Tamaños de clave mínimos
diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini
index c7bef6e6dc..b550916fb0 100644
--- a/options/locale/locale_fa-IR.ini
+++ b/options/locale/locale_fa-IR.ini
@@ -2279,8 +2279,6 @@ config.ssh_domain=دامنه سرور SSH
config.ssh_port=درگاه (پورت)
config.ssh_listen_port=گوش دادن به پورت
config.ssh_root_path=مسیر ریشه
-config.ssh_key_test_path=مسیر کلید آزمایش
-config.ssh_keygen_path=مسیر فایل ssh-keygen
config.ssh_minimum_key_size_check=بررسی حداقل طول کلید
config.ssh_minimum_key_sizes=حداقل اندازهی کلید ها
diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini
index e853273375..69cee090fe 100644
--- a/options/locale/locale_fi-FI.ini
+++ b/options/locale/locale_fi-FI.ini
@@ -1538,8 +1538,6 @@ config.ssh_enabled=Käytössä
config.ssh_port=Portti
config.ssh_listen_port=Kuuntele porttia
config.ssh_root_path=Juuren polku
-config.ssh_key_test_path=Polku jossa avaimet testataan
-config.ssh_keygen_path=Keygen ('ssh-keygen') polku
config.ssh_minimum_key_size_check=Avaimen vähimmäiskoko tarkistus
config.ssh_minimum_key_sizes=Avaimen vähimmäiskoot
diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index db70eaf66a..6e0f0aab46 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -1707,11 +1707,11 @@ issues.start_tracking_history=`a commencé son travail %s.`
issues.tracker_auto_close=Le minuteur sera automatiquement arrêté quand le ticket sera fermé.
issues.tracking_already_started=`Vous avez déjà un minuteur en cours sur un autre ticket !`
issues.stop_tracking=Arrêter le minuteur
-issues.stop_tracking_history=a travaillé sur %[1]s %[2]s
+issues.stop_tracking_history=a travaillé %[1]s %[2]s
issues.cancel_tracking=Abandonner
issues.cancel_tracking_history=`a abandonné son minuteur %s.`
issues.del_time=Supprimer ce minuteur du journal
-issues.add_time_history=a pointé du temps de travail sur %[1]s , %[2]s
+issues.add_time_history=a pointé %[1]s de travail %[2]s.
issues.del_time_history=`a supprimé son temps de travail %s.`
issues.add_time_manually=Temps pointé manuellement
issues.add_time_hours=Heures
@@ -3274,8 +3274,6 @@ config.ssh_domain=Domaine du serveur SSH
config.ssh_port=Port
config.ssh_listen_port=Port d'écoute
config.ssh_root_path=Emplacement racine
-config.ssh_key_test_path=Chemin de test des clés
-config.ssh_keygen_path=Chemin vers le générateur de clés (« ssh-keygen »)
config.ssh_minimum_key_size_check=Vérification de la longueur de clé minimale
config.ssh_minimum_key_sizes=Tailles de clé minimales
diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini
index f2e5de942a..a65ed357b1 100644
--- a/options/locale/locale_ga-IE.ini
+++ b/options/locale/locale_ga-IE.ini
@@ -3282,8 +3282,6 @@ config.ssh_domain=Fearainn Freastalaí SSH
config.ssh_port=Calafort
config.ssh_listen_port=Éist Calafort
config.ssh_root_path=Cosán Fréimhe
-config.ssh_key_test_path=Cosán Tástáil Eochair
-config.ssh_keygen_path=Keygen ('ssh-keygen') Cosán
config.ssh_minimum_key_size_check=Seiceáil Íosta Méid Eochair
config.ssh_minimum_key_sizes=Méideanna Íosta Eochrach
diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini
index a57d6960dd..9be048c42f 100644
--- a/options/locale/locale_hu-HU.ini
+++ b/options/locale/locale_hu-HU.ini
@@ -1412,8 +1412,6 @@ config.ssh_start_builtin_server=Beépített szerver használata
config.ssh_port=Port
config.ssh_listen_port=Figyelő port
config.ssh_root_path=Gyökérkönyvtár
-config.ssh_key_test_path=Kulcs ellenőrzés útvonala
-config.ssh_keygen_path=Kulcsgeneráló ('ssh-keygen') elérési útja
config.ssh_minimum_key_size_check=Kulcsok minimum méretének ellenőrzése
config.ssh_minimum_key_sizes=Minimális kulcsok méretek
diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini
index c54bfbb924..9e2244c1ab 100644
--- a/options/locale/locale_id-ID.ini
+++ b/options/locale/locale_id-ID.ini
@@ -1219,8 +1219,6 @@ config.ssh_enabled=Aktif
config.ssh_port=Port
config.ssh_listen_port=Listen Port
config.ssh_root_path=Path Induk
-config.ssh_key_test_path=Path Key Test
-config.ssh_keygen_path=Path Keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Periksa ukuran kunci minimum
config.ssh_minimum_key_sizes=Ukuran kunci minimum
diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini
index 810f1040f5..73d1762c59 100644
--- a/options/locale/locale_it-IT.ini
+++ b/options/locale/locale_it-IT.ini
@@ -2464,8 +2464,6 @@ config.ssh_domain=Dominio Server Ssh
config.ssh_port=Porta
config.ssh_listen_port=Porta in ascolto
config.ssh_root_path=Percorso Root
-config.ssh_key_test_path=Percorso chiave di test
-config.ssh_keygen_path=Percorso Keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Verifica delle dimensioni minime della chiave
config.ssh_minimum_key_sizes=Dimensioni minime della chiave
diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index 4fe3a2ef60..e98c453744 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -3272,8 +3272,6 @@ config.ssh_domain=SSHサーバーのドメイン
config.ssh_port=ポート
config.ssh_listen_port=待受ポート
config.ssh_root_path=ルートパス
-config.ssh_key_test_path=キーテストパス
-config.ssh_keygen_path=キージェネレータ('ssh-keygen')パス
config.ssh_minimum_key_size_check=最小キー長のチェック
config.ssh_minimum_key_sizes=最小キー長
diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini
index ddb80dde1d..b6b9cf373e 100644
--- a/options/locale/locale_ko-KR.ini
+++ b/options/locale/locale_ko-KR.ini
@@ -1372,8 +1372,6 @@ config.ssh_start_builtin_server=빌트-인 서버 사용
config.ssh_port=포트
config.ssh_listen_port=수신 대기 포트
config.ssh_root_path=최상위 경로
-config.ssh_key_test_path=주 테스트 경로
-config.ssh_keygen_path=키 생성 ('ssh-keygen') 경로
config.ssh_minimum_key_size_check=최소 키 사이즈 검사
config.ssh_minimum_key_sizes=최소 키 사이즈
diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini
index 42c12def23..fa6736df1a 100644
--- a/options/locale/locale_lv-LV.ini
+++ b/options/locale/locale_lv-LV.ini
@@ -2930,8 +2930,6 @@ config.ssh_domain=SSH servera domēns
config.ssh_port=Ports
config.ssh_listen_port=Klausīšanās ports
config.ssh_root_path=Saknes ceļš
-config.ssh_key_test_path=Atslēgu pārbaudes ceļš
-config.ssh_keygen_path=Keygen ('ssh-keygen') ceļš
config.ssh_minimum_key_size_check=Minimālā atslēgas lieluma pārbaude
config.ssh_minimum_key_sizes=Minimālais atslēgas lielums
diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini
index 0ad14807d6..345990b532 100644
--- a/options/locale/locale_nl-NL.ini
+++ b/options/locale/locale_nl-NL.ini
@@ -2310,8 +2310,6 @@ config.ssh_start_builtin_server=Gebruik de ingebouwde server
config.ssh_port=Poort
config.ssh_listen_port=Luister op poort
config.ssh_root_path=Root-pad
-config.ssh_key_test_path=Pad voor key-tests
-config.ssh_keygen_path=Pad van keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Controleer minimale key-lengte
config.ssh_minimum_key_sizes=Minimale key-lengtes
diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini
index 9673db2a71..30c08bc6db 100644
--- a/options/locale/locale_pl-PL.ini
+++ b/options/locale/locale_pl-PL.ini
@@ -2198,8 +2198,6 @@ config.ssh_start_builtin_server=Wykorzystaj wbudowany serwer
config.ssh_port=Port
config.ssh_listen_port=Port nasłuchiwania
config.ssh_root_path=Ścieżka do katalogu głównego
-config.ssh_key_test_path=Ścieżka do klucza testowego
-config.ssh_keygen_path=Ścieżka do generatora ('ssh-keygen')
config.ssh_minimum_key_size_check=Sprawdzanie minimalnej długości klucza
config.ssh_minimum_key_sizes=Minimalne rozmiary kluczy
diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini
index 204fedc311..3dcbdf87aa 100644
--- a/options/locale/locale_pt-BR.ini
+++ b/options/locale/locale_pt-BR.ini
@@ -2878,8 +2878,6 @@ config.ssh_domain=Domínio do servidor SSH
config.ssh_port=Porta
config.ssh_listen_port=Porta de escuta
config.ssh_root_path=Caminho da raiz
-config.ssh_key_test_path=Caminho da chave de teste
-config.ssh_keygen_path=Caminho do keygen ('ssh-keygen')
config.ssh_minimum_key_size_check=Verificar tamanho mínimo da chave
config.ssh_minimum_key_sizes=Tamanhos mínimos da chave
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 4beeeb5b0d..fb91a76f02 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -3286,8 +3286,6 @@ config.ssh_domain=Domínio do servidor SSH
config.ssh_port=Porto
config.ssh_listen_port=Porto de escuta
config.ssh_root_path=Localização base
-config.ssh_key_test_path=Localização do teste das chaves
-config.ssh_keygen_path=Localização do gerador de chaves ('ssh-keygen')
config.ssh_minimum_key_size_check=Verificação de tamanho mínimo da chave
config.ssh_minimum_key_sizes=Tamanhos mínimos da chave
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index 68246ff751..ae3c9a4ed9 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -2870,8 +2870,6 @@ config.ssh_domain=Домен SSH сервера
config.ssh_port=Порт
config.ssh_listen_port=Прослушиваемый порт
config.ssh_root_path=Корневой путь
-config.ssh_key_test_path=Путь к тестовому ключу
-config.ssh_keygen_path=Путь к генератору ключей ('ssh-keygen')
config.ssh_minimum_key_size_check=Минимальный размер ключа проверки
config.ssh_minimum_key_sizes=Минимальные размеры ключа
diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini
index 7c95b254e8..fbeea4591c 100644
--- a/options/locale/locale_si-LK.ini
+++ b/options/locale/locale_si-LK.ini
@@ -2240,8 +2240,6 @@ config.ssh_domain=SSH සේවාදායකය වසම්
config.ssh_port=වරාය
config.ssh_listen_port=සවන් වරාය
config.ssh_root_path=මූල මාර්ගය
-config.ssh_key_test_path=ප්රධාන ටෙස්ට් මාර්ගය
-config.ssh_keygen_path=Keygen ('ssh-keygen') මාර්ගය
config.ssh_minimum_key_size_check=අවම කී ප්රමාණය පරීක්ෂා
config.ssh_minimum_key_sizes=අවම යතුරෙහි ප්රමාණ
diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini
index 92917c103a..5a3091adac 100644
--- a/options/locale/locale_sv-SE.ini
+++ b/options/locale/locale_sv-SE.ini
@@ -1794,8 +1794,6 @@ config.ssh_start_builtin_server=Använd inbyggd Server
config.ssh_port=Port
config.ssh_listen_port=Lyssningsport
config.ssh_root_path=Rotsökväg
-config.ssh_key_test_path=Testsökväg för nyckel
-config.ssh_keygen_path=Sökväg för nyckelgenerator ('ssh-keygen')
config.ssh_minimum_key_size_check=Kontroll av minsta tillåtna nyckelstorlek
config.ssh_minimum_key_sizes=Minsta tillåtna nyckelstorlek
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index faf442431b..bec38b2ed1 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -3106,8 +3106,6 @@ config.ssh_domain=SSH Sunucusu Alan Adı
config.ssh_port=Bağlantı Noktası
config.ssh_listen_port=Port'u Dinle
config.ssh_root_path=Kök Yol
-config.ssh_key_test_path=Anahtar Test Yolu
-config.ssh_keygen_path=Keygen ('ssh-keygen') Yolu
config.ssh_minimum_key_size_check=Minimum Anahtar Uzunluğu Kontrolü
config.ssh_minimum_key_sizes=Minimum Anahtar Uzunlukları
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index 995de50b61..2aae533e7e 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -2290,8 +2290,6 @@ config.ssh_domain=Домен SSH сервера
config.ssh_port=Порт
config.ssh_listen_port=Порт що прослуховується
config.ssh_root_path=Шлях до кореню
-config.ssh_key_test_path=Шлях до тестового ключа
-config.ssh_keygen_path=Шлях до генератора ключів ('ssh-keygen')
config.ssh_minimum_key_size_check=Мінімальний розмір ключа перевірки
config.ssh_minimum_key_sizes=Мінімальні розміри ключів
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 7f15c32304..85f5b08a42 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -3221,8 +3221,6 @@ config.ssh_domain=SSH 服务器域名
config.ssh_port=端口
config.ssh_listen_port=监听端口
config.ssh_root_path=根目录
-config.ssh_key_test_path=密钥测试路径
-config.ssh_keygen_path=密钥生成器('ssh-keygen')路径
config.ssh_minimum_key_size_check=密钥最小长度检查
config.ssh_minimum_key_sizes=密钥最小长度限制
diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini
index ae6c6c3552..73602fe36a 100644
--- a/options/locale/locale_zh-HK.ini
+++ b/options/locale/locale_zh-HK.ini
@@ -816,8 +816,6 @@ config.ssh_enabled=已啟用
config.ssh_port=埠
config.ssh_listen_port=監聽埠
config.ssh_root_path=根路徑
-config.ssh_key_test_path=金鑰測試路徑
-config.ssh_keygen_path=金鑰產生 (' ssh-keygen ') 路徑
config.ssh_minimum_key_size_check=金鑰最小大小檢查
config.ssh_minimum_key_sizes=金鑰最小大小
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index 374ff073f2..343fb401c8 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -3211,8 +3211,6 @@ config.ssh_domain=SSH 伺服器域名
config.ssh_port=連接埠
config.ssh_listen_port=監聽埠
config.ssh_root_path=根路徑
-config.ssh_key_test_path=金鑰測試路徑
-config.ssh_keygen_path=金鑰產生 (' ssh-keygen ') 路徑
config.ssh_minimum_key_size_check=金鑰最小大小檢查
config.ssh_minimum_key_sizes=金鑰最小大小
diff --git a/package-lock.json b/package-lock.json
index c347cf5fd8..e516dcd91a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,12 +16,12 @@
"@primer/octicons": "19.15.1",
"@silverwind/vue3-calendar-heatmap": "2.0.6",
"add-asset-webpack-plugin": "3.0.0",
- "ansi_up": "6.0.2",
+ "ansi_up": "6.0.5",
"asciinema-player": "3.9.0",
"chart.js": "4.4.8",
"chartjs-adapter-dayjs-4": "1.0.4",
"chartjs-plugin-zoom": "2.2.0",
- "clippie": "4.1.5",
+ "clippie": "4.1.6",
"cropperjs": "1.6.2",
"css-loader": "7.1.2",
"dayjs": "1.11.13",
@@ -35,7 +35,7 @@
"jquery": "3.7.1",
"katex": "0.16.21",
"license-checker-webpack-plugin": "0.2.1",
- "mermaid": "11.5.0",
+ "mermaid": "11.6.0",
"mini-css-extract-plugin": "2.9.2",
"minimatch": "10.0.1",
"monaco-editor": "0.52.2",
@@ -46,14 +46,14 @@
"postcss-loader": "8.1.1",
"postcss-nesting": "13.0.1",
"sortablejs": "1.15.6",
- "swagger-ui-dist": "5.20.1",
+ "swagger-ui-dist": "5.20.7",
"tailwindcss": "3.4.17",
"throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0",
"tippy.js": "6.3.7",
"toastify-js": "1.12.0",
"tributejs": "5.1.3",
- "typescript": "5.8.2",
+ "typescript": "5.8.3",
"uint8-to-base64": "0.2.0",
"vanilla-colorful": "0.7.2",
"vue": "3.5.13",
@@ -80,15 +80,15 @@
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/toastify-js": "1.12.3",
- "@typescript-eslint/eslint-plugin": "8.26.1",
- "@typescript-eslint/parser": "8.26.1",
- "@vitejs/plugin-vue": "5.2.1",
- "@vitest/eslint-plugin": "1.1.37",
+ "@typescript-eslint/eslint-plugin": "8.29.1",
+ "@typescript-eslint/parser": "8.29.1",
+ "@vitejs/plugin-vue": "5.2.3",
+ "@vitest/eslint-plugin": "1.1.39",
"eslint": "8.57.0",
- "eslint-import-resolver-typescript": "3.9.0",
+ "eslint-import-resolver-typescript": "4.3.2",
"eslint-plugin-array-func": "4.0.0",
"eslint-plugin-github": "5.0.2",
- "eslint-plugin-import-x": "4.7.2",
+ "eslint-plugin-import-x": "4.10.2",
"eslint-plugin-no-jquery": "3.1.1",
"eslint-plugin-no-use-extend-native": "0.5.0",
"eslint-plugin-playwright": "2.2.0",
@@ -97,23 +97,23 @@
"eslint-plugin-unicorn": "56.0.1",
"eslint-plugin-vue": "10.0.0",
"eslint-plugin-vue-scoped-css": "2.9.0",
- "eslint-plugin-wc": "2.2.1",
+ "eslint-plugin-wc": "3.0.0",
"happy-dom": "17.4.4",
"markdownlint-cli": "0.44.0",
- "material-icon-theme": "5.20.0",
+ "material-icon-theme": "5.21.1",
"nolyfill": "1.0.44",
"postcss-html": "1.8.0",
- "stylelint": "16.16.0",
- "stylelint-config-recommended": "15.0.0",
+ "stylelint": "16.18.0",
+ "stylelint-config-recommended": "16.0.0",
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
"stylelint-declaration-strict-value": "1.10.11",
- "stylelint-define-config": "16.15.0",
+ "stylelint-define-config": "16.17.0",
"stylelint-value-no-unknown-custom-properties": "6.0.1",
"svgo": "3.3.2",
- "type-fest": "4.37.0",
+ "type-fest": "4.39.1",
"updates": "16.4.2",
"vite-string-plugin": "1.4.4",
- "vitest": "3.0.8",
+ "vitest": "3.1.1",
"vue-tsc": "2.2.8"
},
"engines": {
@@ -521,9 +521,9 @@
}
},
"node_modules/@emnapi/core": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.3.1.tgz",
- "integrity": "sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.0.tgz",
+ "integrity": "sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -533,9 +533,9 @@
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
- "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.0.tgz",
+ "integrity": "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -1540,24 +1540,24 @@
}
},
"node_modules/@mermaid-js/parser": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.3.0.tgz",
- "integrity": "sha512-HsvL6zgE5sUPGgkIDlmAWR1HTNHz2Iy11BAWPTa4Jjabkpguy4Ze2gzfLrg6pdRuBvFwgUYyxiaNqZwrEEXepA==",
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz",
+ "integrity": "sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA==",
"license": "MIT",
"dependencies": {
- "langium": "3.0.0"
+ "langium": "3.3.1"
}
},
"node_modules/@napi-rs/wasm-runtime": {
- "version": "0.2.7",
- "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.7.tgz",
- "integrity": "sha512-5yximcFK5FNompXfJFoWanu5l8v1hNGqNHh9du1xETp9HWk/B/PzvchX55WYOPaIeNglG8++68AAiauBAtbnzw==",
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.8.tgz",
+ "integrity": "sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
- "@emnapi/core": "^1.3.1",
- "@emnapi/runtime": "^1.3.1",
+ "@emnapi/core": "^1.4.0",
+ "@emnapi/runtime": "^1.4.0",
"@tybys/wasm-util": "^0.9.0"
}
},
@@ -1596,16 +1596,6 @@
"node": ">= 8"
}
},
- "node_modules/@nolyfill/is-core-module": {
- "version": "1.0.39",
- "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
- "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.4.0"
- }
- },
"node_modules/@nolyfill/shared": {
"version": "1.0.44",
"resolved": "https://registry.npmjs.org/@nolyfill/shared/-/shared-1.0.44.tgz",
@@ -1613,163 +1603,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@oxc-resolver/binding-darwin-arm64": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-5.0.0.tgz",
- "integrity": "sha512-zwHAf+owoxSWTDD4dFuwW+FkpaDzbaL30H5Ltocb+RmLyg4WKuteusRLKh5Y8b/cyu7UzhxM0haIqQjyqA1iuA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@oxc-resolver/binding-darwin-x64": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-5.0.0.tgz",
- "integrity": "sha512-1lS3aBNVjVQKBvZdHm13+8tSjvu2Tl1Cv4FnUyMYxqx6+rsom2YaOylS5LhDUwfZu0zAgpLMwK6kGpF/UPncNg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@oxc-resolver/binding-freebsd-x64": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-5.0.0.tgz",
- "integrity": "sha512-q9sRd68wC1/AJ0eu6ClhxlklVfe8gH4wrUkSyEbIYTZ8zY5yjsLY3fpqqsaCvWJUx65nW+XtnAxCGCi5AXr1Mw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-5.0.0.tgz",
- "integrity": "sha512-catYavWsvqViYnCveQjhrK6yVYDEPFvIOgGLxnz5r2dcgrjpmquzREoyss0L2QG/J5HTTbwqwZ1kk+g56hE/1A==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@oxc-resolver/binding-linux-arm64-gnu": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-5.0.0.tgz",
- "integrity": "sha512-l/0pWoQM5kVmJLg4frQ1mKZOXgi0ex/hzvFt8E4WK2ifXr5JgKFUokxsb/oat7f5YzdJJh5r9p+qS/t3dA26Aw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@oxc-resolver/binding-linux-arm64-musl": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-5.0.0.tgz",
- "integrity": "sha512-bx0oz/oaAW4FGYqpIIxJCnmgb906YfMhTEWCJvYkxjpEI8VKLJEL3PQevYiqDq36SA0yRLJ/sQK2fqry8AFBfA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@oxc-resolver/binding-linux-x64-gnu": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-5.0.0.tgz",
- "integrity": "sha512-4PH++qbSIhlRsFYdN1P9neDov4OGhTGo5nbQ1D7AL6gWFLo3gdZTc00FM2y8JjeTcPWEXkViZuwpuc0w5i6qHg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@oxc-resolver/binding-linux-x64-musl": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-5.0.0.tgz",
- "integrity": "sha512-mLfQFpX3/5y9oWi0b+9FbWDkL2hM0Y29653beCHiHxAdGyVgb2DsJbK74WkMTwtSz9by8vyBh8jGPZcg1yLZbQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@oxc-resolver/binding-wasm32-wasi": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-5.0.0.tgz",
- "integrity": "sha512-uEhsAZSo65qsRi6+IfBTEUUFbjg7T2yruJeLYpFfEATpm3ory5Mgo5vx3L0c2/Cz1OUZXBgp3A8x6VMUB2jT2A==",
- "cpu": [
- "wasm32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@napi-rs/wasm-runtime": "^0.2.7"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@oxc-resolver/binding-win32-arm64-msvc": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-5.0.0.tgz",
- "integrity": "sha512-8DbSso9Jp1ns8AYuZFXdRfAcdJrzZwkFm/RjPuvAPTENsm685dosBF8G6gTHQlHvULnk6o3sa9ygZaTGC/UoEw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@oxc-resolver/binding-win32-x64-msvc": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-5.0.0.tgz",
- "integrity": "sha512-ylppfPEg63NuRXOPNsXFlgyl37JrtRn0QMO26X3K3Ytp5HtLrMreQMGVtgr30e1l2YmAWqhvmKlCryOqzGPD/g==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -3314,17 +3147,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.26.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz",
- "integrity": "sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA==",
+ "version": "8.29.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz",
+ "integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.26.1",
- "@typescript-eslint/type-utils": "8.26.1",
- "@typescript-eslint/utils": "8.26.1",
- "@typescript-eslint/visitor-keys": "8.26.1",
+ "@typescript-eslint/scope-manager": "8.29.1",
+ "@typescript-eslint/type-utils": "8.29.1",
+ "@typescript-eslint/utils": "8.29.1",
+ "@typescript-eslint/visitor-keys": "8.29.1",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@@ -3344,16 +3177,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.26.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.1.tgz",
- "integrity": "sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ==",
+ "version": "8.29.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz",
+ "integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.26.1",
- "@typescript-eslint/types": "8.26.1",
- "@typescript-eslint/typescript-estree": "8.26.1",
- "@typescript-eslint/visitor-keys": "8.26.1",
+ "@typescript-eslint/scope-manager": "8.29.1",
+ "@typescript-eslint/types": "8.29.1",
+ "@typescript-eslint/typescript-estree": "8.29.1",
+ "@typescript-eslint/visitor-keys": "8.29.1",
"debug": "^4.3.4"
},
"engines": {
@@ -3369,14 +3202,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.26.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz",
- "integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==",
+ "version": "8.29.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz",
+ "integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.26.1",
- "@typescript-eslint/visitor-keys": "8.26.1"
+ "@typescript-eslint/types": "8.29.1",
+ "@typescript-eslint/visitor-keys": "8.29.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3387,14 +3220,14 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.26.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz",
- "integrity": "sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg==",
+ "version": "8.29.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz",
+ "integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.26.1",
- "@typescript-eslint/utils": "8.26.1",
+ "@typescript-eslint/typescript-estree": "8.29.1",
+ "@typescript-eslint/utils": "8.29.1",
"debug": "^4.3.4",
"ts-api-utils": "^2.0.1"
},
@@ -3411,9 +3244,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.26.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz",
- "integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==",
+ "version": "8.29.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz",
+ "integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3425,14 +3258,14 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.26.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz",
- "integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==",
+ "version": "8.29.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz",
+ "integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.26.1",
- "@typescript-eslint/visitor-keys": "8.26.1",
+ "@typescript-eslint/types": "8.29.1",
+ "@typescript-eslint/visitor-keys": "8.29.1",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -3468,16 +3301,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.26.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz",
- "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==",
+ "version": "8.29.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz",
+ "integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "8.26.1",
- "@typescript-eslint/types": "8.26.1",
- "@typescript-eslint/typescript-estree": "8.26.1"
+ "@typescript-eslint/scope-manager": "8.29.1",
+ "@typescript-eslint/types": "8.29.1",
+ "@typescript-eslint/typescript-estree": "8.29.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3492,13 +3325,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.26.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz",
- "integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==",
+ "version": "8.29.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz",
+ "integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.26.1",
+ "@typescript-eslint/types": "8.29.1",
"eslint-visitor-keys": "^4.2.0"
},
"engines": {
@@ -3516,10 +3349,10 @@
"dev": true,
"license": "ISC"
},
- "node_modules/@unrs/rspack-resolver-binding-darwin-arm64": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-darwin-arm64/-/rspack-resolver-binding-darwin-arm64-1.1.0.tgz",
- "integrity": "sha512-otdWnJrycP8Ow0rbiKKjhrW7PPeHPoIglYjBruqh75fEwQGV2EmA9oZMgD4YA6g+/hGzD0mXI26YnWL0G0SkTA==",
+ "node_modules/@unrs/resolver-binding-darwin-arm64": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.4.1.tgz",
+ "integrity": "sha512-8Tv+Bsd0BjGwfEedIyor4inw8atppRxM5BdUnIt+3mAm/QXUm7Dw74CHnXpfZKXkp07EXJGiA8hStqCINAWhdw==",
"cpu": [
"arm64"
],
@@ -3530,10 +3363,10 @@
"darwin"
]
},
- "node_modules/@unrs/rspack-resolver-binding-darwin-x64": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-darwin-x64/-/rspack-resolver-binding-darwin-x64-1.1.0.tgz",
- "integrity": "sha512-MqHyrtIw2ra0KZlniDITROq6rEiMsBnaJtQDYLNDv/y+pvPSXdB3VveZCwSldXJ9TrST2b3NnIbmehljjsMVhg==",
+ "node_modules/@unrs/resolver-binding-darwin-x64": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.4.1.tgz",
+ "integrity": "sha512-X8c3PhWziEMKAzZz+YAYWfwawi5AEgzy/hmfizAB4C70gMHLKmInJcp1270yYAOs7z07YVFI220pp50z24Jk3A==",
"cpu": [
"x64"
],
@@ -3544,10 +3377,10 @@
"darwin"
]
},
- "node_modules/@unrs/rspack-resolver-binding-freebsd-x64": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-freebsd-x64/-/rspack-resolver-binding-freebsd-x64-1.1.0.tgz",
- "integrity": "sha512-bopyOqmtWn8np1d4iN90PE1tYHopdWwei7mK8/8mf4qhc99f7WRNXtWa1MoL5sjN3DWef3jvr0+MGnMS9MTfJA==",
+ "node_modules/@unrs/resolver-binding-freebsd-x64": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.4.1.tgz",
+ "integrity": "sha512-UUr/nREy1UdtxXQnmLaaTXFGOcGxPwNIzeJdb3KXai3TKtC1UgNOB9s8KOA4TaxOUBR/qVgL5BvBwmUjD5yuVA==",
"cpu": [
"x64"
],
@@ -3558,10 +3391,10 @@
"freebsd"
]
},
- "node_modules/@unrs/rspack-resolver-binding-linux-arm-gnueabihf": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-linux-arm-gnueabihf/-/rspack-resolver-binding-linux-arm-gnueabihf-1.1.0.tgz",
- "integrity": "sha512-XldXRkQurDBXCiCuIaWcqOX6UtvjFW8O3CH/kFEZxNJISOAt+ztgyRQRxYhf+T1p18R4boripKmWKEE0uBCiYw==",
+ "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.4.1.tgz",
+ "integrity": "sha512-e3pII53dEeS8inkX6A1ad2UXE0nuoWCqik4kOxaDnls0uJUq0ntdj5d9IYd+bv5TDwf9DSge/xPOvCmRYH+Tsw==",
"cpu": [
"arm"
],
@@ -3572,10 +3405,24 @@
"linux"
]
},
- "node_modules/@unrs/rspack-resolver-binding-linux-arm64-gnu": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-linux-arm64-gnu/-/rspack-resolver-binding-linux-arm64-gnu-1.1.0.tgz",
- "integrity": "sha512-8zubI4MY3whPfLNHEiJ0TeSC5eSmNVWTEGAeMGALCUQtVD9TyZTd6wGwWrQVRN7ESIapWUSChkPLr+Bi13d9sQ==",
+ "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.4.1.tgz",
+ "integrity": "sha512-e/AKKd9gR+HNmVyDEPI/PIz2t0DrA3cyonHNhHVjrkxe8pMCiYiqhtn1+h+yIpHUtUlM6Y1FNIdivFa+r7wrEQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.4.1.tgz",
+ "integrity": "sha512-vtIu34luF1jRktlHtiwm2mjuE8oJCsFiFr8hT5+tFQdqFKjPhbJXn83LswKsOhy0GxAEevpXDI4xxEwkjuXIPA==",
"cpu": [
"arm64"
],
@@ -3586,10 +3433,10 @@
"linux"
]
},
- "node_modules/@unrs/rspack-resolver-binding-linux-arm64-musl": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-linux-arm64-musl/-/rspack-resolver-binding-linux-arm64-musl-1.1.0.tgz",
- "integrity": "sha512-+GAyOhl8KPqJsILpHTB/mMc4hfOwI4INed8VAZnSvdaL0ec3Sz/6UXEeTtucW1fWhwaP3lVlpjv2xuRhOCjehA==",
+ "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.4.1.tgz",
+ "integrity": "sha512-H3PaOuGyhFXiyJd+09uPhGl4gocmhyi1BRzvsP8Lv5AQO3p3/ZY7WjV4t2NkBksm9tMjf3YbOVHyPWi2eWsNYw==",
"cpu": [
"arm64"
],
@@ -3600,10 +3447,38 @@
"linux"
]
},
- "node_modules/@unrs/rspack-resolver-binding-linux-x64-gnu": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-linux-x64-gnu/-/rspack-resolver-binding-linux-x64-gnu-1.1.0.tgz",
- "integrity": "sha512-0zoy6UwRFoto5boJKGjgDpA+4kv+G1kysgrAe0KVefJXOnDNJlfgcV7mOV2O9J+FqtIQsXvzmOJxDB9e1Hhbzw==",
+ "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.4.1.tgz",
+ "integrity": "sha512-4+GmJcaaFntCi1S01YByqp8wLMjV/FyQyHVGm0vedIhL1Vfx7uHkz/sZmKsidRwokBGuxi92GFmSzqT2O8KcNA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.4.1.tgz",
+ "integrity": "sha512-6RDQVCmtFYTlhy89D5ixTqo9bTQqFhvNN0Ey1wJs5r+01Dq15gPHRXv2jF2bQATtMrOfYwv+R2ZR9ew1N1N3YQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.4.1.tgz",
+ "integrity": "sha512-XpU9uzIkD86+19NjCXxlVPISMUrVXsXo5htxtuG+uJ59p5JauSRZsIxQxzzfKzkxEjdvANPM/lS1HFoX6A6QeA==",
"cpu": [
"x64"
],
@@ -3614,10 +3489,10 @@
"linux"
]
},
- "node_modules/@unrs/rspack-resolver-binding-linux-x64-musl": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-linux-x64-musl/-/rspack-resolver-binding-linux-x64-musl-1.1.0.tgz",
- "integrity": "sha512-XjC+aZKi+X+ma5e6yaVTvojK0v0kxfikbP1dB0klx80NjCW9KRrldiZxAo7ME8Tb4a7Fz0J6PDOVzd9tFYwkQQ==",
+ "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.4.1.tgz",
+ "integrity": "sha512-3CDjG/spbTKCSHl66QP2ekHSD+H34i7utuDIM5gzoNBcZ1gTO0Op09Wx5cikXnhORRf9+HyDWzm37vU1PLSM1A==",
"cpu": [
"x64"
],
@@ -3628,10 +3503,10 @@
"linux"
]
},
- "node_modules/@unrs/rspack-resolver-binding-wasm32-wasi": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-wasm32-wasi/-/rspack-resolver-binding-wasm32-wasi-1.1.0.tgz",
- "integrity": "sha512-eVBK4z9VN3vahAh2+bQBl+vs9JgWEF1xeccilDcerGNkmlFHB1My5++sbeZzou+DExkioVAdfK+uVyVnHS4k7Q==",
+ "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.4.1.tgz",
+ "integrity": "sha512-50tYhvbCTnuzMn7vmP8IV2UKF7ITo1oihygEYq9wW2DUb/Y+QMqBHJUSCABRngATjZ4shOK6f2+s0gQX6ElENQ==",
"cpu": [
"wasm32"
],
@@ -3639,16 +3514,16 @@
"license": "MIT",
"optional": true,
"dependencies": {
- "@napi-rs/wasm-runtime": "^0.2.7"
+ "@napi-rs/wasm-runtime": "^0.2.8"
},
"engines": {
"node": ">=14.0.0"
}
},
- "node_modules/@unrs/rspack-resolver-binding-win32-arm64-msvc": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-win32-arm64-msvc/-/rspack-resolver-binding-win32-arm64-msvc-1.1.0.tgz",
- "integrity": "sha512-ktm/CnSKOt/Wwdi2SBECLPJ+gL5oaw8LDHG4IfOQO5oT6qlIP0DaOUPrTf8g/WTlLnSp4TryDBM0B/gGla3LEw==",
+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.4.1.tgz",
+ "integrity": "sha512-KyJiIne/AqV4IW0wyQO34wSMuJwy3VxVQOfIXIPyQ/Up6y/zi2P/WwXb78gHsLiGRUqCA9LOoCX+6dQZde0g1g==",
"cpu": [
"arm64"
],
@@ -3659,10 +3534,24 @@
"win32"
]
},
- "node_modules/@unrs/rspack-resolver-binding-win32-x64-msvc": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@unrs/rspack-resolver-binding-win32-x64-msvc/-/rspack-resolver-binding-win32-x64-msvc-1.1.0.tgz",
- "integrity": "sha512-cdMid8RdR6XaQ5uAudzdu9Ydl3HbYjiwpsh+X01APmTZG2ph7OeaRTozW4F8ScUHkPHfrYedv9McICbHxgBvXQ==",
+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.4.1.tgz",
+ "integrity": "sha512-y2NUD7pygrBolN2NoXUrwVqBpKPhF8DiSNE5oB5/iFO49r2DpoYqdj5HPb3F42fPBH5qNqj6Zg63+xCEzAD2hw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.4.1.tgz",
+ "integrity": "sha512-hVXaObGI2lGFmrtT77KSbPQ3I+zk9IU500wobjk0+oX59vg/0VqAzABNtt3YSQYgXTC2a/LYxekLfND/wlt0yQ==",
"cpu": [
"x64"
],
@@ -3674,9 +3563,9 @@
]
},
"node_modules/@vitejs/plugin-vue": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz",
- "integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==",
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.3.tgz",
+ "integrity": "sha512-IYSLEQj4LgZZuoVpdSUCw3dIynTWQgPlaRP6iAvMle4My0HdYwr5g5wQAfwOeHQBmYwEkqF70nRpSilr6PoUDg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3688,9 +3577,9 @@
}
},
"node_modules/@vitest/eslint-plugin": {
- "version": "1.1.37",
- "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.37.tgz",
- "integrity": "sha512-cnlBV8zr0oaBu1Vk6ruqWzpPzFCtwY0yuwUQpNIeFOUl3UhXVwNUoOYfWkZzeToGuNBaXvIsr6/RgHrXiHXqXA==",
+ "version": "1.1.39",
+ "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.39.tgz",
+ "integrity": "sha512-l5/MUFCYI8nxwr62JHlWwXfeQNS8E7xy71lSLGQ3CrjGjBdWLs1Rtee+BvYwy2m4YVPwYqUwdcAIOaZOwPUpfg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -3709,14 +3598,14 @@
}
},
"node_modules/@vitest/expect": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.8.tgz",
- "integrity": "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz",
+ "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/spy": "3.0.8",
- "@vitest/utils": "3.0.8",
+ "@vitest/spy": "3.1.1",
+ "@vitest/utils": "3.1.1",
"chai": "^5.2.0",
"tinyrainbow": "^2.0.0"
},
@@ -3725,13 +3614,13 @@
}
},
"node_modules/@vitest/mocker": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.8.tgz",
- "integrity": "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz",
+ "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/spy": "3.0.8",
+ "@vitest/spy": "3.1.1",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.17"
},
@@ -3752,9 +3641,9 @@
}
},
"node_modules/@vitest/mocker/node_modules/@types/estree": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
- "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
+ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
"dev": true,
"license": "MIT"
},
@@ -3779,9 +3668,9 @@
}
},
"node_modules/@vitest/pretty-format": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.8.tgz",
- "integrity": "sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz",
+ "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3792,13 +3681,13 @@
}
},
"node_modules/@vitest/runner": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.8.tgz",
- "integrity": "sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz",
+ "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/utils": "3.0.8",
+ "@vitest/utils": "3.1.1",
"pathe": "^2.0.3"
},
"funding": {
@@ -3806,13 +3695,13 @@
}
},
"node_modules/@vitest/snapshot": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.8.tgz",
- "integrity": "sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz",
+ "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "3.0.8",
+ "@vitest/pretty-format": "3.1.1",
"magic-string": "^0.30.17",
"pathe": "^2.0.3"
},
@@ -3831,9 +3720,9 @@
}
},
"node_modules/@vitest/spy": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.8.tgz",
- "integrity": "sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz",
+ "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3844,13 +3733,13 @@
}
},
"node_modules/@vitest/utils": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.8.tgz",
- "integrity": "sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz",
+ "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "3.0.8",
+ "@vitest/pretty-format": "3.1.1",
"loupe": "^3.1.3",
"tinyrainbow": "^2.0.0"
},
@@ -4378,9 +4267,9 @@
"license": "MIT"
},
"node_modules/ansi_up": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/ansi_up/-/ansi_up-6.0.2.tgz",
- "integrity": "sha512-3G3vKvl1ilEp7J1u6BmULpMA0xVoW/f4Ekqhl8RTrJrhEBkonKn5k3bUc5Xt+qDayA6iDX0jyUh3AbZjB/l0tw==",
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/ansi_up/-/ansi_up-6.0.5.tgz",
+ "integrity": "sha512-bo4K8S5usgFivfgvgQozTC2EfusPf76o7w0LUVdAOkpISvVmQqtwCdF5c6okokrgIN13KhFIVB/0BhnNXueQeA==",
"license": "MIT",
"engines": {
"node": "*"
@@ -5103,9 +4992,9 @@
}
},
"node_modules/clippie": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/clippie/-/clippie-4.1.5.tgz",
- "integrity": "sha512-ZNGL7+p8HZWM2G8fecb3N7izoagXGktTg7nSYxzBID4OAtOBU7SUFvI9EL/0IZpy9VkU8AY6Zp8AvaH4/Xj7pA==",
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/clippie/-/clippie-4.1.6.tgz",
+ "integrity": "sha512-1M3xZRNWcVwRkh3i2XcVYFjVtfC6FCLmIpk5s54uT3+jkBa25KRE3dB0Fgkt/YLoeR7AABWkVTlb0WziQYGgaw==",
"license": "BSD-2-Clause"
},
"node_modules/cliui": {
@@ -6586,25 +6475,24 @@
}
},
"node_modules/eslint-import-resolver-typescript": {
- "version": "3.9.0",
- "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.9.0.tgz",
- "integrity": "sha512-EUcFmaz0zAa6P2C9jAb5XDymRld8S6TURvWcIW7y+czOW+K8hrjgQgbhBsNE0J/dDZ6HLfcn70LqnIil9W+ICw==",
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.3.2.tgz",
+ "integrity": "sha512-T2LqBXj87ndEC9t1LrDiPkzalSFzD4rrXr6BTzGdgMx1jdQM4T972guQvg7Ih+LNO51GURXI/qMHS5GF3h1ilw==",
"dev": true,
"license": "ISC",
"dependencies": {
- "@nolyfill/is-core-module": "1.0.39",
- "debug": "^4.3.7",
+ "debug": "^4.4.0",
"get-tsconfig": "^4.10.0",
- "is-bun-module": "^1.0.2",
- "oxc-resolver": "^5.0.0",
+ "is-bun-module": "^2.0.0",
"stable-hash": "^0.0.5",
- "tinyglobby": "^0.2.12"
+ "tinyglobby": "^0.2.12",
+ "unrs-resolver": "^1.4.1"
},
"engines": {
- "node": "^14.18.0 || >=16.0.0"
+ "node": "^16.17.0 || >=18.6.0"
},
"funding": {
- "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts"
+ "url": "https://opencollective.com/eslint-import-resolver-typescript"
},
"peerDependencies": {
"eslint": "*",
@@ -6797,24 +6685,25 @@
}
},
"node_modules/eslint-plugin-import-x": {
- "version": "4.7.2",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.7.2.tgz",
- "integrity": "sha512-+GpGWKbQMK3izrwU4XoRGdAJHwvxuboiNusqU25nNXlRsmnxj8B5niQRuFK1aHEvcbIKE6D9ZfwjsLmBQbnJmw==",
+ "version": "4.10.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.10.2.tgz",
+ "integrity": "sha512-jO3Y6+zBUyTX5MVbbLSzoz6fe65t+WEBaXStRLM4EBhZWbuSwAH3cLwARtM0Yp4zRtZGp9sL2zzK7G9JkHR8LA==",
"dev": true,
"license": "MIT",
"dependencies": {
+ "@pkgr/core": "^0.2.1",
"@types/doctrine": "^0.0.9",
- "@typescript-eslint/utils": "^8.26.1",
+ "@typescript-eslint/utils": "^8.29.0",
"debug": "^4.4.0",
"doctrine": "^3.0.0",
"eslint-import-resolver-node": "^0.3.9",
"get-tsconfig": "^4.10.0",
"is-glob": "^4.0.3",
- "minimatch": "^10.0.1",
- "rspack-resolver": "^1.1.0",
+ "minimatch": "^9.0.3 || ^10.0.1",
"semver": "^7.7.1",
"stable-hash": "^0.0.5",
- "tslib": "^2.8.1"
+ "tslib": "^2.8.1",
+ "unrs-resolver": "^1.4.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -6823,6 +6712,19 @@
"eslint": "^8.57.0 || ^9.0.0"
}
},
+ "node_modules/eslint-plugin-import-x/node_modules/@pkgr/core": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.1.tgz",
+ "integrity": "sha512-VzgHzGblFmUeBmmrk55zPyrQIArQN4vujc9shWytaPdB3P7qhi0cpaiKIr7tlCmFv2lYUwnLospIqjL9ZSAhhg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
"node_modules/eslint-plugin-import/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -7176,14 +7078,14 @@
}
},
"node_modules/eslint-plugin-wc": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.2.1.tgz",
- "integrity": "sha512-KstLqGmyQz088DvFlDYHg0sHih+w2QeulreCi1D1ftr357klO2zqHdG/bbnNMmuQdVFDuNkopNIyNhmG0XCT/g==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-3.0.0.tgz",
+ "integrity": "sha512-TVbwa4ytaKoUGCCAG14+FPDF3v4nGoPcEOd4kjN2MLZ2NTo1lHUs7HsVaiwC0ReKQVivJ7+v6d7u0GYu1c6GJQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-valid-element-name": "^1.0.0",
- "js-levenshtein-esm": "^1.2.0"
+ "js-levenshtein-esm": "^2.0.0"
},
"peerDependencies": {
"eslint": ">=8.40.0"
@@ -8275,13 +8177,13 @@
}
},
"node_modules/is-bun-module": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.3.0.tgz",
- "integrity": "sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
+ "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "semver": "^7.6.3"
+ "semver": "^7.7.1"
}
},
"node_modules/is-core-module": {
@@ -8520,9 +8422,9 @@
"license": "MIT"
},
"node_modules/js-levenshtein-esm": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz",
- "integrity": "sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-2.0.0.tgz",
+ "integrity": "sha512-1n4LEPOL4wRXY8rOQcuA7Iuaphe5xCMayvufCzlLAi+hRsnBRDbSS6XPuV58CBVJxj5D9ApFLyjQ7KzFToyHBw==",
"dev": true,
"license": "MIT"
},
@@ -8751,9 +8653,9 @@
"license": "MIT"
},
"node_modules/langium": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/langium/-/langium-3.0.0.tgz",
- "integrity": "sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz",
+ "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==",
"license": "MIT",
"dependencies": {
"chevrotain": "~11.0.3",
@@ -9244,9 +9146,9 @@
}
},
"node_modules/material-icon-theme": {
- "version": "5.20.0",
- "resolved": "https://registry.npmjs.org/material-icon-theme/-/material-icon-theme-5.20.0.tgz",
- "integrity": "sha512-EAz5I2O7Hq6G8Rv0JdO6NXL+jK/mvDppcVUVbsUMpSqSmFczNdaR5WJ3lOiRz4HNBlEN2i2sVSfuqI5iNQfGLg==",
+ "version": "5.21.1",
+ "resolved": "https://registry.npmjs.org/material-icon-theme/-/material-icon-theme-5.21.1.tgz",
+ "integrity": "sha512-7gWH20MC3rvf1fBXwTpeMEAJqfuhXaciY2IN/hb74hR0XU/TnicoggblFoWtZvt0HrCzxyqX5P+u60BHWXEEHw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9316,14 +9218,14 @@
}
},
"node_modules/mermaid": {
- "version": "11.5.0",
- "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.5.0.tgz",
- "integrity": "sha512-IYhyukID3zzDj1EihKiN1lp+PXNImoJ3Iyz73qeDAgnus4BNGsJV1n471P4PyeGxPVONerZxignwGxGTSwZnlg==",
+ "version": "11.6.0",
+ "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.6.0.tgz",
+ "integrity": "sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==",
"license": "MIT",
"dependencies": {
"@braintree/sanitize-url": "^7.0.4",
"@iconify/utils": "^2.1.33",
- "@mermaid-js/parser": "^0.3.0",
+ "@mermaid-js/parser": "^0.4.0",
"@types/d3": "^7.4.3",
"cytoscape": "^3.29.3",
"cytoscape-cose-bilkent": "^4.1.0",
@@ -10327,29 +10229,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/oxc-resolver": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-5.0.0.tgz",
- "integrity": "sha512-66fopyAqCN8Mx4tzNiBXWbk8asCSuxUWN62gwTc3yfRs7JfWhX/eVJCf+fUrfbNOdQVOWn+o8pAKllp76ysMXA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/Boshen"
- },
- "optionalDependencies": {
- "@oxc-resolver/binding-darwin-arm64": "5.0.0",
- "@oxc-resolver/binding-darwin-x64": "5.0.0",
- "@oxc-resolver/binding-freebsd-x64": "5.0.0",
- "@oxc-resolver/binding-linux-arm-gnueabihf": "5.0.0",
- "@oxc-resolver/binding-linux-arm64-gnu": "5.0.0",
- "@oxc-resolver/binding-linux-arm64-musl": "5.0.0",
- "@oxc-resolver/binding-linux-x64-gnu": "5.0.0",
- "@oxc-resolver/binding-linux-x64-musl": "5.0.0",
- "@oxc-resolver/binding-wasm32-wasi": "5.0.0",
- "@oxc-resolver/binding-win32-arm64-msvc": "5.0.0",
- "@oxc-resolver/binding-win32-x64-msvc": "5.0.0"
- }
- },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -11652,29 +11531,6 @@
"points-on-path": "^0.2.1"
}
},
- "node_modules/rspack-resolver": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/rspack-resolver/-/rspack-resolver-1.1.0.tgz",
- "integrity": "sha512-pJfTX5KuwbJc4agd2AQ9sMwrBxMAGkLt4/HHw5+L06WuzxjsEjg3oDKdbfn43QGq0Stw8wQ7VpZjWA/T03L0Pg==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/JounQin"
- },
- "optionalDependencies": {
- "@unrs/rspack-resolver-binding-darwin-arm64": "1.1.0",
- "@unrs/rspack-resolver-binding-darwin-x64": "1.1.0",
- "@unrs/rspack-resolver-binding-freebsd-x64": "1.1.0",
- "@unrs/rspack-resolver-binding-linux-arm-gnueabihf": "1.1.0",
- "@unrs/rspack-resolver-binding-linux-arm64-gnu": "1.1.0",
- "@unrs/rspack-resolver-binding-linux-arm64-musl": "1.1.0",
- "@unrs/rspack-resolver-binding-linux-x64-gnu": "1.1.0",
- "@unrs/rspack-resolver-binding-linux-x64-musl": "1.1.0",
- "@unrs/rspack-resolver-binding-wasm32-wasi": "1.1.0",
- "@unrs/rspack-resolver-binding-win32-arm64-msvc": "1.1.0",
- "@unrs/rspack-resolver-binding-win32-x64-msvc": "1.1.0"
- }
- },
"node_modules/run-con": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz",
@@ -12258,9 +12114,9 @@
"license": "ISC"
},
"node_modules/stylelint": {
- "version": "16.16.0",
- "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.16.0.tgz",
- "integrity": "sha512-40X5UOb/0CEFnZVEHyN260HlSSUxPES+arrUphOumGWgXERHfwCD0kNBVILgQSij8iliYVwlc0V7M5bcLP9vPg==",
+ "version": "16.18.0",
+ "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.18.0.tgz",
+ "integrity": "sha512-OXb68qzesv7J70BSbFwfK3yTVLEVXiQ/ro6wUE4UrSbKCMjLLA02S8Qq3LC01DxKyVjk7z8xh35aB4JzO3/sNA==",
"dev": true,
"funding": [
{
@@ -12321,9 +12177,9 @@
}
},
"node_modules/stylelint-config-recommended": {
- "version": "15.0.0",
- "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-15.0.0.tgz",
- "integrity": "sha512-9LejMFsat7L+NXttdHdTq94byn25TD+82bzGRiV1Pgasl99pWnwipXS5DguTpp3nP1XjvLXVnEJIuYBfsRjRkA==",
+ "version": "16.0.0",
+ "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-16.0.0.tgz",
+ "integrity": "sha512-4RSmPjQegF34wNcK1e1O3Uz91HN8P1aFdFzio90wNK9mjgAI19u5vsU868cVZboKzCaa5XbpvtTzAAGQAxpcXA==",
"dev": true,
"funding": [
{
@@ -12340,7 +12196,7 @@
"node": ">=18.12.0"
},
"peerDependencies": {
- "stylelint": "^16.13.0"
+ "stylelint": "^16.16.0"
}
},
"node_modules/stylelint-declaration-block-no-ignored-properties": {
@@ -12370,9 +12226,9 @@
}
},
"node_modules/stylelint-define-config": {
- "version": "16.15.0",
- "resolved": "https://registry.npmjs.org/stylelint-define-config/-/stylelint-define-config-16.15.0.tgz",
- "integrity": "sha512-nzHX9ZpI/k4A7izGYPS79xLAf2HyGvYkk/UXMgsQ7ZQEvkOZpQt4Aca4Qn5DYqNmWnqNlW5E3wK+qUmdR3vdxg==",
+ "version": "16.17.0",
+ "resolved": "https://registry.npmjs.org/stylelint-define-config/-/stylelint-define-config-16.17.0.tgz",
+ "integrity": "sha512-HToy83mn05K4g72GjXfyw3PhFV9QZKVJWt6m+w7+XNqAOsegiBI+sYXq8sg0R+cedsi2KVyfmEEpKO550yMmrQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12777,9 +12633,9 @@
}
},
"node_modules/swagger-ui-dist": {
- "version": "5.20.1",
- "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.20.1.tgz",
- "integrity": "sha512-qBPCis2w8nP4US7SvUxdJD3OwKcqiWeZmjN2VWhq2v+ESZEXOP/7n4DeiOiiZcGYTKMHAHUUrroHaTsjUWTEGw==",
+ "version": "5.20.7",
+ "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.20.7.tgz",
+ "integrity": "sha512-gLpb1wrWinUwMFKfSvDYsIlCyGQSryftzi6uWc9Qo98zO3mFT6oHOqmDUu5OoahvepuS6HGTe/3MsGUCVtpLig==",
"license": "Apache-2.0",
"dependencies": {
"@scarf/scarf": "=1.4.0"
@@ -13181,9 +13037,9 @@
}
},
"node_modules/type-fest": {
- "version": "4.37.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.37.0.tgz",
- "integrity": "sha512-S/5/0kFftkq27FPNye0XM1e2NsnoD/3FS+pBmbjmmtLT6I+i344KoOf7pvXreaFsDamWeaJX55nczA1m5PsBDg==",
+ "version": "4.39.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.39.1.tgz",
+ "integrity": "sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
@@ -13194,9 +13050,9 @@
}
},
"node_modules/typescript": {
- "version": "5.8.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
- "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -13247,6 +13103,33 @@
"node": ">= 10.0.0"
}
},
+ "node_modules/unrs-resolver": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.4.1.tgz",
+ "integrity": "sha512-MhPB3wBI5BR8TGieTb08XuYlE8oFVEXdSAgat3psdlRyejl8ojQ8iqPcjh094qCZ1r+TnkxzP6BeCd/umfHckQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/JounQin"
+ },
+ "optionalDependencies": {
+ "@unrs/resolver-binding-darwin-arm64": "1.4.1",
+ "@unrs/resolver-binding-darwin-x64": "1.4.1",
+ "@unrs/resolver-binding-freebsd-x64": "1.4.1",
+ "@unrs/resolver-binding-linux-arm-gnueabihf": "1.4.1",
+ "@unrs/resolver-binding-linux-arm-musleabihf": "1.4.1",
+ "@unrs/resolver-binding-linux-arm64-gnu": "1.4.1",
+ "@unrs/resolver-binding-linux-arm64-musl": "1.4.1",
+ "@unrs/resolver-binding-linux-ppc64-gnu": "1.4.1",
+ "@unrs/resolver-binding-linux-s390x-gnu": "1.4.1",
+ "@unrs/resolver-binding-linux-x64-gnu": "1.4.1",
+ "@unrs/resolver-binding-linux-x64-musl": "1.4.1",
+ "@unrs/resolver-binding-wasm32-wasi": "1.4.1",
+ "@unrs/resolver-binding-win32-arm64-msvc": "1.4.1",
+ "@unrs/resolver-binding-win32-ia32-msvc": "1.4.1",
+ "@unrs/resolver-binding-win32-x64-msvc": "1.4.1"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
@@ -13436,9 +13319,9 @@
}
},
"node_modules/vite-node": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.8.tgz",
- "integrity": "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz",
+ "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -13527,31 +13410,31 @@
}
},
"node_modules/vitest": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.8.tgz",
- "integrity": "sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz",
+ "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/expect": "3.0.8",
- "@vitest/mocker": "3.0.8",
- "@vitest/pretty-format": "^3.0.8",
- "@vitest/runner": "3.0.8",
- "@vitest/snapshot": "3.0.8",
- "@vitest/spy": "3.0.8",
- "@vitest/utils": "3.0.8",
+ "@vitest/expect": "3.1.1",
+ "@vitest/mocker": "3.1.1",
+ "@vitest/pretty-format": "^3.1.1",
+ "@vitest/runner": "3.1.1",
+ "@vitest/snapshot": "3.1.1",
+ "@vitest/spy": "3.1.1",
+ "@vitest/utils": "3.1.1",
"chai": "^5.2.0",
"debug": "^4.4.0",
- "expect-type": "^1.1.0",
+ "expect-type": "^1.2.0",
"magic-string": "^0.30.17",
"pathe": "^2.0.3",
- "std-env": "^3.8.0",
+ "std-env": "^3.8.1",
"tinybench": "^2.9.0",
"tinyexec": "^0.3.2",
"tinypool": "^1.0.2",
"tinyrainbow": "^2.0.0",
"vite": "^5.0.0 || ^6.0.0",
- "vite-node": "3.0.8",
+ "vite-node": "3.1.1",
"why-is-node-running": "^2.3.0"
},
"bin": {
@@ -13567,8 +13450,8 @@
"@edge-runtime/vm": "*",
"@types/debug": "^4.1.12",
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
- "@vitest/browser": "3.0.8",
- "@vitest/ui": "3.0.8",
+ "@vitest/browser": "3.1.1",
+ "@vitest/ui": "3.1.1",
"happy-dom": "*",
"jsdom": "*"
},
diff --git a/package.json b/package.json
index ef1a132994..040d6b24fd 100644
--- a/package.json
+++ b/package.json
@@ -15,12 +15,12 @@
"@primer/octicons": "19.15.1",
"@silverwind/vue3-calendar-heatmap": "2.0.6",
"add-asset-webpack-plugin": "3.0.0",
- "ansi_up": "6.0.2",
+ "ansi_up": "6.0.5",
"asciinema-player": "3.9.0",
"chart.js": "4.4.8",
"chartjs-adapter-dayjs-4": "1.0.4",
"chartjs-plugin-zoom": "2.2.0",
- "clippie": "4.1.5",
+ "clippie": "4.1.6",
"cropperjs": "1.6.2",
"css-loader": "7.1.2",
"dayjs": "1.11.13",
@@ -34,7 +34,7 @@
"jquery": "3.7.1",
"katex": "0.16.21",
"license-checker-webpack-plugin": "0.2.1",
- "mermaid": "11.5.0",
+ "mermaid": "11.6.0",
"mini-css-extract-plugin": "2.9.2",
"minimatch": "10.0.1",
"monaco-editor": "0.52.2",
@@ -45,14 +45,14 @@
"postcss-loader": "8.1.1",
"postcss-nesting": "13.0.1",
"sortablejs": "1.15.6",
- "swagger-ui-dist": "5.20.1",
+ "swagger-ui-dist": "5.20.7",
"tailwindcss": "3.4.17",
"throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0",
"tippy.js": "6.3.7",
"toastify-js": "1.12.0",
"tributejs": "5.1.3",
- "typescript": "5.8.2",
+ "typescript": "5.8.3",
"uint8-to-base64": "0.2.0",
"vanilla-colorful": "0.7.2",
"vue": "3.5.13",
@@ -79,15 +79,15 @@
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/toastify-js": "1.12.3",
- "@typescript-eslint/eslint-plugin": "8.26.1",
- "@typescript-eslint/parser": "8.26.1",
- "@vitejs/plugin-vue": "5.2.1",
- "@vitest/eslint-plugin": "1.1.37",
+ "@typescript-eslint/eslint-plugin": "8.29.1",
+ "@typescript-eslint/parser": "8.29.1",
+ "@vitejs/plugin-vue": "5.2.3",
+ "@vitest/eslint-plugin": "1.1.39",
"eslint": "8.57.0",
- "eslint-import-resolver-typescript": "3.9.0",
+ "eslint-import-resolver-typescript": "4.3.2",
"eslint-plugin-array-func": "4.0.0",
"eslint-plugin-github": "5.0.2",
- "eslint-plugin-import-x": "4.7.2",
+ "eslint-plugin-import-x": "4.10.2",
"eslint-plugin-no-jquery": "3.1.1",
"eslint-plugin-no-use-extend-native": "0.5.0",
"eslint-plugin-playwright": "2.2.0",
@@ -96,23 +96,23 @@
"eslint-plugin-unicorn": "56.0.1",
"eslint-plugin-vue": "10.0.0",
"eslint-plugin-vue-scoped-css": "2.9.0",
- "eslint-plugin-wc": "2.2.1",
+ "eslint-plugin-wc": "3.0.0",
"happy-dom": "17.4.4",
"markdownlint-cli": "0.44.0",
- "material-icon-theme": "5.20.0",
+ "material-icon-theme": "5.21.1",
"nolyfill": "1.0.44",
"postcss-html": "1.8.0",
- "stylelint": "16.16.0",
- "stylelint-config-recommended": "15.0.0",
+ "stylelint": "16.18.0",
+ "stylelint-config-recommended": "16.0.0",
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
"stylelint-declaration-strict-value": "1.10.11",
- "stylelint-define-config": "16.15.0",
+ "stylelint-define-config": "16.17.0",
"stylelint-value-no-unknown-custom-properties": "6.0.1",
"svgo": "3.3.2",
- "type-fest": "4.37.0",
+ "type-fest": "4.39.1",
"updates": "16.4.2",
"vite-string-plugin": "1.4.4",
- "vitest": "3.0.8",
+ "vitest": "3.1.1",
"vue-tsc": "2.2.8"
},
"browserslist": [
diff --git a/poetry.lock b/poetry.lock
index ca7ae78cb8..b532bf0a3a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand.
[[package]]
name = "click"
@@ -119,18 +119,18 @@ six = ">=1.13.0"
[[package]]
name = "json5"
-version = "0.10.0"
+version = "0.12.0"
description = "A Python implementation of the JSON5 data format."
optional = false
python-versions = ">=3.8.0"
groups = ["dev"]
files = [
- {file = "json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa"},
- {file = "json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559"},
+ {file = "json5-0.12.0-py3-none-any.whl", hash = "sha256:6d37aa6c08b0609f16e1ec5ff94697e2cbbfbad5ac112afa05794da9ab7810db"},
+ {file = "json5-0.12.0.tar.gz", hash = "sha256:0b4b6ff56801a1c7dc817b0241bca4ce474a0e6a163bfef3fc594d3fd263ff3a"},
]
[package.extras]
-dev = ["build (==1.2.2.post1)", "coverage (==7.5.3)", "mypy (==1.13.0)", "pip (==24.3.1)", "pylint (==3.2.3)", "ruff (==0.7.3)", "twine (==5.1.1)", "uv (==0.5.1)"]
+dev = ["build (==1.2.2.post1)", "coverage (==7.5.4) ; python_version < \"3.9\"", "coverage (==7.8.0) ; python_version >= \"3.9\"", "mypy (==1.14.1) ; python_version < \"3.9\"", "mypy (==1.15.0) ; python_version >= \"3.9\"", "pip (==25.0.1)", "pylint (==3.2.7) ; python_version < \"3.9\"", "pylint (==3.3.6) ; python_version >= \"3.9\"", "ruff (==0.11.2)", "twine (==6.1.0)", "uv (==0.6.11)"]
[[package]]
name = "pathspec"
@@ -330,7 +330,7 @@ description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
-markers = "python_version < \"3.11\""
+markers = "python_version == \"3.10\""
files = [
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
{file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
@@ -390,27 +390,27 @@ telegram = ["requests"]
[[package]]
name = "typing-extensions"
-version = "4.12.2"
+version = "4.13.1"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
-markers = "python_version < \"3.11\""
+markers = "python_version == \"3.10\""
files = [
- {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
- {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
+ {file = "typing_extensions-4.13.1-py3-none-any.whl", hash = "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69"},
+ {file = "typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff"},
]
[[package]]
name = "yamllint"
-version = "1.36.1"
+version = "1.37.0"
description = "A linter for YAML files."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
- {file = "yamllint-1.36.1-py3-none-any.whl", hash = "sha256:3e2ccd47ea12449837adf6b2c56fd9e31172ce42bc1470380806be461f25df66"},
- {file = "yamllint-1.36.1.tar.gz", hash = "sha256:a287689daaafc301a80549b2d0170452ebfdcabd826e3fe3b4c66e322d4851fa"},
+ {file = "yamllint-1.37.0-py3-none-any.whl", hash = "sha256:c03ab4e79ab4af964c8eb16ac9746880fc76a3bb0ffb14925b9a55220ae7dda0"},
+ {file = "yamllint-1.37.0.tar.gz", hash = "sha256:ead81921d4d87216b2528b7a055664708f9fb8267beb0c427cb706ac6ab93580"},
]
[package.dependencies]
@@ -423,4 +423,4 @@ dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"]
[metadata]
lock-version = "2.1"
python-versions = "^3.10"
-content-hash = "d48a461813418f7b803a7f94713d93076a009a96554e0f4c707be0cc2a95b563"
+content-hash = "d8ed1d7f88ff4be57be2c599b9d906dee25d0f6f6e46fdc9051c892c4e812a1c"
diff --git a/pyproject.toml b/pyproject.toml
index a2aedfe148..4eed5f0e74 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -6,7 +6,7 @@ python = "^3.10"
[tool.poetry.group.dev.dependencies]
djlint = "1.36.4"
-yamllint = "1.36.1"
+yamllint = "1.37.0"
[tool.djlint]
profile="golang"
diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go
index f70e5dd235..71c21f2dde 100644
--- a/routers/api/v1/org/team.go
+++ b/routers/api/v1/org/team.go
@@ -141,26 +141,18 @@ func GetTeam(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, apiTeam)
}
-func attachTeamUnits(team *organization.Team, units []string) {
+func attachTeamUnits(team *organization.Team, defaultAccessMode perm.AccessMode, units []string) {
unitTypes, _ := unit_model.FindUnitTypes(units...)
team.Units = make([]*organization.TeamUnit, 0, len(units))
for _, tp := range unitTypes {
team.Units = append(team.Units, &organization.TeamUnit{
OrgID: team.OrgID,
Type: tp,
- AccessMode: team.AccessMode,
+ AccessMode: defaultAccessMode,
})
}
}
-func convertUnitsMap(unitsMap map[string]string) map[unit_model.Type]perm.AccessMode {
- res := make(map[unit_model.Type]perm.AccessMode, len(unitsMap))
- for unitKey, p := range unitsMap {
- res[unit_model.TypeFromKey(unitKey)] = perm.ParseAccessMode(p)
- }
- return res
-}
-
func attachTeamUnitsMap(team *organization.Team, unitsMap map[string]string) {
team.Units = make([]*organization.TeamUnit, 0, len(unitsMap))
for unitKey, p := range unitsMap {
@@ -214,24 +206,22 @@ func CreateTeam(ctx *context.APIContext) {
// "422":
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.CreateTeamOption)
- p := perm.ParseAccessMode(form.Permission)
- if p < perm.AccessModeAdmin && len(form.UnitsMap) > 0 {
- p = unit_model.MinUnitAccessMode(convertUnitsMap(form.UnitsMap))
- }
+ teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin)
team := &organization.Team{
OrgID: ctx.Org.Organization.ID,
Name: form.Name,
Description: form.Description,
IncludesAllRepositories: form.IncludesAllRepositories,
CanCreateOrgRepo: form.CanCreateOrgRepo,
- AccessMode: p,
+ AccessMode: teamPermission,
}
if team.AccessMode < perm.AccessModeAdmin {
if len(form.UnitsMap) > 0 {
attachTeamUnitsMap(team, form.UnitsMap)
} else if len(form.Units) > 0 {
- attachTeamUnits(team, form.Units)
+ unitPerm := perm.ParseAccessMode(form.Permission, perm.AccessModeRead, perm.AccessModeWrite)
+ attachTeamUnits(team, unitPerm, form.Units)
} else {
ctx.APIErrorInternal(errors.New("units permission should not be empty"))
return
@@ -304,15 +294,10 @@ func EditTeam(ctx *context.APIContext) {
isAuthChanged := false
isIncludeAllChanged := false
if !team.IsOwnerTeam() && len(form.Permission) != 0 {
- // Validate permission level.
- p := perm.ParseAccessMode(form.Permission)
- if p < perm.AccessModeAdmin && len(form.UnitsMap) > 0 {
- p = unit_model.MinUnitAccessMode(convertUnitsMap(form.UnitsMap))
- }
-
- if team.AccessMode != p {
+ teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin)
+ if team.AccessMode != teamPermission {
isAuthChanged = true
- team.AccessMode = p
+ team.AccessMode = teamPermission
}
if form.IncludesAllRepositories != nil {
@@ -325,7 +310,8 @@ func EditTeam(ctx *context.APIContext) {
if len(form.UnitsMap) > 0 {
attachTeamUnitsMap(team, form.UnitsMap)
} else if len(form.Units) > 0 {
- attachTeamUnits(team, form.Units)
+ unitPerm := perm.ParseAccessMode(form.Permission, perm.AccessModeRead, perm.AccessModeWrite)
+ attachTeamUnits(team, unitPerm, form.Units)
}
} else {
attachAdminTeamUnits(team)
diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go
index a54225f0fd..d1652c1d51 100644
--- a/routers/api/v1/repo/collaborators.go
+++ b/routers/api/v1/repo/collaborators.go
@@ -181,7 +181,7 @@ func AddOrUpdateCollaborator(ctx *context.APIContext) {
p := perm.AccessModeWrite
if form.Permission != nil {
- p = perm.ParseAccessMode(*form.Permission)
+ p = perm.ParseAccessMode(*form.Permission, perm.AccessModeRead, perm.AccessModeWrite, perm.AccessModeAdmin)
}
if err := repo_service.AddOrUpdateCollaborator(ctx, ctx.Repo.Repository, collaborator, p); err != nil {
diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go
index aeea3708b2..b1b0dd2c49 100644
--- a/routers/web/org/teams.go
+++ b/routers/web/org/teams.go
@@ -284,6 +284,8 @@ func NewTeam(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplTeamNew)
}
+// FIXME: TEAM-UNIT-PERMISSION: this design is not right, when a new unit is added in the future,
+// admin team won't inherit the correct admin permission for the new unit.
func getUnitPerms(forms url.Values, teamPermission perm.AccessMode) map[unit_model.Type]perm.AccessMode {
unitPerms := make(map[unit_model.Type]perm.AccessMode)
for _, ut := range unit_model.AllRepoUnitTypes {
@@ -314,19 +316,14 @@ func getUnitPerms(forms url.Values, teamPermission perm.AccessMode) map[unit_mod
func NewTeamPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateTeamForm)
includesAllRepositories := form.RepoAccess == "all"
- p := perm.ParseAccessMode(form.Permission)
- unitPerms := getUnitPerms(ctx.Req.Form, p)
- if p < perm.AccessModeAdmin {
- // if p is less than admin accessmode, then it should be general accessmode,
- // so we should calculate the minial accessmode from units accessmodes.
- p = unit_model.MinUnitAccessMode(unitPerms)
- }
+ teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin)
+ unitPerms := getUnitPerms(ctx.Req.Form, teamPermission)
t := &org_model.Team{
OrgID: ctx.Org.Organization.ID,
Name: form.TeamName,
Description: form.Description,
- AccessMode: p,
+ AccessMode: teamPermission,
IncludesAllRepositories: includesAllRepositories,
CanCreateOrgRepo: form.CanCreateOrgRepo,
}
@@ -485,13 +482,8 @@ func EditTeam(ctx *context.Context) {
func EditTeamPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateTeamForm)
t := ctx.Org.Team
- newAccessMode := perm.ParseAccessMode(form.Permission)
- unitPerms := getUnitPerms(ctx.Req.Form, newAccessMode)
- if newAccessMode < perm.AccessModeAdmin {
- // if newAccessMode is less than admin accessmode, then it should be general accessmode,
- // so we should calculate the minial accessmode from units accessmodes.
- newAccessMode = unit_model.MinUnitAccessMode(unitPerms)
- }
+ teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin)
+ unitPerms := getUnitPerms(ctx.Req.Form, teamPermission)
isAuthChanged := false
isIncludeAllChanged := false
includesAllRepositories := form.RepoAccess == "all"
@@ -503,9 +495,9 @@ func EditTeamPost(ctx *context.Context) {
if !t.IsOwnerTeam() {
t.Name = form.TeamName
- if t.AccessMode != newAccessMode {
+ if t.AccessMode != teamPermission {
isAuthChanged = true
- t.AccessMode = newAccessMode
+ t.AccessMode = teamPermission
}
if t.IncludesAllRepositories != includesAllRepositories {
diff --git a/routers/web/repo/fork.go b/routers/web/repo/fork.go
index 36e64bfee3..79f033659b 100644
--- a/routers/web/repo/fork.go
+++ b/routers/web/repo/fork.go
@@ -91,12 +91,17 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
ctx.Data["CanForkToUser"] = canForkToUser
ctx.Data["Orgs"] = orgs
+ // TODO: this message should only be shown for the "current doer" when it is selected, just like the "new repo" page.
+ // msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", ctx.Doer.MaxCreationLimit())
+
if canForkToUser {
ctx.Data["ContextUser"] = ctx.Doer
+ ctx.Data["CanForkRepoInNewOwner"] = true
} else if len(orgs) > 0 {
ctx.Data["ContextUser"] = orgs[0]
+ ctx.Data["CanForkRepoInNewOwner"] = true
} else {
- ctx.Data["CanForkRepo"] = false
+ ctx.Data["CanForkRepoInNewOwner"] = false
ctx.Flash.Error(ctx.Tr("repo.fork_no_valid_owners"), true)
return nil
}
@@ -120,15 +125,6 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
// Fork render repository fork page
func Fork(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("new_fork")
-
- if ctx.Doer.CanForkRepo() {
- ctx.Data["CanForkRepo"] = true
- } else {
- maxCreationLimit := ctx.Doer.MaxCreationLimit()
- msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
- ctx.Flash.Error(msg, true)
- }
-
getForkRepository(ctx)
if ctx.Written() {
return
@@ -141,7 +137,6 @@ func Fork(ctx *context.Context) {
func ForkPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateRepoForm)
ctx.Data["Title"] = ctx.Tr("new_fork")
- ctx.Data["CanForkRepo"] = true
ctxUser := checkContextUser(ctx, form.UID)
if ctx.Written() {
diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go
index f4ac9d769b..61606f8c5f 100644
--- a/routers/web/repo/githttp.go
+++ b/routers/web/repo/githttp.go
@@ -29,7 +29,6 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
@@ -303,17 +302,12 @@ var (
func dummyInfoRefs(ctx *context.Context) {
infoRefsOnce.Do(func() {
- tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-info-refs-cache")
+ tmpDir, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-info-refs-cache")
if err != nil {
log.Error("Failed to create temp dir for git-receive-pack cache: %v", err)
return
}
-
- defer func() {
- if err := util.RemoveAll(tmpDir); err != nil {
- log.Error("RemoveAll: %v", err)
- }
- }()
+ defer cleanup()
if err := git.InitRepository(ctx, tmpDir, true, git.Sha1ObjectFormat.Name()); err != nil {
log.Error("Failed to init bare repo for git-receive-pack cache: %v", err)
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index e260ea36dd..ee112b83f2 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -87,17 +87,13 @@ func checkContextUser(ctx *context.Context, uid int64) *user_model.User {
return nil
}
- if !ctx.Doer.IsAdmin {
- orgsAvailable := []*organization.Organization{}
- for i := 0; i < len(orgs); i++ {
- if orgs[i].CanCreateRepo() {
- orgsAvailable = append(orgsAvailable, orgs[i])
- }
+ var orgsAvailable []*organization.Organization
+ for i := 0; i < len(orgs); i++ {
+ if ctx.Doer.CanCreateRepoIn(orgs[i].AsUser()) {
+ orgsAvailable = append(orgsAvailable, orgs[i])
}
- ctx.Data["Orgs"] = orgsAvailable
- } else {
- ctx.Data["Orgs"] = orgs
}
+ ctx.Data["Orgs"] = orgsAvailable
// Not equal means current user is an organization.
if uid == ctx.Doer.ID || uid == 0 {
@@ -154,7 +150,7 @@ func createCommon(ctx *context.Context) {
ctx.Data["Licenses"] = repo_module.Licenses
ctx.Data["Readmes"] = repo_module.Readmes
ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
- ctx.Data["CanCreateRepoInDoer"] = ctx.Doer.CanCreateRepo()
+ ctx.Data["CanCreateRepoInDoer"] = ctx.Doer.CanCreateRepoIn(ctx.Doer)
ctx.Data["MaxCreationLimitOfDoer"] = ctx.Doer.MaxCreationLimit()
ctx.Data["SupportedObjectFormats"] = git.DefaultFeatures().SupportedObjectFormats
ctx.Data["DefaultObjectFormat"] = git.Sha1ObjectFormat
diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go
index 655291d25c..efda9bda58 100644
--- a/routers/web/repo/setting/lfs.go
+++ b/routers/web/repo/setting/lfs.go
@@ -109,17 +109,13 @@ func LFSLocks(ctx *context.Context) {
}
// Clone base repo.
- tmpBasePath, err := repo_module.CreateTemporaryPath("locks")
+ tmpBasePath, cleanup, err := repo_module.CreateTemporaryPath("locks")
if err != nil {
log.Error("Failed to create temporary path: %v", err)
ctx.ServerError("LFSLocks", err)
return
}
- defer func() {
- if err := repo_module.RemoveTemporaryPath(tmpBasePath); err != nil {
- log.Error("LFSLocks: RemoveTemporaryPath: %v", err)
- }
- }()
+ defer cleanup()
if err := git.Clone(ctx, ctx.Repo.Repository.RepoPath(), tmpBasePath, git.CloneRepoOptions{
Bare: true,
diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index 380fec9d4a..5552a8726c 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -59,6 +59,7 @@ func SettingsCtxData(ctx *context.Context) {
ctx.Data["DisableNewPushMirrors"] = setting.Mirror.DisableNewPush
ctx.Data["DefaultMirrorInterval"] = setting.Mirror.DefaultInterval
ctx.Data["MinimumMirrorInterval"] = setting.Mirror.MinInterval
+ ctx.Data["CanConvertFork"] = ctx.Repo.Repository.IsFork && ctx.Doer.CanCreateRepoIn(ctx.Repo.Repository.Owner)
signing, _ := asymkey_service.SigningKey(ctx, ctx.Repo.Repository.RepoPath())
ctx.Data["SigningKeyAvailable"] = len(signing) > 0
@@ -786,7 +787,7 @@ func handleSettingsPostConvertFork(ctx *context.Context) {
return
}
- if !ctx.Repo.Owner.CanCreateRepo() {
+ if !ctx.Doer.CanForkRepoIn(ctx.Repo.Owner) {
maxCreationLimit := ctx.Repo.Owner.MaxCreationLimit()
msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
ctx.Flash.Error(msg)
diff --git a/services/context/org.go b/services/context/org.go
index 992a48afa0..c8b6ed09b7 100644
--- a/services/context/org.go
+++ b/services/context/org.go
@@ -182,7 +182,7 @@ func OrgAssignment(opts OrgAssignmentOptions) func(ctx *Context) {
return
}
for _, team := range teams {
- if team.IncludesAllRepositories && team.AccessMode >= perm.AccessModeAdmin {
+ if team.IncludesAllRepositories && team.HasAdminAccess() {
shouldSeeAllTeams = true
break
}
@@ -228,7 +228,7 @@ func OrgAssignment(opts OrgAssignmentOptions) func(ctx *Context) {
return
}
- ctx.Org.IsTeamAdmin = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.AccessMode >= perm.AccessModeAdmin
+ ctx.Org.IsTeamAdmin = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.HasAdminAccess()
ctx.Data["IsTeamAdmin"] = ctx.Org.IsTeamAdmin
if opts.RequireTeamAdmin && !ctx.Org.IsTeamAdmin {
ctx.NotFound(err)
diff --git a/services/org/team.go b/services/org/team.go
index ee3bd898ea..6890dafd90 100644
--- a/services/org/team.go
+++ b/services/org/team.go
@@ -259,37 +259,6 @@ func AddTeamMember(ctx context.Context, team *organization.Team, user *user_mode
}
team.NumMembers++
-
- // Give access to team repositories.
- // update exist access if mode become bigger
- subQuery := builder.Select("repo_id").From("team_repo").
- Where(builder.Eq{"team_id": team.ID})
-
- if _, err := sess.Where("user_id=?", user.ID).
- In("repo_id", subQuery).
- And("mode < ?", team.AccessMode).
- SetExpr("mode", team.AccessMode).
- Update(new(access_model.Access)); err != nil {
- return fmt.Errorf("update user accesses: %w", err)
- }
-
- // for not exist access
- var repoIDs []int64
- accessSubQuery := builder.Select("repo_id").From("access").Where(builder.Eq{"user_id": user.ID})
- if err := sess.SQL(subQuery.And(builder.NotIn("repo_id", accessSubQuery))).Find(&repoIDs); err != nil {
- return fmt.Errorf("select id accesses: %w", err)
- }
-
- accesses := make([]*access_model.Access, 0, 100)
- for i, repoID := range repoIDs {
- accesses = append(accesses, &access_model.Access{RepoID: repoID, UserID: user.ID, Mode: team.AccessMode})
- if (i%100 == 0 || i == len(repoIDs)-1) && len(accesses) > 0 {
- if err = db.Insert(ctx, accesses); err != nil {
- return fmt.Errorf("insert new user accesses: %w", err)
- }
- accesses = accesses[:0]
- }
- }
return nil
})
if err != nil {
diff --git a/services/org/team_test.go b/services/org/team_test.go
index a7070fadb0..831fe1669f 100644
--- a/services/org/team_test.go
+++ b/services/org/team_test.go
@@ -166,24 +166,6 @@ func TestRemoveTeamMember(t *testing.T) {
assert.True(t, organization.IsErrLastOrgOwner(err))
}
-func TestRepository_RecalculateAccesses3(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
- team5 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5})
- user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29})
-
- has, err := db.GetEngine(db.DefaultContext).Get(&access_model.Access{UserID: user29.ID, RepoID: 23})
- assert.NoError(t, err)
- assert.False(t, has)
-
- // adding user29 to team5 should add an explicit access row for repo 23
- // even though repo 23 is public
- assert.NoError(t, AddTeamMember(db.DefaultContext, team5, user29))
-
- has, err = db.GetEngine(db.DefaultContext).Get(&access_model.Access{UserID: user29.ID, RepoID: 23})
- assert.NoError(t, err)
- assert.True(t, has)
-}
-
func TestIncludesAllRepositoriesTeams(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
diff --git a/services/pull/patch.go b/services/pull/patch.go
index 68f3f02669..7a24237724 100644
--- a/services/pull/patch.go
+++ b/services/pull/patch.go
@@ -355,23 +355,19 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
}
// 3b. Create a plain patch from head to base
- tmpPatchFile, err := os.CreateTemp("", "patch")
+ tmpPatchFile, cleanup, err := setting.AppDataTempDir("git-repo-content").CreateTempFileRandom("patch")
if err != nil {
log.Error("Unable to create temporary patch file! Error: %v", err)
return false, fmt.Errorf("unable to create temporary patch file! Error: %w", err)
}
- defer func() {
- _ = util.Remove(tmpPatchFile.Name())
- }()
+ defer cleanup()
if err := gitRepo.GetDiffBinary(pr.MergeBase+"...tracking", tmpPatchFile); err != nil {
- tmpPatchFile.Close()
log.Error("Unable to get patch file from %s to %s in %s Error: %v", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err)
return false, fmt.Errorf("unable to get patch file from %s to %s in %s Error: %w", pr.MergeBase, pr.HeadBranch, pr.BaseRepo.FullName(), err)
}
stat, err := tmpPatchFile.Stat()
if err != nil {
- tmpPatchFile.Close()
return false, fmt.Errorf("unable to stat patch file: %w", err)
}
patchPath := tmpPatchFile.Name()
diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go
index 3f33370798..d543e3d4a3 100644
--- a/services/pull/temp_repo.go
+++ b/services/pull/temp_repo.go
@@ -74,11 +74,13 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
}
// Clone base repo.
- tmpBasePath, err := repo_module.CreateTemporaryPath("pull")
+ tmpBasePath, cleanup, err := repo_module.CreateTemporaryPath("pull")
if err != nil {
log.Error("CreateTemporaryPath[%-v]: %v", pr, err)
return nil, nil, err
}
+ cancel = cleanup
+
prCtx = &prContext{
Context: ctx,
tmpBasePath: tmpBasePath,
@@ -86,11 +88,6 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest)
outbuf: &strings.Builder{},
errbuf: &strings.Builder{},
}
- cancel = func() {
- if err := repo_module.RemoveTemporaryPath(tmpBasePath); err != nil {
- log.Error("Error whilst removing removing temporary repo for %-v: %v", pr, err)
- }
- }
baseRepoPath := pr.BaseRepo.RepoPath()
headRepoPath := pr.HeadRepo.RepoPath()
diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index b7321156d9..6d5505c42c 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -16,7 +16,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
@@ -28,18 +27,30 @@ import (
"github.com/gobwas/glob"
)
+func deleteFailedAdoptRepository(repoID int64) error {
+ return db.WithTx(db.DefaultContext, func(ctx context.Context) error {
+ if err := deleteDBRepository(ctx, repoID); err != nil {
+ return fmt.Errorf("deleteDBRepository: %w", err)
+ }
+ if err := git_model.DeleteRepoBranches(ctx, repoID); err != nil {
+ return fmt.Errorf("deleteRepoBranches: %w", err)
+ }
+ return repo_model.DeleteRepoReleases(ctx, repoID)
+ })
+}
+
// AdoptRepository adopts pre-existing repository files for the user/organization.
-func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
- if !doer.IsAdmin && !u.CanCreateRepo() {
+func AdoptRepository(ctx context.Context, doer, owner *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
+ if !doer.CanCreateRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
- Limit: u.MaxRepoCreation,
+ Limit: owner.MaxRepoCreation,
}
}
repo := &repo_model.Repository{
- OwnerID: u.ID,
- Owner: u,
- OwnerName: u.Name,
+ OwnerID: owner.ID,
+ Owner: owner,
+ OwnerName: owner.Name,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
@@ -48,59 +59,52 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR
IsPrivate: opts.IsPrivate,
IsFsckEnabled: !opts.IsMirror,
CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch,
- Status: opts.Status,
+ Status: repo_model.RepositoryBeingMigrated,
IsEmpty: !opts.AutoInit,
}
- if err := db.WithTx(ctx, func(ctx context.Context) error {
- isExist, err := gitrepo.IsRepositoryExist(ctx, repo)
+ // 1 - create the repository database operations first
+ err := db.WithTx(ctx, func(ctx context.Context) error {
+ return createRepositoryInDB(ctx, doer, owner, repo, false)
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // last - clean up if something goes wrong
+ // WARNING: Don't override all later err with local variables
+ defer func() {
if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
- return err
- }
- if !isExist {
- return repo_model.ErrRepoNotExist{
- OwnerName: u.Name,
- Name: repo.Name,
+ // we can not use the ctx because it maybe canceled or timeout
+ if errDel := deleteFailedAdoptRepository(repo.ID); errDel != nil {
+ log.Error("Failed to delete repository %s that could not be adopted: %v", repo.FullName(), errDel)
}
}
+ }()
- if err := CreateRepositoryByExample(ctx, doer, u, repo, true, false); err != nil {
- return err
- }
-
- // Re-fetch the repository from database before updating it (else it would
- // override changes that were done earlier with sql)
- if repo, err = repo_model.GetRepositoryByID(ctx, repo.ID); err != nil {
- return fmt.Errorf("getRepositoryByID: %w", err)
- }
- return nil
- }); err != nil {
- return nil, err
+ // Re-fetch the repository from database before updating it (else it would
+ // override changes that were done earlier with sql)
+ if repo, err = repo_model.GetRepositoryByID(ctx, repo.ID); err != nil {
+ return nil, fmt.Errorf("getRepositoryByID: %w", err)
}
- if err := func() error {
- if err := adoptRepository(ctx, repo, opts.DefaultBranch); err != nil {
- return fmt.Errorf("adoptRepository: %w", err)
- }
-
- if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil {
- return fmt.Errorf("checkDaemonExportOK: %w", err)
- }
-
- if stdout, _, err := git.NewCommand("update-server-info").
- RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}); err != nil {
- log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
- return fmt.Errorf("CreateRepository(git update-server-info): %w", err)
- }
- return nil
- }(); err != nil {
- if errDel := DeleteRepository(ctx, doer, repo, false /* no notify */); errDel != nil {
- log.Error("Failed to delete repository %s that could not be adopted: %v", repo.FullName(), errDel)
- }
- return nil, err
+ // 2 - adopt the repository from disk
+ if err = adoptRepository(ctx, repo, opts.DefaultBranch); err != nil {
+ return nil, fmt.Errorf("adoptRepository: %w", err)
}
- notify_service.AdoptRepository(ctx, doer, u, repo)
+
+ // 3 - Update the git repository
+ if err = updateGitRepoAfterCreate(ctx, repo); err != nil {
+ return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
+ }
+
+ // 4 - update repository status
+ repo.Status = repo_model.RepositoryReady
+ if err = repo_model.UpdateRepositoryCols(ctx, repo, "status"); err != nil {
+ return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
+ }
+
+ notify_service.AdoptRepository(ctx, doer, owner, repo)
return repo, nil
}
diff --git a/services/repository/adopt_test.go b/services/repository/adopt_test.go
index 294185ea1f..6e1dc417b3 100644
--- a/services/repository/adopt_test.go
+++ b/services/repository/adopt_test.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@@ -89,10 +90,36 @@ func TestListUnadoptedRepositories_ListOptions(t *testing.T) {
func TestAdoptRepository(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- assert.NoError(t, unittest.SyncDirs(filepath.Join(setting.RepoRootPath, "user2", "repo1.git"), filepath.Join(setting.RepoRootPath, "user2", "test-adopt.git")))
+
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- _, err := AdoptRepository(db.DefaultContext, user2, user2, CreateRepoOptions{Name: "test-adopt"})
+
+ // a successful adopt
+ destDir := filepath.Join(setting.RepoRootPath, user2.Name, "test-adopt.git")
+ assert.NoError(t, unittest.SyncDirs(filepath.Join(setting.RepoRootPath, user2.Name, "repo1.git"), destDir))
+
+ adoptedRepo, err := AdoptRepository(db.DefaultContext, user2, user2, CreateRepoOptions{Name: "test-adopt"})
assert.NoError(t, err)
repoTestAdopt := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: "test-adopt"})
assert.Equal(t, "sha1", repoTestAdopt.ObjectFormatName)
+
+ // just delete the adopted repo's db records
+ err = deleteFailedAdoptRepository(adoptedRepo.ID)
+ assert.NoError(t, err)
+
+ unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: "test-adopt"})
+
+ // a failed adopt because some mock data
+ // remove the hooks directory and create a file so that we cannot create the hooks successfully
+ _ = os.RemoveAll(filepath.Join(destDir, "hooks", "update.d"))
+ assert.NoError(t, os.WriteFile(filepath.Join(destDir, "hooks", "update.d"), []byte("tests"), os.ModePerm))
+
+ adoptedRepo, err = AdoptRepository(db.DefaultContext, user2, user2, CreateRepoOptions{Name: "test-adopt"})
+ assert.Error(t, err)
+ assert.Nil(t, adoptedRepo)
+
+ unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: "test-adopt"})
+
+ exist, err := util.IsExist(repo_model.RepoPath(user2.Name, "test-adopt"))
+ assert.NoError(t, err)
+ assert.True(t, exist) // the repository should be still in the disk
}
diff --git a/services/repository/create.go b/services/repository/create.go
index af4e897151..5a9b2ecd2a 100644
--- a/services/repository/create.go
+++ b/services/repository/create.go
@@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
@@ -28,7 +29,6 @@ import (
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates/vars"
- "code.gitea.io/gitea/modules/util"
)
// CreateRepoOptions contains the create repository options
@@ -140,21 +140,20 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir
// InitRepository initializes README and .gitignore if needed.
func initRepository(ctx context.Context, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) {
- if err = repo_module.CheckInitRepository(ctx, repo); err != nil {
- return err
+ // Init git bare new repository.
+ if err = git.InitRepository(ctx, repo.RepoPath(), true, repo.ObjectFormatName); err != nil {
+ return fmt.Errorf("git.InitRepository: %w", err)
+ } else if err = gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
+ return fmt.Errorf("createDelegateHooks: %w", err)
}
// Initialize repository according to user's choice.
if opts.AutoInit {
- tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-"+repo.Name)
+ tmpDir, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("repos-" + repo.Name)
if err != nil {
- return fmt.Errorf("Failed to create temp dir for repository %s: %w", repo.FullName(), err)
+ return fmt.Errorf("failed to create temp dir for repository %s: %w", repo.FullName(), err)
}
- defer func() {
- if err := util.RemoveAll(tmpDir); err != nil {
- log.Warn("Unable to remove temporary directory: %s: Error: %v", tmpDir, err)
- }
- }()
+ defer cleanup()
if err = prepareRepoCommit(ctx, repo, tmpDir, opts); err != nil {
return fmt.Errorf("prepareRepoCommit: %w", err)
@@ -200,10 +199,10 @@ func initRepository(ctx context.Context, u *user_model.User, repo *repo_model.Re
}
// CreateRepositoryDirectly creates a repository for the user/organization.
-func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
- if !doer.IsAdmin && !u.CanCreateRepo() {
+func CreateRepositoryDirectly(ctx context.Context, doer, owner *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
+ if !doer.CanCreateRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
- Limit: u.MaxRepoCreation,
+ Limit: owner.MaxRepoCreation,
}
}
@@ -223,9 +222,9 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
}
repo := &repo_model.Repository{
- OwnerID: u.ID,
- Owner: u,
- OwnerName: u.Name,
+ OwnerID: owner.ID,
+ Owner: owner,
+ OwnerName: owner.Name,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
@@ -244,100 +243,93 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
ObjectFormatName: opts.ObjectFormatName,
}
- var rollbackRepo *repo_model.Repository
-
- if err := db.WithTx(ctx, func(ctx context.Context) error {
- if err := CreateRepositoryByExample(ctx, doer, u, repo, false, false); err != nil {
- return err
- }
-
- // No need for init mirror.
- if opts.IsMirror {
- return nil
- }
-
- isExist, err := gitrepo.IsRepositoryExist(ctx, repo)
- if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
- return err
- }
- if isExist {
- // repo already exists - We have two or three options.
- // 1. We fail stating that the directory exists
- // 2. We create the db repository to go with this data and adopt the git repo
- // 3. We delete it and start afresh
- //
- // Previously Gitea would just delete and start afresh - this was naughty.
- // So we will now fail and delegate to other functionality to adopt or delete
- log.Error("Files already exist in %s and we are not going to adopt or delete.", repo.FullName())
- return repo_model.ErrRepoFilesAlreadyExist{
- Uname: u.Name,
- Name: repo.Name,
- }
- }
-
- if err = initRepository(ctx, doer, repo, opts); err != nil {
- if err2 := gitrepo.DeleteRepository(ctx, repo); err2 != nil {
- log.Error("initRepository: %v", err)
- return fmt.Errorf(
- "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2)
- }
- return fmt.Errorf("initRepository: %w", err)
- }
-
- // Initialize Issue Labels if selected
- if len(opts.IssueLabels) > 0 {
- if err = repo_module.InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil {
- rollbackRepo = repo
- rollbackRepo.OwnerID = u.ID
- return fmt.Errorf("InitializeLabels: %w", err)
- }
- }
-
- if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil {
- return fmt.Errorf("checkDaemonExportOK: %w", err)
- }
-
- if stdout, _, err := git.NewCommand("update-server-info").
- RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}); err != nil {
- log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
- rollbackRepo = repo
- rollbackRepo.OwnerID = u.ID
- return fmt.Errorf("CreateRepository(git update-server-info): %w", err)
- }
-
- // update licenses
- var licenses []string
- if len(opts.License) > 0 {
- licenses = append(licenses, opts.License)
-
- stdout, _, err := git.NewCommand("rev-parse", "HEAD").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
- if err != nil {
- log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err)
- rollbackRepo = repo
- rollbackRepo.OwnerID = u.ID
- return fmt.Errorf("CreateRepository(git rev-parse HEAD): %w", err)
- }
- if err := repo_model.UpdateRepoLicenses(ctx, repo, stdout, licenses); err != nil {
- return err
- }
- }
- return nil
- }); err != nil {
- if rollbackRepo != nil {
- if errDelete := DeleteRepositoryDirectly(ctx, doer, rollbackRepo.ID); errDelete != nil {
- log.Error("Rollback deleteRepository: %v", errDelete)
- }
- }
+ needsUpdateStatus := opts.Status != repo_model.RepositoryReady
+ // 1 - create the repository database operations first
+ err := db.WithTx(ctx, func(ctx context.Context) error {
+ return createRepositoryInDB(ctx, doer, owner, repo, false)
+ })
+ if err != nil {
return nil, err
}
+ // last - clean up if something goes wrong
+ // WARNING: Don't override all later err with local variables
+ defer func() {
+ if err != nil {
+ // we can not use the ctx because it maybe canceled or timeout
+ cleanupRepository(doer, repo.ID)
+ }
+ }()
+
+ // No need for init mirror.
+ if opts.IsMirror {
+ return repo, nil
+ }
+
+ // 2 - check whether the repository with the same storage exists
+ var isExist bool
+ isExist, err = gitrepo.IsRepositoryExist(ctx, repo)
+ if err != nil {
+ log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
+ return nil, err
+ }
+ if isExist {
+ log.Error("Files already exist in %s and we are not going to adopt or delete.", repo.FullName())
+ // Don't return directly, we need err in defer to cleanupRepository
+ err = repo_model.ErrRepoFilesAlreadyExist{
+ Uname: repo.OwnerName,
+ Name: repo.Name,
+ }
+ return nil, err
+ }
+
+ // 3 - init git repository in storage
+ if err = initRepository(ctx, doer, repo, opts); err != nil {
+ return nil, fmt.Errorf("initRepository: %w", err)
+ }
+
+ // 4 - Initialize Issue Labels if selected
+ if len(opts.IssueLabels) > 0 {
+ if err = repo_module.InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil {
+ return nil, fmt.Errorf("InitializeLabels: %w", err)
+ }
+ }
+
+ // 5 - Update the git repository
+ if err = updateGitRepoAfterCreate(ctx, repo); err != nil {
+ return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
+ }
+
+ // 6 - update licenses
+ var licenses []string
+ if len(opts.License) > 0 {
+ licenses = append(licenses, opts.License)
+
+ var stdout string
+ stdout, _, err = git.NewCommand("rev-parse", "HEAD").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()})
+ if err != nil {
+ log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err)
+ return nil, fmt.Errorf("CreateRepository(git rev-parse HEAD): %w", err)
+ }
+ if err = repo_model.UpdateRepoLicenses(ctx, repo, stdout, licenses); err != nil {
+ return nil, err
+ }
+ }
+
+ // 7 - update repository status to be ready
+ if needsUpdateStatus {
+ repo.Status = repo_model.RepositoryReady
+ if err = repo_model.UpdateRepositoryCols(ctx, repo, "status"); err != nil {
+ return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
+ }
+ }
+
return repo, nil
}
-// CreateRepositoryByExample creates a repository for the user/organization.
-func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt, isFork bool) (err error) {
+// createRepositoryInDB creates a repository for the user/organization.
+func createRepositoryInDB(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, isFork bool) (err error) {
if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
return err
}
@@ -352,19 +344,6 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
}
}
- isExist, err := gitrepo.IsRepositoryExist(ctx, repo)
- if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
- return err
- }
- if !overwriteOrAdopt && isExist {
- log.Error("Files already exist in %s and we are not going to adopt or delete.", repo.FullName())
- return repo_model.ErrRepoFilesAlreadyExist{
- Uname: u.Name,
- Name: repo.Name,
- }
- }
-
if err = db.Insert(ctx, repo); err != nil {
return err
}
@@ -473,3 +452,26 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re
return nil
}
+
+func cleanupRepository(doer *user_model.User, repoID int64) {
+ if errDelete := DeleteRepositoryDirectly(db.DefaultContext, doer, repoID); errDelete != nil {
+ log.Error("cleanupRepository failed: %v", errDelete)
+ // add system notice
+ if err := system_model.CreateRepositoryNotice("DeleteRepositoryDirectly failed when cleanup repository: %v", errDelete); err != nil {
+ log.Error("CreateRepositoryNotice: %v", err)
+ }
+ }
+}
+
+func updateGitRepoAfterCreate(ctx context.Context, repo *repo_model.Repository) error {
+ if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil {
+ return fmt.Errorf("checkDaemonExportOK: %w", err)
+ }
+
+ if stdout, _, err := git.NewCommand("update-server-info").
+ RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}); err != nil {
+ log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
+ return fmt.Errorf("CreateRepository(git update-server-info): %w", err)
+ }
+ return nil
+}
diff --git a/services/repository/create_test.go b/services/repository/create_test.go
new file mode 100644
index 0000000000..9ecf404e8b
--- /dev/null
+++ b/services/repository/create_test.go
@@ -0,0 +1,57 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repository
+
+import (
+ "os"
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ 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/git"
+ "code.gitea.io/gitea/modules/util"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCreateRepositoryDirectly(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ // a successful creating repository
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+
+ createdRepo, err := CreateRepositoryDirectly(git.DefaultContext, user2, user2, CreateRepoOptions{
+ Name: "created-repo",
+ })
+ assert.NoError(t, err)
+ assert.NotNil(t, createdRepo)
+
+ exist, err := util.IsExist(repo_model.RepoPath(user2.Name, createdRepo.Name))
+ assert.NoError(t, err)
+ assert.True(t, exist)
+
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: createdRepo.Name})
+
+ err = DeleteRepositoryDirectly(db.DefaultContext, user2, createdRepo.ID)
+ assert.NoError(t, err)
+
+ // a failed creating because some mock data
+ // create the repository directory so that the creation will fail after database record created.
+ assert.NoError(t, os.MkdirAll(repo_model.RepoPath(user2.Name, createdRepo.Name), os.ModePerm))
+
+ createdRepo2, err := CreateRepositoryDirectly(db.DefaultContext, user2, user2, CreateRepoOptions{
+ Name: "created-repo",
+ })
+ assert.Nil(t, createdRepo2)
+ assert.Error(t, err)
+
+ // assert the cleanup is successful
+ unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: createdRepo.Name})
+
+ exist, err = util.IsExist(repo_model.RepoPath(user2.Name, createdRepo.Name))
+ assert.NoError(t, err)
+ assert.False(t, exist)
+}
diff --git a/services/repository/delete.go b/services/repository/delete.go
index ff74a20817..cf960af8cf 100644
--- a/services/repository/delete.go
+++ b/services/repository/delete.go
@@ -32,6 +32,19 @@ import (
"xorm.io/builder"
)
+func deleteDBRepository(ctx context.Context, repoID int64) error {
+ if cnt, err := db.GetEngine(ctx).ID(repoID).Delete(&repo_model.Repository{}); err != nil {
+ return err
+ } else if cnt != 1 {
+ return repo_model.ErrRepoNotExist{
+ ID: repoID,
+ OwnerName: "",
+ Name: "",
+ }
+ }
+ return nil
+}
+
// DeleteRepository deletes a repository for a user or organization.
// make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID int64, ignoreOrgTeams ...bool) error {
@@ -82,14 +95,8 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID
}
needRewriteKeysFile := deleted > 0
- if cnt, err := sess.ID(repoID).Delete(&repo_model.Repository{}); err != nil {
+ if err := deleteDBRepository(ctx, repoID); err != nil {
return err
- } else if cnt != 1 {
- return repo_model.ErrRepoNotExist{
- ID: repoID,
- OwnerName: "",
- Name: "",
- }
}
if org != nil && org.IsOrganization() {
diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go
index 1969676ab4..493ff9998d 100644
--- a/services/repository/files/temp_repo.go
+++ b/services/repository/files/temp_repo.go
@@ -30,23 +30,24 @@ type TemporaryUploadRepository struct {
repo *repo_model.Repository
gitRepo *git.Repository
basePath string
+ cleanup func()
}
// NewTemporaryUploadRepository creates a new temporary upload repository
func NewTemporaryUploadRepository(repo *repo_model.Repository) (*TemporaryUploadRepository, error) {
- basePath, err := repo_module.CreateTemporaryPath("upload")
+ basePath, cleanup, err := repo_module.CreateTemporaryPath("upload")
if err != nil {
return nil, err
}
- t := &TemporaryUploadRepository{repo: repo, basePath: basePath}
+ t := &TemporaryUploadRepository{repo: repo, basePath: basePath, cleanup: cleanup}
return t, nil
}
// Close the repository cleaning up all files
func (t *TemporaryUploadRepository) Close() {
defer t.gitRepo.Close()
- if err := repo_module.RemoveTemporaryPath(t.basePath); err != nil {
- log.Error("Failed to remove temporary path %s: %v", t.basePath, err)
+ if t.cleanup != nil {
+ t.cleanup()
}
}
diff --git a/services/repository/fork.go b/services/repository/fork.go
index 5b1ba7a418..1794cc18ab 100644
--- a/services/repository/fork.go
+++ b/services/repository/fork.go
@@ -65,7 +65,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
}
// Fork is prohibited, if user has reached maximum limit of repositories
- if !owner.CanForkRepo() {
+ if !doer.CanForkRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: owner.MaxRepoCreation,
}
@@ -100,114 +100,106 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
IsFork: true,
ForkID: opts.BaseRepo.ID,
ObjectFormatName: opts.BaseRepo.ObjectFormatName,
+ Status: repo_model.RepositoryBeingMigrated,
}
- oldRepoPath := opts.BaseRepo.RepoPath()
-
- needsRollback := false
- rollbackFn := func() {
- if !needsRollback {
- return
- }
-
- if exists, _ := gitrepo.IsRepositoryExist(ctx, repo); !exists {
- return
- }
-
- // As the transaction will be failed and hence database changes will be destroyed we only need
- // to delete the related repository on the filesystem
- if errDelete := gitrepo.DeleteRepository(ctx, repo); errDelete != nil {
- log.Error("Failed to remove fork repo")
- }
- }
-
- needsRollbackInPanic := true
- defer func() {
- panicErr := recover()
- if panicErr == nil {
- return
- }
-
- if needsRollbackInPanic {
- rollbackFn()
- }
- panic(panicErr)
- }()
-
- err = db.WithTx(ctx, func(txCtx context.Context) error {
- if err = CreateRepositoryByExample(txCtx, doer, owner, repo, false, true); err != nil {
+ // 1 - Create the repository in the database
+ err = db.WithTx(ctx, func(ctx context.Context) error {
+ if err = createRepositoryInDB(ctx, doer, owner, repo, true); err != nil {
return err
}
-
- if err = repo_model.IncrementRepoForkNum(txCtx, opts.BaseRepo.ID); err != nil {
+ if err = repo_model.IncrementRepoForkNum(ctx, opts.BaseRepo.ID); err != nil {
return err
}
// copy lfs files failure should not be ignored
- if err = git_model.CopyLFS(txCtx, repo, opts.BaseRepo); err != nil {
- return err
- }
-
- needsRollback = true
-
- cloneCmd := git.NewCommand("clone", "--bare")
- if opts.SingleBranch != "" {
- cloneCmd.AddArguments("--single-branch", "--branch").AddDynamicArguments(opts.SingleBranch)
- }
- if stdout, _, err := cloneCmd.AddDynamicArguments(oldRepoPath, repo.RepoPath()).
- RunStdBytes(txCtx, &git.RunOpts{Timeout: 10 * time.Minute}); err != nil {
- log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err)
- return fmt.Errorf("git clone: %w", err)
- }
-
- if err := repo_module.CheckDaemonExportOK(txCtx, repo); err != nil {
- return fmt.Errorf("checkDaemonExportOK: %w", err)
- }
-
- if stdout, _, err := git.NewCommand("update-server-info").
- RunStdString(txCtx, &git.RunOpts{Dir: repo.RepoPath()}); err != nil {
- log.Error("Fork Repository (git update-server-info) failed for %v:\nStdout: %s\nError: %v", repo, stdout, err)
- return fmt.Errorf("git update-server-info: %w", err)
- }
-
- if err = gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
- return fmt.Errorf("createDelegateHooks: %w", err)
- }
-
- gitRepo, err := gitrepo.OpenRepository(txCtx, repo)
- if err != nil {
- return fmt.Errorf("OpenRepository: %w", err)
- }
- defer gitRepo.Close()
-
- _, err = repo_module.SyncRepoBranchesWithRepo(txCtx, repo, gitRepo, doer.ID)
- return err
+ return git_model.CopyLFS(ctx, repo, opts.BaseRepo)
})
- needsRollbackInPanic = false
if err != nil {
- rollbackFn()
return nil, err
}
- // even if below operations failed, it could be ignored. And they will be retried
- if err := repo_module.UpdateRepoSize(ctx, repo); err != nil {
- log.Error("Failed to update size for repository: %v", err)
- }
- if err := repo_model.CopyLanguageStat(ctx, opts.BaseRepo, repo); err != nil {
- log.Error("Copy language stat from oldRepo failed: %v", err)
- }
- if err := repo_model.CopyLicense(ctx, opts.BaseRepo, repo); err != nil {
- return nil, err
- }
-
- gitRepo, err := gitrepo.OpenRepository(ctx, repo)
- if err != nil {
- log.Error("Open created git repository failed: %v", err)
- } else {
- defer gitRepo.Close()
- if err := repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil {
- log.Error("Sync releases from git tags failed: %v", err)
+ // last - clean up if something goes wrong
+ // WARNING: Don't override all later err with local variables
+ defer func() {
+ if err != nil {
+ // we can not use the ctx because it maybe canceled or timeout
+ cleanupRepository(doer, repo.ID)
}
+ }()
+
+ // 2 - check whether the repository with the same storage exists
+ var isExist bool
+ isExist, err = gitrepo.IsRepositoryExist(ctx, repo)
+ if err != nil {
+ log.Error("Unable to check if %s exists. Error: %v", repo.FullName(), err)
+ return nil, err
+ }
+ if isExist {
+ log.Error("Files already exist in %s and we are not going to adopt or delete.", repo.FullName())
+ // Don't return directly, we need err in defer to cleanupRepository
+ err = repo_model.ErrRepoFilesAlreadyExist{
+ Uname: repo.OwnerName,
+ Name: repo.Name,
+ }
+ return nil, err
+ }
+
+ // 3 - Clone the repository
+ cloneCmd := git.NewCommand("clone", "--bare")
+ if opts.SingleBranch != "" {
+ cloneCmd.AddArguments("--single-branch", "--branch").AddDynamicArguments(opts.SingleBranch)
+ }
+ var stdout []byte
+ if stdout, _, err = cloneCmd.AddDynamicArguments(opts.BaseRepo.RepoPath(), repo.RepoPath()).
+ RunStdBytes(ctx, &git.RunOpts{Timeout: 10 * time.Minute}); err != nil {
+ log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err)
+ return nil, fmt.Errorf("git clone: %w", err)
+ }
+
+ // 4 - Update the git repository
+ if err = updateGitRepoAfterCreate(ctx, repo); err != nil {
+ return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
+ }
+
+ // 5 - Create hooks
+ if err = gitrepo.CreateDelegateHooks(ctx, repo); err != nil {
+ return nil, fmt.Errorf("createDelegateHooks: %w", err)
+ }
+
+ // 6 - Sync the repository branches and tags
+ var gitRepo *git.Repository
+ gitRepo, err = gitrepo.OpenRepository(ctx, repo)
+ if err != nil {
+ return nil, fmt.Errorf("OpenRepository: %w", err)
+ }
+ defer gitRepo.Close()
+
+ if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doer.ID); err != nil {
+ return nil, fmt.Errorf("SyncRepoBranchesWithRepo: %w", err)
+ }
+ if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil {
+ return nil, fmt.Errorf("Sync releases from git tags failed: %v", err)
+ }
+
+ // 7 - Update the repository
+ // even if below operations failed, it could be ignored. And they will be retried
+ if err = repo_module.UpdateRepoSize(ctx, repo); err != nil {
+ log.Error("Failed to update size for repository: %v", err)
+ err = nil
+ }
+ if err = repo_model.CopyLanguageStat(ctx, opts.BaseRepo, repo); err != nil {
+ log.Error("Copy language stat from oldRepo failed: %v", err)
+ err = nil
+ }
+ if err = repo_model.CopyLicense(ctx, opts.BaseRepo, repo); err != nil {
+ return nil, err
+ }
+
+ // 8 - update repository status to be ready
+ repo.Status = repo_model.RepositoryReady
+ if err = repo_model.UpdateRepositoryCols(ctx, repo, "status"); err != nil {
+ return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
}
notify_service.ForkRepository(ctx, doer, opts.BaseRepo, repo)
diff --git a/services/repository/fork_test.go b/services/repository/fork_test.go
index 452798b25b..9edc0aa39f 100644
--- a/services/repository/fork_test.go
+++ b/services/repository/fork_test.go
@@ -4,13 +4,16 @@
package repository
import (
+ "os"
"testing"
+ "code.gitea.io/gitea/models/db"
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/git"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@@ -46,3 +49,43 @@ func TestForkRepository(t *testing.T) {
assert.Nil(t, fork2)
assert.True(t, repo_model.IsErrReachLimitOfRepo(err))
}
+
+func TestForkRepositoryCleanup(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ // a successful fork
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
+
+ fork, err := ForkRepository(git.DefaultContext, user2, user2, ForkRepoOptions{
+ BaseRepo: repo10,
+ Name: "test",
+ })
+ assert.NoError(t, err)
+ assert.NotNil(t, fork)
+
+ exist, err := util.IsExist(repo_model.RepoPath(user2.Name, "test"))
+ assert.NoError(t, err)
+ assert.True(t, exist)
+
+ err = DeleteRepositoryDirectly(db.DefaultContext, user2, fork.ID)
+ assert.NoError(t, err)
+
+ // a failed creating because some mock data
+ // create the repository directory so that the creation will fail after database record created.
+ assert.NoError(t, os.MkdirAll(repo_model.RepoPath(user2.Name, "test"), os.ModePerm))
+
+ fork2, err := ForkRepository(db.DefaultContext, user2, user2, ForkRepoOptions{
+ BaseRepo: repo10,
+ Name: "test",
+ })
+ assert.Nil(t, fork2)
+ assert.Error(t, err)
+
+ // assert the cleanup is successful
+ unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerName: user2.Name, Name: "test"})
+
+ exist, err = util.IsExist(repo_model.RepoPath(user2.Name, "test"))
+ assert.NoError(t, err)
+ assert.False(t, exist)
+}
diff --git a/services/repository/generate.go b/services/repository/generate.go
index 9d2bbb1f7f..b02f7c9482 100644
--- a/services/repository/generate.go
+++ b/services/repository/generate.go
@@ -17,11 +17,11 @@ import (
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/gobwas/glob"
@@ -256,16 +256,11 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
}
func generateGitContent(ctx context.Context, repo, templateRepo, generateRepo *repo_model.Repository) (err error) {
- tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-"+repo.Name)
+ tmpDir, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-" + repo.Name)
if err != nil {
- return fmt.Errorf("Failed to create temp dir for repository %s: %w", repo.FullName(), err)
+ return fmt.Errorf("failed to create temp dir for repository %s: %w", repo.FullName(), err)
}
-
- defer func() {
- if err := util.RemoveAll(tmpDir); err != nil {
- log.Error("RemoveAll: %v", err)
- }
- }()
+ defer cleanup()
if err = generateRepoCommit(ctx, repo, templateRepo, generateRepo, tmpDir); err != nil {
return fmt.Errorf("generateRepoCommit: %w", err)
@@ -328,57 +323,6 @@ func (gro GenerateRepoOptions) IsValid() bool {
gro.IssueLabels || gro.ProtectedBranch // or other items as they are added
}
-// generateRepository generates a repository from a template
-func generateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *repo_model.Repository, opts GenerateRepoOptions) (_ *repo_model.Repository, err error) {
- generateRepo := &repo_model.Repository{
- OwnerID: owner.ID,
- Owner: owner,
- OwnerName: owner.Name,
- Name: opts.Name,
- LowerName: strings.ToLower(opts.Name),
- Description: opts.Description,
- DefaultBranch: opts.DefaultBranch,
- IsPrivate: opts.Private,
- IsEmpty: !opts.GitContent || templateRepo.IsEmpty,
- IsFsckEnabled: templateRepo.IsFsckEnabled,
- TemplateID: templateRepo.ID,
- TrustModel: templateRepo.TrustModel,
- ObjectFormatName: templateRepo.ObjectFormatName,
- }
-
- if err = CreateRepositoryByExample(ctx, doer, owner, generateRepo, false, false); err != nil {
- return nil, err
- }
-
- isExist, err := gitrepo.IsRepositoryExist(ctx, generateRepo)
- if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", generateRepo.FullName(), err)
- return nil, err
- }
- if isExist {
- return nil, repo_model.ErrRepoFilesAlreadyExist{
- Uname: generateRepo.OwnerName,
- Name: generateRepo.Name,
- }
- }
-
- if err = repo_module.CheckInitRepository(ctx, generateRepo); err != nil {
- return generateRepo, err
- }
-
- if err = repo_module.CheckDaemonExportOK(ctx, generateRepo); err != nil {
- return generateRepo, fmt.Errorf("checkDaemonExportOK: %w", err)
- }
-
- if stdout, _, err := git.NewCommand("update-server-info").
- RunStdString(ctx, &git.RunOpts{Dir: generateRepo.RepoPath()}); err != nil {
- log.Error("GenerateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", generateRepo, stdout, err)
- return generateRepo, fmt.Errorf("error in GenerateRepository(git update-server-info): %w", err)
- }
-
- return generateRepo, nil
-}
-
var fileNameSanitizeRegexp = regexp.MustCompile(`(?i)\.\.|[<>:\"/\\|?*\x{0000}-\x{001F}]|^(con|prn|aux|nul|com\d|lpt\d)$`)
// Sanitize user input to valid OS filenames
diff --git a/services/repository/migrate.go b/services/repository/migrate.go
index 2a17e9acd9..003be1a9ab 100644
--- a/services/repository/migrate.go
+++ b/services/repository/migrate.go
@@ -118,14 +118,8 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
repo.Owner = u
}
- if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil {
- return repo, fmt.Errorf("checkDaemonExportOK: %w", err)
- }
-
- if stdout, _, err := git.NewCommand("update-server-info").
- RunStdString(ctx, &git.RunOpts{Dir: repoPath}); err != nil {
- log.Error("MigrateRepositoryGitData(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
- return repo, fmt.Errorf("error in MigrateRepositoryGitData(git update-server-info): %w", err)
+ if err := updateGitRepoAfterCreate(ctx, repo); err != nil {
+ return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
}
gitRepo, err := git.OpenRepository(ctx, repoPath)
diff --git a/services/repository/repository.go b/services/repository/repository.go
index 10f175d989..cd56010546 100644
--- a/services/repository/repository.go
+++ b/services/repository/repository.go
@@ -13,7 +13,6 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
- system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/graceful"
@@ -102,8 +101,6 @@ func Init(ctx context.Context) error {
if err := repo_module.LoadRepoConfig(); err != nil {
return err
}
- system_model.RemoveAllWithNotice(ctx, "Clean up temporary repository uploads", setting.Repository.Upload.TempPath)
- system_model.RemoveAllWithNotice(ctx, "Clean up temporary repositories", repo_module.LocalCopyPath())
if err := initPushQueue(); err != nil {
return err
}
diff --git a/services/repository/template.go b/services/repository/template.go
index 36a680c8e2..95f585cead 100644
--- a/services/repository/template.go
+++ b/services/repository/template.go
@@ -5,12 +5,17 @@ package repository
import (
"context"
+ "fmt"
+ "strings"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/log"
notify_service "code.gitea.io/gitea/services/notify"
)
@@ -63,72 +68,126 @@ func GenerateProtectedBranch(ctx context.Context, templateRepo, generateRepo *re
// GenerateRepository generates a repository from a template
func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *repo_model.Repository, opts GenerateRepoOptions) (_ *repo_model.Repository, err error) {
- if !doer.IsAdmin && !owner.CanCreateRepo() {
+ if !doer.CanCreateRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: owner.MaxRepoCreation,
}
}
- var generateRepo *repo_model.Repository
- if err = db.WithTx(ctx, func(ctx context.Context) error {
- generateRepo, err = generateRepository(ctx, doer, owner, templateRepo, opts)
- if err != nil {
- return err
- }
+ generateRepo := &repo_model.Repository{
+ OwnerID: owner.ID,
+ Owner: owner,
+ OwnerName: owner.Name,
+ Name: opts.Name,
+ LowerName: strings.ToLower(opts.Name),
+ Description: opts.Description,
+ DefaultBranch: opts.DefaultBranch,
+ IsPrivate: opts.Private,
+ IsEmpty: !opts.GitContent || templateRepo.IsEmpty,
+ IsFsckEnabled: templateRepo.IsFsckEnabled,
+ TemplateID: templateRepo.ID,
+ TrustModel: templateRepo.TrustModel,
+ ObjectFormatName: templateRepo.ObjectFormatName,
+ Status: repo_model.RepositoryBeingMigrated,
+ }
- // Git Content
- if opts.GitContent && !templateRepo.IsEmpty {
- if err = GenerateGitContent(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
- }
-
- // Topics
- if opts.Topics {
- if err = repo_model.GenerateTopics(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
- }
-
- // Git Hooks
- if opts.GitHooks {
- if err = GenerateGitHooks(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
- }
-
- // Webhooks
- if opts.Webhooks {
- if err = GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
- }
-
- // Avatar
- if opts.Avatar && len(templateRepo.Avatar) > 0 {
- if err = generateAvatar(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
- }
-
- // Issue Labels
- if opts.IssueLabels {
- if err = GenerateIssueLabels(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
- }
-
- if opts.ProtectedBranch {
- if err = GenerateProtectedBranch(ctx, templateRepo, generateRepo); err != nil {
- return err
- }
- }
-
- return nil
+ // 1 - Create the repository in the database
+ if err := db.WithTx(ctx, func(ctx context.Context) error {
+ return createRepositoryInDB(ctx, doer, owner, generateRepo, false)
}); err != nil {
return nil, err
}
+ // last - clean up the repository if something goes wrong
+ defer func() {
+ if err != nil {
+ // we can not use the ctx because it maybe canceled or timeout
+ cleanupRepository(doer, generateRepo.ID)
+ }
+ }()
+
+ // 2 - check whether the repository with the same storage exists
+ isExist, err := gitrepo.IsRepositoryExist(ctx, generateRepo)
+ if err != nil {
+ log.Error("Unable to check if %s exists. Error: %v", generateRepo.FullName(), err)
+ return nil, err
+ }
+ if isExist {
+ // Don't return directly, we need err in defer to cleanupRepository
+ err = repo_model.ErrRepoFilesAlreadyExist{
+ Uname: generateRepo.OwnerName,
+ Name: generateRepo.Name,
+ }
+ return nil, err
+ }
+
+ // 3 -Init git bare new repository.
+ if err = git.InitRepository(ctx, generateRepo.RepoPath(), true, generateRepo.ObjectFormatName); err != nil {
+ return nil, fmt.Errorf("git.InitRepository: %w", err)
+ } else if err = gitrepo.CreateDelegateHooks(ctx, generateRepo); err != nil {
+ return nil, fmt.Errorf("createDelegateHooks: %w", err)
+ }
+
+ // 4 - Update the git repository
+ if err = updateGitRepoAfterCreate(ctx, generateRepo); err != nil {
+ return nil, fmt.Errorf("updateGitRepoAfterCreate: %w", err)
+ }
+
+ // 5 - generate the repository contents according to the template
+ // Git Content
+ if opts.GitContent && !templateRepo.IsEmpty {
+ if err = GenerateGitContent(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
+ }
+ }
+
+ // Topics
+ if opts.Topics {
+ if err = repo_model.GenerateTopics(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
+ }
+ }
+
+ // Git Hooks
+ if opts.GitHooks {
+ if err = GenerateGitHooks(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
+ }
+ }
+
+ // Webhooks
+ if opts.Webhooks {
+ if err = GenerateWebhooks(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
+ }
+ }
+
+ // Avatar
+ if opts.Avatar && len(templateRepo.Avatar) > 0 {
+ if err = generateAvatar(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
+ }
+ }
+
+ // Issue Labels
+ if opts.IssueLabels {
+ if err = GenerateIssueLabels(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
+ }
+ }
+
+ if opts.ProtectedBranch {
+ if err = GenerateProtectedBranch(ctx, templateRepo, generateRepo); err != nil {
+ return nil, err
+ }
+ }
+
+ // 6 - update repository status to be ready
+ generateRepo.Status = repo_model.RepositoryReady
+ if err = repo_model.UpdateRepositoryCols(ctx, generateRepo, "status"); err != nil {
+ return nil, fmt.Errorf("UpdateRepositoryCols: %w", err)
+ }
+
notify_service.CreateRepository(ctx, doer, owner, generateRepo)
return generateRepo, nil
diff --git a/services/repository/transfer.go b/services/repository/transfer.go
index 4e44b90df2..86917ad285 100644
--- a/services/repository/transfer.go
+++ b/services/repository/transfer.go
@@ -61,7 +61,7 @@ func AcceptTransferOwnership(ctx context.Context, repo *repo_model.Repository, d
return err
}
- if !doer.IsAdmin && !repoTransfer.Recipient.CanCreateRepo() {
+ if !doer.CanCreateRepoIn(repoTransfer.Recipient) {
limit := util.Iif(repoTransfer.Recipient.MaxRepoCreation >= 0, repoTransfer.Recipient.MaxRepoCreation, setting.Repository.MaxCreationLimit)
return LimitReachedError{Limit: limit}
}
@@ -416,7 +416,7 @@ func StartRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.Use
return err
}
- if !doer.IsAdmin && !newOwner.CanCreateRepo() {
+ if !doer.CanForkRepoIn(newOwner) {
limit := util.Iif(newOwner.MaxRepoCreation >= 0, newOwner.MaxRepoCreation, setting.Repository.MaxCreationLimit)
return LimitReachedError{Limit: limit}
}
diff --git a/services/repository/transfer_test.go b/services/repository/transfer_test.go
index bf71c7ca2e..80a073e9f9 100644
--- a/services/repository/transfer_test.go
+++ b/services/repository/transfer_test.go
@@ -144,7 +144,7 @@ func TestRepositoryTransferRejection(t *testing.T) {
require.NotNil(t, transfer)
require.NoError(t, transfer.LoadRecipient(db.DefaultContext))
- require.True(t, transfer.Recipient.CanCreateRepo()) // admin is not subject to limits
+ require.True(t, doerAdmin.CanCreateRepoIn(transfer.Recipient)) // admin is not subject to limits
// Administrator should not be affected by the limits so transfer should be successful
assert.NoError(t, AcceptTransferOwnership(db.DefaultContext, repo, doerAdmin))
@@ -158,7 +158,7 @@ func TestRepositoryTransferRejection(t *testing.T) {
require.NotNil(t, transfer)
require.NoError(t, transfer.LoadRecipient(db.DefaultContext))
- require.False(t, transfer.Recipient.CanCreateRepo()) // regular user is subject to limits
+ require.False(t, doer.CanCreateRepoIn(transfer.Recipient)) // regular user is subject to limits
// Cannot accept because of the limit
err = AcceptTransferOwnership(db.DefaultContext, repo, doer)
diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go
index b21f46639d..45a08dc5d6 100644
--- a/services/wiki/wiki.go
+++ b/services/wiki/wiki.go
@@ -102,15 +102,11 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
hasDefaultBranch := gitrepo.IsBranchExist(ctx, repo.WikiStorageRepo(), repo.DefaultWikiBranch)
- basePath, err := repo_module.CreateTemporaryPath("update-wiki")
+ basePath, cleanup, err := repo_module.CreateTemporaryPath("update-wiki")
if err != nil {
return err
}
- defer func() {
- if err := repo_module.RemoveTemporaryPath(basePath); err != nil {
- log.Error("Merge: RemoveTemporaryPath: %s", err)
- }
- }()
+ defer cleanup()
cloneOpts := git.CloneRepoOptions{
Bare: true,
@@ -264,15 +260,11 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
return fmt.Errorf("InitWiki: %w", err)
}
- basePath, err := repo_module.CreateTemporaryPath("update-wiki")
+ basePath, cleanup, err := repo_module.CreateTemporaryPath("update-wiki")
if err != nil {
return err
}
- defer func() {
- if err := repo_module.RemoveTemporaryPath(basePath); err != nil {
- log.Error("Merge: RemoveTemporaryPath: %s", err)
- }
- }()
+ defer cleanup()
if err := git.Clone(ctx, repo.WikiPath(), basePath, git.CloneRepoOptions{
Bare: true,
diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl
index 88dadeb3ee..806347c720 100644
--- a/templates/admin/config.tmpl
+++ b/templates/admin/config.tmpl
@@ -69,10 +69,6 @@
{{if not .SSH.StartBuiltinServer}}
{{ctx.Locale.Tr "admin.config.ssh_root_path"}}
{{.SSH.RootPath}}
- {{ctx.Locale.Tr "admin.config.ssh_key_test_path"}}
- {{.SSH.KeyTestPath}}
- {{ctx.Locale.Tr "admin.config.ssh_keygen_path"}}
- {{.SSH.KeygenPath}}
{{ctx.Locale.Tr "admin.config.ssh_minimum_key_size_check"}}
{{svg (Iif .SSH.MinimumKeySizeCheck "octicon-check" "octicon-x")}}
{{if .SSH.MinimumKeySizeCheck}}
diff --git a/templates/org/team/new.tmpl b/templates/org/team/new.tmpl
index b67c18dd7d..67529ddfba 100644
--- a/templates/org/team/new.tmpl
+++ b/templates/org/team/new.tmpl
@@ -56,7 +56,7 @@
{{end}}
- {{if and .Repository.IsFork .Repository.Owner.CanCreateRepo}}
+ {{if .CanConvertFork}}
{{ctx.Locale.Tr "repo.settings.convert_fork"}}
@@ -916,7 +916,7 @@
{{end}}
- {{if and .Repository.IsFork .Repository.Owner.CanCreateRepo}}
+ {{if .CanConvertFork}}