From 9f9bff0f04e7104fb9db5d2a039aa6d1a2fae08e Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Tue, 17 Dec 2024 00:45:49 +0000 Subject: [PATCH 01/50] [skip ci] Updated translations via Crowdin --- options/locale/locale_fr-FR.ini | 1 - options/locale/locale_ga-IE.ini | 1 - options/locale/locale_pt-PT.ini | 2 +- options/locale/locale_zh-CN.ini | 169 ++++++++++++++++++++++++++++++++ 4 files changed, 170 insertions(+), 3 deletions(-) diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 4d4940cff5..0870983c98 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1676,7 +1676,6 @@ issues.timetracker_timer_stop=Arrêter le minuteur issues.timetracker_timer_discard=Annuler le minuteur issues.timetracker_timer_manually_add=Pointer du temps -issues.time_estimate_placeholder=1h 2m issues.time_estimate_set=Définir le temps estimé issues.time_estimate_display=Estimation : %s issues.change_time_estimate_at=a changé le temps estimé à %s %s diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index f40e0037d2..0b74776c17 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -1676,7 +1676,6 @@ issues.timetracker_timer_stop=Stop an t-amadóir issues.timetracker_timer_discard=Déan an t-amadóir a scriosadh issues.timetracker_timer_manually_add=Cuir Am leis -issues.time_estimate_placeholder=1u 2n issues.time_estimate_set=Socraigh am measta issues.time_estimate_display=Meastachán: %s issues.change_time_estimate_at=d'athraigh an meastachán ama go %s %s diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 776d2bdc2b..b959e7fdba 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1679,7 +1679,6 @@ issues.timetracker_timer_stop=Parar cronómetro issues.timetracker_timer_discard=Descartar cronómetro issues.timetracker_timer_manually_add=Adicionar tempo -issues.time_estimate_placeholder=1h 2m issues.time_estimate_set=Definir tempo estimado issues.time_estimate_display=Estimativa: %s issues.change_time_estimate_at=alterou a estimativa de tempo para %s %s @@ -2632,6 +2631,7 @@ release.new_release=Novo lançamento release.draft=Rascunho release.prerelease=Pré-lançamento release.stable=Estável +release.latest=Mais recente release.compare=Comparar release.edit=editar release.ahead.commits=%d cometimentos diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 3add7f8be3..8c6392a4dc 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -93,6 +93,7 @@ remove_all=移除所有 remove_label_str=`删除标签 "%s"` edit=编辑 view=查看 +test=测试 enabled=启用 disabled=禁用 @@ -103,6 +104,7 @@ copy_url=复制网址 copy_hash=复制哈希值 copy_content=复制内容 copy_branch=复制分支名 +copy_path=复制路径 copy_success=复制成功! copy_error=复制失败 copy_type_unsupported=无法复制此类型的文件内容 @@ -143,6 +145,7 @@ confirm_delete_selected=确认删除所有选中项目? name=名称 value=值 +readme=自述文档 filter=过滤 filter.clear=清除筛选器 @@ -178,6 +181,7 @@ package_kind=搜索软件包... project_kind=搜索项目... branch_kind=搜索分支... tag_kind=搜索标签... +tag_tooltip=搜索匹配的标签。使用“%”来匹配任何序列的数字 commit_kind=搜索提交记录... runner_kind=搜索runners... no_results=未找到匹配结果 @@ -223,16 +227,20 @@ string.desc=Z - A [error] occurred=发生了一个错误 +report_message=如果您确定这是一个 Gitea bug,请在 这里 搜索问题,或在必要时创建一个新工单。 not_found=找不到目标。 network_error=网络错误 [startpage] app_desc=一款极易搭建的自助 Git 服务 install=易安装 +install_desc=通过 二进制 来运行;或者通过 Docker 来运行;或者通过 安装包 来运行。 platform=跨平台 +platform_desc=任何 Go 语言 支持的平台都可以运行 Gitea,包括 Windows、Mac、Linux 以及 ARM。挑一个您喜欢的就行! lightweight=轻量级 lightweight_desc=一个廉价的树莓派的配置足以满足 Gitea 的最低系统硬件要求。最大程度上节省您的服务器资源! license=开源化 +license_desc=所有的代码都开源在 %[2]s 上,赶快加入我们来共同发展这个伟大的项目!还等什么?成为贡献者吧! [install] install=安装页面 @@ -346,6 +354,7 @@ enable_update_checker=启用更新检查 enable_update_checker_helper=通过连接到 gitea.io 定期检查新版本发布。 env_config_keys=环境配置 env_config_keys_prompt=以下环境变量也将应用于您的配置文件: +config_write_file_prompt=这些配置选项将写入以下位置: %s [home] nav_menu=导航菜单 @@ -386,6 +395,7 @@ relevant_repositories=只显示相关的仓库, 显示未过滤 [auth] create_new_account=注册帐号 +already_have_account=已有账号? sign_in_now=立即登录 disable_register_prompt=对不起,注册功能已被关闭。请联系网站管理员。 disable_register_mail=已禁用注册的电子邮件确认。 @@ -394,6 +404,7 @@ remember_me=记住此设备 remember_me.compromised=登录令牌不再有效,因为它可能表明帐户已被破坏。请检查您的帐户是否有异常活动。 forgot_password_title=忘记密码 forgot_password=忘记密码? +need_account=需要一个帐户? sign_up_now=还没账号?马上注册。 sign_up_successful=帐户创建成功。欢迎! confirmation_mail_sent_prompt_ex=一封新的确认邮件已经发送到 %s请在下一个 %s 中检查您的收件箱以完成注册过程。 如果您的注册电子邮件地址不正确,您可以重新登录并更改它。 @@ -449,12 +460,15 @@ authorize_application=应用授权 authorize_redirect_notice=如果您授权此应用,您将会被重定向到 %s。 authorize_application_created_by=此应用由%s创建。 authorize_application_description=如果您允许,它将能够读取和修改您的所有帐户信息,包括私人仓库和组织。 +authorize_application_with_scopes=范围: %s authorize_title=授权 %s 访问您的帐户? authorization_failed=授权失败 authorization_failed_desc=因为检测到无效请求,授权失败。请尝试联系您授权应用的管理员。 sspi_auth_failed=SSPI 认证失败 +password_pwned=此密码出现在 被盗密码 列表上并且曾经被公开。 请使用另一个密码再试一次。 password_pwned_err=无法完成对 HaveIBeenPwned 的请求 last_admin=您不能删除最后一个管理员。必须至少保留一个管理员。 +signin_passkey=使用密钥登录 back_to_sign_in=返回登录页面 [mail] @@ -574,6 +588,8 @@ lang_select_error=从列表中选出语言 username_been_taken=用户名已被使用。 username_change_not_local_user=非本地用户不允许更改用户名。 +change_username_disabled=更改用户名已被禁用。 +change_full_name_disabled=更改用户全名已禁用 username_has_not_been_changed=用户名未更改 repo_name_been_taken=仓库名称已被使用。 repository_force_private=“强制私有”已启用:私有仓库不能被公开。 @@ -623,6 +639,7 @@ org_still_own_repo=该组织仍然是某些仓库的拥有者,请先删除或 org_still_own_packages=该组织仍然是一个或多个软件包的拥有者,请先删除它们。 target_branch_not_exist=目标分支不存在。 +target_ref_not_exist=目标引用 %s 不存在 admin_cannot_delete_self=当您是管理员时,您不能删除自己。请先移除您的管理员权限 @@ -688,14 +705,18 @@ applications=应用 orgs=管理组织 repos=仓库列表 delete=删除帐户 +twofa=两步验证(TOTP) account_link=已绑定帐户 organization=组织 uid=UID +webauthn=两步验证(安全密钥) public_profile=公开信息 biography_placeholder=告诉我们一点您自己! (您可以使用Markdown) location_placeholder=与他人分享你的大概位置 profile_desc=控制您的个人资料对其他用户的显示方式。您的主要电子邮件地址将用于通知、密码恢复和基于网页界面的 Git 操作 +password_username_disabled=不允许非本地用户更改他们的用户名。更多详情请联系您的系统管理员。 +password_full_name_disabled=您无权更改他们的全名。请联系您的站点管理员了解更多详情。 full_name=自定义名称 website=个人网站 location=所在地区 @@ -745,6 +766,7 @@ uploaded_avatar_not_a_image=上传的文件不是一张图片。 uploaded_avatar_is_too_big=上传的文件大小(%d KiB) 超过最大限制(%d KiB)。 update_avatar_success=您的头像已更新。 update_user_avatar_success=用户头像已更新。 +cropper_prompt=您可以在保存前编辑图像。编辑的图像将被保存为 PNG 格式。 change_password=更新密码 old_password=当前密码 @@ -787,6 +809,7 @@ add_email_success=新的电子邮件地址已添加。 email_preference_set_success=电子邮件首选项已成功设置。 add_openid_success=新的 OpenID 地址已添加。 keep_email_private=隐藏电子邮件地址 +keep_email_private_popup=这将会隐藏您的电子邮件地址,不仅在您的个人资料中,还在您使用Web界面创建合并请求或编辑文件时。已推送的提交将不会被修改。在提交中使用 %s 以和您的账号关联。 openid_desc=OpenID 让你可以将认证转发到外部服务。 manage_ssh_keys=管理 SSH 密钥 @@ -905,6 +928,7 @@ create_oauth2_application_success=您已成功创建了一个新的 OAuth2 应 update_oauth2_application_success=您已成功更新了此 OAuth2 应用。 oauth2_application_name=应用名称 oauth2_confidential_client=机密客户端。是否是能够维持凭据机密性的应用,比如网页应用程序。如果是本地应用程序请不要勾选,包括桌面和移动端应用。 +oauth2_skip_secondary_authorization=首次授权后允许公共客户端跳过授权步骤。 可能会带来安全风险。 oauth2_redirect_uris=重定向 URI。每行一个 URI。 save_application=保存 oauth2_client_id=客户端ID @@ -915,6 +939,7 @@ oauth2_client_secret_hint=您离开或刷新此页面后将不会再显示此密 oauth2_application_edit=编辑 oauth2_application_create_description=OAuth2 应用允许您的第三方应用程序访问此实例的用户帐户。 oauth2_application_remove_description=移除一个OAuth2应用将会阻止它访问此实例上的已授权用户账户。是否继续? +oauth2_application_locked=如果配置启用,Gitea预注册一些OAuth2应用程序。 为了防止意外的行为, 这些应用既不能编辑也不能删除。请参阅OAuth2文档以获取更多信息。 authorized_oauth2_applications=已授权的 OAuth2 应用 authorized_oauth2_applications_description=您已授予这些第三方应用程序访问您的个人 Gitea 账户的权限。请撤销那些您不再需要的应用程序的访问权限。 @@ -923,20 +948,26 @@ revoke_oauth2_grant=撤回权限 revoke_oauth2_grant_description=确定撤销此三方应用程序的授权,并阻止此应用程序访问您的数据? revoke_oauth2_grant_success=成功撤销了访问权限。 +twofa_desc=为保护你的账号密码安全,你可以使用智能手机或其它设备来接收时间强相关的一次性密码(TOTP)。 twofa_recovery_tip=如果您丢失了您的设备,您将能够使用一次性恢复密钥来重新获得对您账户的访问。 twofa_is_enrolled=你的账号已启用了两步验证。 twofa_not_enrolled=你的账号未开启两步验证。 twofa_disable=禁用两步认证 +twofa_scratch_token_regenerate=重新生成初始令牌 +twofa_scratch_token_regenerated=您的初始令牌现在是 %s。将其存放在安全的地方,它将不会再次显示。 twofa_enroll=启用两步验证 twofa_disable_note=如果需要, 可以禁用双因素身份验证。 twofa_disable_desc=关掉两步验证会使得您的账号不安全,继续执行? +regenerate_scratch_token_desc=如果您丢失了您的恢复密钥或已经使用它登录, 您可以在这里重置它。 twofa_disabled=两步验证已被禁用。 scan_this_image=使用您的授权应用扫描这张图片: or_enter_secret=或者输入密钥:%s then_enter_passcode=并输入应用程序中显示的密码: passcode_invalid=密码不正确。再试一次。 +twofa_enrolled=你的账号已经启用了两步验证。请保存初始令牌(%s)到一个安全的地方,此令牌仅显示一次。 twofa_failed_get_secret=获取 secret 失败。 +webauthn_desc=安全密钥是包含加密密钥的硬件设备。它们可以用于双因素身份验证。安全密钥必须支持 WebAuthn 身份验证器 标准。 webauthn_register_key=添加安全密钥 webauthn_nickname=昵称 webauthn_delete_key=移除安全密钥 @@ -1002,6 +1033,8 @@ fork_to_different_account=派生到其他账号 fork_visibility_helper=无法更改派生仓库的可见性。 fork_branch=要克隆到 Fork 的分支 all_branches=所有分支 +view_all_branches=查看所有分支 +view_all_tags=查看所有标签 fork_no_valid_owners=这个代码仓库无法被派生,因为没有有效的所有者。 fork.blocked_user=无法克隆仓库,因为您被仓库所有者屏蔽。 use_template=使用此模板 @@ -1013,6 +1046,8 @@ generate_repo=生成仓库 generate_from=生成自 repo_desc=仓库描述 repo_desc_helper=输入简要描述 (可选) +repo_no_desc=无详细信息 +repo_lang=编程语言 repo_gitignore_helper=选择 .gitignore 模板。 repo_gitignore_helper_desc=从常见语言的模板列表中选择忽略跟踪的文件。默认情况下,由开发或构建工具生成的特殊文件都包含在 .gitignore 中。 issue_labels=工单标签 @@ -1074,6 +1109,7 @@ delete_preexisting_success=删除 %s 中未收录的文件 blame_prior=查看此更改前的 blame blame.ignore_revs=忽略 .git-blame-ignore-revs 的修订。点击 绕过 并查看正常的 Blame 视图。 blame.ignore_revs.failed=忽略 .git-blame-ignore-revs 版本失败。 +user_search_tooltip=最多显示30名用户 tree_path_not_found_commit=路径%[1]s 在提交 %[2]s 中不存在 tree_path_not_found_branch=路径 %[1]s 不存在于分支 %[2]s 中。 @@ -1222,6 +1258,7 @@ releases=版本发布 tag=Git标签 released_this=发布 tagged_this=已标记 +file.title=%s 位于 %s file_raw=原始文件 file_history=文件历史 file_view_source=源码模式 @@ -1238,6 +1275,7 @@ ambiguous_runes_header=`此文件含有模棱两可的 Unicode 字符` ambiguous_runes_description=`此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。` invisible_runes_line=`此行含有不可见的 unicode 字符` ambiguous_runes_line=`此行有模棱两可的 unicode 字符` +ambiguous_character=`%[1]c [U+%04[1]X] 容易和 %[2]c [U+%04[2]X] 混淆` escape_control_characters=Escape unescape_control_characters=Unescape @@ -1429,6 +1467,7 @@ issues.new.clear_milestone=取消选中里程碑 issues.new.assignees=指派成员 issues.new.clear_assignees=取消指派成员 issues.new.no_assignees=未指派成员 +issues.new.no_reviewers=无审核者 issues.new.blocked_user=无法创建工单,因为您已被仓库所有者屏蔽。 issues.edit.already_changed=无法保存对工单的更改。其内容似乎已被其他用户更改。 请刷新页面并重新编辑以避免覆盖他们的更改 issues.edit.blocked_user=无法编辑内容,因为您已被仓库所有者或工单创建者屏蔽。 @@ -1457,6 +1496,7 @@ issues.remove_labels=于 %[2]s 删除了标签 %[1]s issues.add_remove_labels=于 %[3]s 添加了标签 %[1]s ,删除了标签 %[2]s issues.add_milestone_at=`于 %[2]s 添加了里程碑 %[1]s` issues.add_project_at=`于 %[2]s 将此添加到 %[1]s 项目` +issues.move_to_column_of_project=`将此对象移至 %s 的 %s 中在 %s 上` issues.change_milestone_at=`%[3]s 修改了里程碑从 %[1]s%[2]s` issues.change_project_at=于 %[3]s 将此从项目 %[1]s 移到 %[2]s issues.remove_milestone_at=`%[2]s 删除了里程碑 %[1]s` @@ -1488,6 +1528,8 @@ issues.filter_assignee=指派人筛选 issues.filter_assginee_no_select=所有指派成员 issues.filter_assginee_no_assignee=未指派 issues.filter_poster=作者 +issues.filter_user_placeholder=搜索用户 +issues.filter_user_no_select=所有用户 issues.filter_type=类型筛选 issues.filter_type.all_issues=所有工单 issues.filter_type.assigned_to_you=指派给您的 @@ -1541,7 +1583,9 @@ issues.no_content=没有提供说明。 issues.close=关闭工单 issues.comment_pull_merged_at=已合并提交 %[1]s 到 %[2]s %[3]s issues.comment_manually_pull_merged_at=手动合并提交 %[1]s 到 %[2]s %[3]s +issues.close_comment_issue=评论并关闭 issues.reopen_issue=重新开启 +issues.reopen_comment_issue=评论并重新开启 issues.create_comment=评论 issues.comment.blocked_user=无法创建或编辑评论,因为您已被仓库所有者或工单创建者屏蔽。 issues.closed_at=`于 %[2]s 关闭此工单` @@ -1630,12 +1674,25 @@ issues.delete.title=是否删除工单? issues.delete.text=您真的要删除这个工单吗?(该操作将会永久删除所有内容。如果您需要保留,请关闭它) issues.tracker=时间跟踪 +issues.timetracker_timer_start=启动计时器 +issues.timetracker_timer_stop=停止计时器 +issues.timetracker_timer_discard=删除计时器 +issues.timetracker_timer_manually_add=添加时间 +issues.time_estimate_set=设置预计时间 +issues.time_estimate_display=预计: %s +issues.change_time_estimate_at=将预计时间修改为 %s %s +issues.remove_time_estimate_at=删除预计时间 %s +issues.time_estimate_invalid=预计时间格式无效 +issues.start_tracking_history=`开始工作 %s` issues.tracker_auto_close=当此工单关闭时,自动停止计时器 issues.tracking_already_started=`你已经开始对 另一个工单 进行时间跟踪!` +issues.stop_tracking_history=`停止工作 %s` issues.cancel_tracking_history=`取消时间跟踪 %s` issues.del_time=删除此时间跟踪日志 +issues.add_time_history=`添加计时 %s` issues.del_time_history=`已删除时间 %s` +issues.add_time_manually=手动添加时间 issues.add_time_hours=小时 issues.add_time_minutes=分钟 issues.add_time_sum_to_small=没有输入时间。 @@ -1695,6 +1752,7 @@ issues.dependency.add_error_dep_not_same_repo=这两个工单必须在同一仓 issues.review.self.approval=您不能批准您自己的合并请求。 issues.review.self.rejection=您不能请求对您自己的合并请求进行更改。 issues.review.approve=于 %s 批准此合并请求 +issues.review.comment=评审于 %s issues.review.dismissed=于 %[2]s 取消了 %[1]s 的评审 issues.review.dismissed_label=已取消 issues.review.left_comment=留下了一条评论 @@ -1720,6 +1778,10 @@ issues.review.resolve_conversation=已解决问题 issues.review.un_resolve_conversation=未解决问题 issues.review.resolved_by=标记问题为已解决 issues.review.commented=评论 +issues.review.official=已批准 +issues.review.requested=等待审核 +issues.review.rejected=请求变更 +issues.review.stale=批准后已更新 issues.review.unofficial=非官方审批数 issues.assignee.error=因为未知原因,并非所有的指派都成功。 issues.reference_issue.body=内容 @@ -1837,7 +1899,9 @@ pulls.unrelated_histories=合并失败:两个分支没有共同历史。提示 pulls.merge_out_of_date=合并失败:在生成合并时,主分支已更新。提示:再试一次。 pulls.head_out_of_date=合并失败:在生成合并时,head 已更新。提示:再试一次。 pulls.has_merged=失败:合并请求已经被合并,您不能再次合并或更改目标分支。 +pulls.push_rejected=推送失败:推送被拒绝。审查此仓库的 Git 钩子。 pulls.push_rejected_summary=详细拒绝信息 +pulls.push_rejected_no_message=推送失败:此推送被拒绝但未提供其他信息。请检查此仓库的 Git 钩子。 pulls.open_unmerged_pull_exists=`您不能执行重新打开操作, 因为已经存在相同的合并请求 (#%d)。` pulls.status_checking=一些检测仍在等待运行 pulls.status_checks_success=所有检测均成功 @@ -1881,6 +1945,10 @@ pulls.delete.title=删除此合并请求? pulls.delete.text=你真的要删除这个合并请求吗? (这将永久删除所有内容。如果你打算将内容存档,请考虑关闭它) pulls.recently_pushed_new_branches=您已经于%[2]s推送了分支 %[1]s +pulls.upstream_diverging_prompt_behind_1=该分支落后于 %s %d 个提交 +pulls.upstream_diverging_prompt_behind_n=该分支落后于 %s %d 个提交 +pulls.upstream_diverging_prompt_base_newer=基础分支 %s 有新的更改 +pulls.upstream_diverging_merge=同步派生 pull.deleted_branch=(已删除): %s pull.agit_documentation=查看有关 AGit 的文档 @@ -1894,6 +1962,7 @@ milestones.no_due_date=暂无截止日期 milestones.open=开启 milestones.close=关闭 milestones.new_subheader=里程碑可以帮助您组织工单并跟踪其进度。 +milestones.completeness=%d%% 已完成 milestones.create=创建里程碑 milestones.title=标题 milestones.desc=描述 @@ -2116,6 +2185,7 @@ settings.pulls.default_delete_branch_after_merge=默认合并后删除合并请 settings.pulls.default_allow_edits_from_maintainers=默认开启允许维护者编辑 settings.releases_desc=启用发布 settings.packages_desc=启用仓库软件包注册中心 +settings.projects_desc=启用项目 settings.projects_mode_desc=项目模式 (要显示的项目类型) settings.projects_mode_repo=仅仓库项目 settings.projects_mode_owner=仅限用户或组织项目 @@ -2155,6 +2225,7 @@ settings.transfer_in_progress=当前正在进行转让。 如果你想将此代 settings.transfer_notices_1=- 如果将此仓库转移给其他用户, 您将失去对此仓库的访问权限。 settings.transfer_notices_2=-如果将其转移到您 (共同) 拥有的组织,您可以继续访问该仓库。 settings.transfer_notices_3=- 如果仓库是私有的并且被转移给某个用户,那么此操作可以确保该用户至少具有读权限(以及必要时的更改权限)。 +settings.transfer_notices_4=- 如果存储库属于某个组织,而您将其转移给另一个组织或个人,那么您将失去存储库工单与其组织项目系统之间的链接。 settings.transfer_owner=新拥有者 settings.transfer_perform=执行转让 settings.transfer_started=该代码库已被标记为转让并等待来自 %s 的确认 @@ -2291,6 +2362,7 @@ settings.event_pull_request_merge=合并请求合并 settings.event_package=软件包 settings.event_package_desc=软件包已在仓库中被创建或删除。 settings.branch_filter=分支过滤 +settings.branch_filter_desc=推送、创建,删除分支事件的分支白名单,使用 glob 模式匹配指定。若为空或 *,则将报告所有分支的事件。语法文档见 %[2]s。示例:master,{master,release*}。 settings.authorization_header=授权标头 settings.authorization_header_desc=当存在时将被作为授权标头包含在内。例如: %s。 settings.active=激活 @@ -2336,25 +2408,53 @@ settings.deploy_key_deletion=删除部署密钥 settings.deploy_key_deletion_desc=删除部署密钥将取消此密钥对此仓库的访问权限。继续? settings.deploy_key_deletion_success=部署密钥已删除。 settings.branches=分支 +settings.protected_branch=分支保护 settings.protected_branch.save_rule=保存规则 settings.protected_branch.delete_rule=删除规则 +settings.protected_branch_can_push=是否允许推送? settings.protected_branch_can_push_yes=你可以推 +settings.protected_branch_can_push_no=你不能推送 +settings.branch_protection=分支 '%s' 的保护规则 settings.protect_this_branch=启用分支保护 settings.protect_this_branch_desc=阻止删除并限制Git推送和合并到分支。 settings.protect_disable_push=禁用推送 settings.protect_disable_push_desc=此分支不允许推送。 +settings.protect_disable_force_push=禁用强制推送 +settings.protect_disable_force_push_desc=此分支禁止强制推送。 settings.protect_enable_push=启用推送 settings.protect_enable_push_desc=任何拥有写访问权限的人将被允许推送到此分支(但不能强行推送)。 +settings.protect_enable_force_push_all=启用强制推送 +settings.protect_enable_force_push_all_desc=任何拥有推送权限的人都可以对这个分支进行强制推送。 +settings.protect_enable_force_push_allowlist=强制推送白名单 +settings.protect_enable_force_push_allowlist_desc=只有白名单中的用户或团队才能强制推送到这个分支。 settings.protect_enable_merge=启用合并 settings.protect_enable_merge_desc=任何具有写入权限的人都可以将合并请求合并到此分支中。 +settings.protect_whitelist_committers=受白名单限制的推送 +settings.protect_whitelist_committers_desc=只有列入白名单的用户或团队才能被允许推送到此分支(但不能强行推送)。 +settings.protect_whitelist_deploy_keys=具有推送权限的部署密钥白名单。 +settings.protect_whitelist_users=推送白名单用户: +settings.protect_whitelist_teams=推送白名单团队: +settings.protect_force_push_allowlist_users=允许强制推送的白名单用户: +settings.protect_force_push_allowlist_teams=允许强制推送的白名单团队: +settings.protect_force_push_allowlist_deploy_keys=允许白名单中的部署密钥进行强制推送。 +settings.protect_merge_whitelist_committers=启用合并白名单 +settings.protect_merge_whitelist_committers_desc=仅允许白名单用户或团队合并合并请求到此分支。 +settings.protect_merge_whitelist_users=合并白名单用户: +settings.protect_merge_whitelist_teams=合并白名单团队: settings.protect_check_status_contexts=启用状态检查 settings.protect_status_check_patterns=状态检查模式: settings.protect_status_check_patterns_desc=输入模式,指定哪些状态检查必须通过,才能将分支合并到符合此规则的分支中去。每一行指定一个模式,模式不能为空。 +settings.protect_check_status_contexts_desc=要求状态检查通过才能合并。如果启用,提交必须先推送到另一个分支,然后再合并或推送到匹配这些保护规则的分支。如果没有选择具体的状态检查上下文,则所有的状态检查都通过才能合并。 settings.protect_check_status_contexts_list=此仓库上周进行过的状态检查 settings.protect_status_check_matched=匹配 settings.protect_invalid_status_check_pattern=无效的状态检查规则:“%s”。 settings.protect_no_valid_status_check_patterns=没有有效的状态检查规则。 settings.protect_required_approvals=所需的批准: +settings.protect_required_approvals_desc=只允许合并有足够审核的合并请求。要求的审核必须来自白名单或者有权限的用户或团队。 +settings.protect_approvals_whitelist_enabled=仅列入白名单的用户或团队才可批准 +settings.protect_approvals_whitelist_enabled_desc=只有白名单用户或团队的审核才能计数。 如果规则没有批准白名单,来自任何有写访问权限的人的审核都将计数。 +settings.protect_approvals_whitelist_users=审查者白名单: +settings.protect_approvals_whitelist_teams=审查团队白名单: settings.dismiss_stale_approvals=取消过时的批准 settings.dismiss_stale_approvals_desc=当新的提交更改合并请求内容被推送到分支时,旧的批准将被撤销。 settings.ignore_stale_approvals=忽略过期批准 @@ -2362,12 +2462,18 @@ settings.ignore_stale_approvals_desc=对旧提交(过期审核)的批准将 settings.require_signed_commits=需要签名提交 settings.require_signed_commits_desc=拒绝推送未签名或无法验证的提交到分支 settings.protect_branch_name_pattern=受保护的分支名称模式 +settings.protect_branch_name_pattern_desc=分支保护的名称匹配规则。语法请参阅 文档 。如:main, release/** settings.protect_patterns=规则 settings.protect_protected_file_patterns=受保护的文件模式(使用分号 ';' 分隔): +settings.protect_protected_file_patterns_desc=即使用户有权添加、编辑或删除此分支中的文件,也不允许直接更改受保护的文件。 可以使用分号 (';') 分隔多个模式。 见%[2]s文档了解模式语法。例如: .drone.yml, /docs/**/*.txt settings.protect_unprotected_file_patterns=不受保护的文件模式(使用分号 ';' 分隔): +settings.protect_unprotected_file_patterns_desc=如果用户有写权限,则允许直接更改的不受保护的文件,以绕过推送限制。可以使用分号分隔多个模式 (';')。 见 %[2]s 文档了解模式语法。例如: .drone.yml, /docs/**/*.txt +settings.add_protected_branch=启用保护 +settings.delete_protected_branch=禁用保护 settings.update_protect_branch_success=分支保护规则 %s 更新成功。 settings.remove_protected_branch_success=移除分支保护规则"%s"成功。 settings.remove_protected_branch_failed=移除分支保护规则"%s"失败。 +settings.protected_branch_deletion=删除分支保护 settings.protected_branch_deletion_desc=禁用分支保护允许具有写入权限的用户推送提交到此分支。继续? settings.block_rejected_reviews=拒绝审核阻止了此合并 settings.block_rejected_reviews_desc=如果官方审查人员要求作出改动,即使有足够的批准,合并也不允许。 @@ -2375,8 +2481,11 @@ settings.block_on_official_review_requests=有官方审核阻止了代码合并 settings.block_on_official_review_requests_desc=处于评审状态时,即使有足够的批准,也不能合并。 settings.block_outdated_branch=如果合并请求已经过时,阻止合并 settings.block_outdated_branch_desc=当头部分支落后基础分支时,不能合并。 +settings.block_admin_merge_override=管理员须遵守分支保护规则 +settings.block_admin_merge_override_desc=管理员须遵守分支保护规则,不能规避该规则。 settings.default_branch_desc=请选择一个默认的分支用于合并请求和提交: settings.merge_style_desc=合并方式 +settings.default_merge_style_desc=默认合并风格 settings.choose_branch=选择一个分支... settings.no_protected_branch=没有受保护的分支 settings.edit_protected_branch=编辑 @@ -2392,12 +2501,25 @@ settings.tags.protection.allowed.teams=允许的团队 settings.tags.protection.allowed.noone=无 settings.tags.protection.create=保护Git标签 settings.tags.protection.none=没有受保护的Git标签 +settings.tags.protection.pattern.description=你可以使用单个名称或 glob 模式匹配或正则表达式来匹配多个标签。了解详情请访问 保护Git标签指南。 settings.bot_token=Bot 令牌 settings.chat_id=聊天 ID settings.thread_id=线程 ID settings.matrix.homeserver_url=主服务器网址 settings.matrix.room_id=房间ID settings.matrix.message_type=消息类型 +settings.visibility.private.button=设为私有 +settings.visibility.private.text=将可见性更改为私有不仅会使仓库仅对允许的成员可见,而且可能会消除它与派生仓库、关注者和点赞之间的关系。 +settings.visibility.private.bullet_title=将可见性改为私有将会: +settings.visibility.private.bullet_one=使仓库只对允许的成员可见。 +settings.visibility.private.bullet_two=可能会删除它与 派生仓库关注者点赞 之间的关系。 +settings.visibility.public.button=设为公开 +settings.visibility.public.text=将可见性更改为公开会使任何人都可见。 +settings.visibility.public.bullet_title=将可见性改为公开将会: +settings.visibility.public.bullet_one=使仓库让任何人都可见。 +settings.visibility.success=仓库可见性已更改。 +settings.visibility.error=试图更改仓库可见性时出错。 +settings.visibility.fork_error=无法更改派生仓库的可见性。 settings.archive.button=归档仓库 settings.archive.header=归档此仓库 settings.archive.text=归档仓库将使其完全只读。它将在首页隐藏。没有人(甚至你!)能够进行新的提交,或打开工单及合并请求。 @@ -2509,6 +2631,7 @@ release.new_release=发布新版 release.draft=草稿 release.prerelease=预发行 release.stable=稳定 +release.latest=最新版本 release.compare=比较 release.edit=编辑 release.ahead.commits=%d 次提交 @@ -2593,6 +2716,7 @@ tag.create_success=标签"%s"已存在 topic.manage_topics=管理主题 topic.done=保存 +topic.count_prompt=您最多选择25个主题 topic.format_prompt=主题必须以字母或数字开头,可以包含连字符 ('-') 和句点 ('.'),长度不得超过35个字符。字符必须为小写。 find_file.go_to_file=转到文件 @@ -2665,6 +2789,7 @@ settings.delete_prompt=删除操作会永久清除该组织的信息,并且 所有仓库。 settings.labels_desc=添加能够被该组织下的 所有仓库 的工单使用的标签。 @@ -2689,6 +2814,7 @@ teams.leave.detail=离开 %s? teams.can_create_org_repo=创建仓库 teams.can_create_org_repo_helper=成员可以在组织中创建仓库。创建者将自动获得创建的仓库的管理员权限。 teams.none_access=无访问权限 +teams.none_access_helper=成员无法查看此单元或对其执行任何其他操作。对公开仓库无此影响。 teams.general_access=常规访问 teams.general_access_helper=成员权限将由以下权限表决定。 teams.read_access=可读 @@ -2757,6 +2883,7 @@ last_page=末页 total=总计:%d settings=管理设置 +dashboard.new_version_hint=Gitea %s 现已可用,您正在运行 %s。查看 博客 了解详情。 dashboard.statistic=摘要 dashboard.maintenance_operations=运维 dashboard.system_status=系统状态 @@ -2799,6 +2926,7 @@ dashboard.reinit_missing_repos=重新初始化所有丢失的 Git 仓库存在 dashboard.sync_external_users=同步外部用户数据 dashboard.cleanup_hook_task_table=清理 hook_task 表 dashboard.cleanup_packages=清理过期的软件包 +dashboard.cleanup_actions=清理过期的 Actions 资源 dashboard.server_uptime=服务运行时间 dashboard.current_goroutine=当前 Goroutines 数量 dashboard.current_memory_usage=当前内存使用量 @@ -2828,12 +2956,19 @@ dashboard.total_gc_time=GC 暂停时间总量 dashboard.total_gc_pause=GC 暂停时间总量 dashboard.last_gc_pause=上次 GC 暂停时间 dashboard.gc_times=GC 执行次数 +dashboard.delete_old_actions=从数据库中删除所有旧操作记录 +dashboard.delete_old_actions.started=已开始从数据库中删除所有旧操作记录。 dashboard.update_checker=更新检查器 dashboard.delete_old_system_notices=从数据库中删除所有旧系统通知 dashboard.gc_lfs=垃圾回收 LFS 元数据 +dashboard.stop_zombie_tasks=停止僵尸任务 +dashboard.stop_endless_tasks=停止无法停止的任务 +dashboard.cancel_abandoned_jobs=取消丢弃的任务 +dashboard.start_schedule_tasks=开始Actions调度任务 dashboard.sync_branch.started=分支同步已开始 dashboard.sync_tag.started=标签同步已开始 dashboard.rebuild_issue_indexer=重建工单索引 +dashboard.sync_repo_licenses=重新仓库许可证探测 users.user_manage_panel=用户帐户管理 users.new_account=创建新帐户 @@ -2905,6 +3040,10 @@ emails.not_updated=无法更新请求的电子邮件地址: %v emails.duplicate_active=此电子邮件地址已被另一个用户激活使用。 emails.change_email_header=更新电子邮件属性 emails.change_email_text=您确定要更新该电子邮件地址吗? +emails.delete=删除电子邮件 +emails.delete_desc=您确定要删除该电子邮件地址? +emails.deletion_success=电子邮件地址已被删除。 +emails.delete_primary_email_error=您不能删除主电子邮件。 orgs.org_manage_panel=组织管理 orgs.name=名称 @@ -2937,10 +3076,12 @@ packages.size=大小 packages.published=已发布 defaulthooks=默认Web钩子 +defaulthooks.desc=当某些 Gitea 事件触发时,Web 钩子自动向服务器发出 HTTP POST 请求。这里定义的 Web 钩子是默认配置,将被复制到所有新的仓库中。详情请访问 Web 钩子指南。 defaulthooks.add_webhook=添加默认Web 钩子 defaulthooks.update_webhook=更新默认 Web 钩子 systemhooks=系统 Web 钩子 +systemhooks.desc=当某些 Gitea 事件触发时,Web 钩子自动向服务器发出HTTP POST请求。这里定义的 Web 钩子将作用于系统上的所有仓库,所以请考虑这可能带来的任何性能影响。了解详情请访问 Web 钩子指南。 systemhooks.add_webhook=添加系统 Web 钩子 systemhooks.update_webhook=更新系统 Web 钩子 @@ -3035,8 +3176,18 @@ auths.tips=帮助提示 auths.tips.oauth2.general=OAuth2 认证 auths.tips.oauth2.general.tip=当注册新的 OAuth2 身份验证时,回调/重定向 URL 应该是: auths.tip.oauth2_provider=OAuth2 提供程序 +auths.tip.bitbucket=在 %s 注册新的 OAuth 使用者同时添加权限“账号”-“读取” auths.tip.nextcloud=使用下面的菜单“设置(Settings) -> 安全(Security) -> OAuth 2.0 client”在您的实例上注册一个新的 OAuth 客户端。 +auths.tip.dropbox=在 %s 上创建一个新的应用程序 +auths.tip.facebook=`在 %s 注册一个新的应用,并添加产品"Facebook 登录"` +auths.tip.github=在 %s 注册一个 OAuth 应用程序 +auths.tip.gitlab_new=在 %s 注册一个新的应用 +auths.tip.google_plus=从谷歌 API 控制台 %s 获得 OAuth2 客户端凭据 auths.tip.openid_connect=使用 OpenID 连接发现 URL ({server}/.well-known/openid-configuration) 来指定终点 +auths.tip.twitter=访问 %s,创建应用并确保启用了"允许此应用程序用于登录 Twitter"的选项。 +auths.tip.discord=在 %s 上注册新应用程序 +auths.tip.gitea=注册一个新的 OAuth2 应用程序。可以访问 %s 查看帮助 +auths.tip.yandex=在 %s 上创建一个新的应用程序。在“ Yandex.Passport API”这部分中选择以下权限:“访问电子邮件地址(Access to email address)”,“访问用户头像(Access to user avatar)”和“访问用户名,名字和姓氏,性别(Access to username, first name and surname, genderAccess to username, first name and surname, gender)” auths.tip.mastodon=输入您想要认证的 mastodon 实例的自定义 URL (或使用默认值) auths.edit=修改认证源 auths.activated=该认证源已经启用 @@ -3153,6 +3304,9 @@ config.cache_interval=Cache 周期 config.cache_conn=Cache 连接字符串 config.cache_item_ttl=缓存项目 TTL config.cache_test=测试缓存 +config.cache_test_failed=缓存测试失败: %v。 +config.cache_test_slow=缓存测试成功,但响应缓慢: %s。 +config.cache_test_succeeded=缓存测试成功,在 %s 时间内得到响应。 config.session_config=Session 配置 config.session_provider=Session 提供者 @@ -3199,6 +3353,7 @@ monitor.next=下次执行时间 monitor.previous=上次执行时间 monitor.execute_times=执行次数 monitor.process=运行中进程 +monitor.stacktrace=调用栈踪迹 monitor.processes_count=%d 个进程 monitor.download_diagnosis_report=下载诊断报告 monitor.desc=进程描述 @@ -3206,6 +3361,8 @@ monitor.start=开始时间 monitor.execute_time=执行时长 monitor.last_execution_result=结果 monitor.process.cancel=中止进程 +monitor.process.cancel_desc=中止一个进程可能导致数据丢失 +monitor.process.cancel_notices=中止:%s ? monitor.process.children=子进程 monitor.queues=队列 @@ -3307,6 +3464,8 @@ raw_minutes=分钟 [dropzone] default_message=拖动文件或者点击此处上传。 +invalid_input_type=您不能上传该类型的文件 +file_too_big=文件体积({{filesize}} MB)超过了最大允许体积({{maxFilesize}} MB) remove_file=移除文件 [notification] @@ -3378,6 +3537,8 @@ alpine.repository=仓库信息 alpine.repository.branches=分支 alpine.repository.repositories=仓库管理 alpine.repository.architectures=架构 +arch.registry=添加具有相关仓库和架构的服务器到 /etc/pacman.conf 中: +arch.install=使用 pacman 同步软件包: arch.repository=仓库信息 arch.repository.repositories=仓库管理 arch.repository.architectures=架构 @@ -3456,6 +3617,7 @@ settings.link=将此软件包链接到仓库 settings.link.description=如果您将一个软件包与一个代码库链接起来,软件包将显示在代码库的软件包列表中。 settings.link.select=选择仓库 settings.link.button=更新仓库链接 +settings.link.success=仓库链接已成功更新。 settings.link.error=更新仓库链接失败。 settings.delete=删除软件包 settings.delete.description=删除软件包是永久性的,无法撤消。 @@ -3579,12 +3741,18 @@ runs.no_workflows.quick_start=不知道如何使用 Gitea Actions吗?请查看 runs.no_workflows.documentation=关于Gitea Actions的更多信息,请参阅 文档。 runs.no_runs=工作流尚未运行过。 runs.empty_commit_message=(空白的提交消息) +runs.expire_log_message=旧的日志已被清除 workflow.disable=禁用工作流 workflow.disable_success=工作流 '%s' 已成功禁用。 workflow.enable=启用工作流 workflow.enable_success=工作流 '%s' 已成功启用。 workflow.disabled=工作流已禁用。 +workflow.run=运行工作流 +workflow.not_found=工作流 %s 未找到。 +workflow.run_success=工作流 %s 已成功运行。 +workflow.from_ref=使用工作流从 +workflow.has_workflow_dispatch=此 Workflow 有一个 Workflow_dispatch 事件触发器。 need_approval_desc=该工作流由派生仓库的合并请求所触发,需要批准方可运行。 @@ -3605,6 +3773,7 @@ variables.update.failed=编辑变量失败。 variables.update.success=该变量已被编辑。 [projects] +deleted.display_name=已删除项目 type-1.display_name=个人项目 type-2.display_name=仓库项目 type-3.display_name=组织项目 From 2d7e6e9482da19aae4660823f38c2153cf942bb8 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 17 Dec 2024 09:15:18 +0800 Subject: [PATCH 02/50] Fix various trivial problems (#32861) 1. add/improve comments to help future readers could understand the problem more easily. 2. add an error log to LDAP with username fallback 3. use `or` instead of `Iif` for "repo/branch_dropdown" (`Iif` was a mistake, but it doesn't really affect the UI) 4. add `tw-font-mono` style to container digest to match dockerhub 5. fix a bug in RepoBranchTagSelector: the form is not updated when there is no click to an item --------- Co-authored-by: delvh --- models/actions/run_job_status_test.go | 2 +- modules/markup/markdown/markdown.go | 3 ++- modules/repository/fork.go | 3 +++ routers/web/repo/actions/view.go | 3 +++ services/auth/source/ldap/source_authenticate.go | 6 ++++++ templates/package/content/container.tmpl | 3 ++- templates/repo/commit_page.tmpl | 4 +++- templates/repo/issue/filter_item_label.tmpl | 3 +++ templates/user/auth/signin_inner.tmpl | 1 + templates/user/auth/signup_inner.tmpl | 2 ++ templates/user/dashboard/issues.tmpl | 2 ++ web_src/js/components/RepoBranchTagSelector.vue | 10 ++++++++++ 12 files changed, 38 insertions(+), 4 deletions(-) diff --git a/models/actions/run_job_status_test.go b/models/actions/run_job_status_test.go index 04fd9ceba7..523d38327e 100644 --- a/models/actions/run_job_status_test.go +++ b/models/actions/run_job_status_test.go @@ -69,7 +69,7 @@ func TestAggregateJobStatus(t *testing.T) { {[]Status{StatusFailure, StatusBlocked}, StatusFailure}, // skipped with other status - // TODO: need to clarify whether a PR with "skipped" job status is considered as "mergeable" or not. + // "all skipped" is also considered as "mergeable" by "services/actions.toCommitStatus", the same as GitHub {[]Status{StatusSkipped}, StatusSkipped}, {[]Status{StatusSkipped, StatusSuccess}, StatusSuccess}, {[]Status{StatusSkipped, StatusFailure}, StatusFailure}, diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index a14c0cad59..b5fffccdb9 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -129,7 +129,8 @@ func SpecializedMarkdown(ctx *markup.RenderContext) *GlodmarkRender { Enabled: setting.Markdown.EnableMath, ParseDollarInline: true, ParseDollarBlock: true, - ParseSquareBlock: true, // TODO: this is a bad syntax, it should be deprecated in the future (by some config options) + ParseSquareBlock: true, // TODO: this is a bad syntax "\[ ... \]", it conflicts with normal markdown escaping, it should be deprecated in the future (by some config options) + // ParseBracketInline: true, // TODO: this is also a bad syntax "\( ... \)", it also conflicts, it should be deprecated in the future }), meta.Meta, ), diff --git a/modules/repository/fork.go b/modules/repository/fork.go index d530634071..84ed4b23d6 100644 --- a/modules/repository/fork.go +++ b/modules/repository/fork.go @@ -12,6 +12,9 @@ import ( "code.gitea.io/gitea/modules/setting" ) +// CanUserForkBetweenOwners returns true if user can fork between owners. +// By default, a user can fork a repository from another owner, but not from themselves. +// Many users really like to fork their own repositories, so add an experimental setting to allow this. func CanUserForkBetweenOwners(id1, id2 int64) bool { if id1 != id2 { return true diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index b711038da0..ba17fa427d 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -205,8 +205,11 @@ func ViewPost(ctx *context_module.Context) { } } + // TODO: "ComposeMetas" (usually for comment) is not quite right, but it is still the same as what template "RenderCommitMessage" does. + // need to be refactored together in the future metas := ctx.Repo.Repository.ComposeMetas(ctx) + // the title for the "run" is from the commit message resp.State.Run.Title = run.Title resp.State.Run.TitleHTML = templates.NewRenderUtils(ctx).RenderCommitMessage(run.Title, metas) resp.State.Run.Link = run.Link() diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go index 020e5784dc..6a6c60cd40 100644 --- a/services/auth/source/ldap/source_authenticate.go +++ b/services/auth/source/ldap/source_authenticate.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" auth_module "code.gitea.io/gitea/modules/auth" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" asymkey_service "code.gitea.io/gitea/services/asymkey" source_service "code.gitea.io/gitea/services/auth/source" @@ -31,7 +32,12 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u return nil, user_model.ErrUserNotExist{Name: loginName} } // Fallback. + // FIXME: this fallback would cause problems when the "Username" attribute is not set and a user inputs their email. + // In this case, the email would be used as the username, and will cause the "CreateUser" failure for the first login. if sr.Username == "" { + if strings.Contains(userName, "@") { + log.Error("No username in search result (Username Attribute is not set properly?), using email as username might cause problems") + } sr.Username = userName } if sr.Mail == "" { diff --git a/templates/package/content/container.tmpl b/templates/package/content/container.tmpl index 207774bfef..04732d276a 100644 --- a/templates/package/content/container.tmpl +++ b/templates/package/content/container.tmpl @@ -36,9 +36,10 @@ {{range .PackageDescriptor.Metadata.Manifests}} + {{/* "unknown/unknown" is attestation-manifest, so we should skip it */}} {{if ne .Platform "unknown/unknown"}} - {{StringUtils.TrimPrefix .Digest "sha256:" | ShortSha}} + {{StringUtils.TrimPrefix .Digest "sha256:" | ShortSha}} {{.Platform}} {{FileSize .Size}} diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl index 8792c4e1b2..71f77154fb 100644 --- a/templates/repo/commit_page.tmpl +++ b/templates/repo/commit_page.tmpl @@ -68,11 +68,13 @@

+ {{/*FIXME: CurrentRefShortName seems not making sense here (old code), + because the "commit page" has no "$.BranchName" info, so only using DefaultBranch should be enough */}} {{template "repo/branch_dropdown" dict "Repository" .Repository "ShowTabBranches" true "CurrentRefType" "branch" - "CurrentRefShortName" (Iif $.BranchName $.Repository.DefaultBranch) + "CurrentRefShortName" (or $.BranchName $.Repository.DefaultBranch) "RefFormActionTemplate" (print "{RepoLink}/_cherrypick/" .CommitID "/{RefShortName}") }}
diff --git a/templates/repo/issue/filter_item_label.tmpl b/templates/repo/issue/filter_item_label.tmpl index 927328ba14..88e2e43120 100644 --- a/templates/repo/issue/filter_item_label.tmpl +++ b/templates/repo/issue/filter_item_label.tmpl @@ -23,6 +23,9 @@
{{ctx.Locale.Tr "repo.issues.filter_label_no_select"}} {{ctx.Locale.Tr "repo.issues.filter_label_select_no_label"}} + {{/* The logic here is not the same as the label selector in the issue sidebar. + The one in the issue sidebar renders "repo labels | divider | org labels". + Maybe the logic should be updated to be consistent.*/}} {{$previousExclusiveScope := "_no_scope"}} {{range .Labels}} {{$exclusiveScope := .ExclusiveScope}} diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl index 9daa051e06..dd608e5aa1 100644 --- a/templates/user/auth/signin_inner.tmpl +++ b/templates/user/auth/signin_inner.tmpl @@ -48,6 +48,7 @@
{{end}}{{/*if .EnablePasswordSignInForm*/}} + {{/* "oauth_container" contains not only "oauth2" methods, but also "OIDC" and "SSPI" methods */}} {{$showOAuth2Methods := or .OAuth2Providers .EnableOpenIDSignIn .EnableSSPI}} {{if and $showOAuth2Methods .EnablePasswordSignInForm}}
{{ctx.Locale.Tr "sign_in_or"}}
diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl index ea8d0bafe4..b3b2a4205e 100644 --- a/templates/user/auth/signup_inner.tmpl +++ b/templates/user/auth/signup_inner.tmpl @@ -47,6 +47,8 @@ {{end}} + {{/* "oauth_container" contains not only "oauth2" methods, but also "OIDC" and "SSPI" methods */}} + {{/* TODO: it seems that "EnableSSPI" is only set in "sign-in" handlers, but it should use the same logic to control its display */}} {{$showOAuth2Methods := or .OAuth2Providers .EnableOpenIDSignIn .EnableSSPI}} {{if $showOAuth2Methods}}
{{ctx.Locale.Tr "sign_in_or"}}
diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl index 7f960a4709..7924dd2305 100644 --- a/templates/user/dashboard/issues.tmpl +++ b/templates/user/dashboard/issues.tmpl @@ -61,6 +61,7 @@ {{template "repo/issue/filter_item_label" dict "Labels" .Labels "QueryLink" $queryLinkWithFilter "SupportArchivedLabel" true}} {{end}} + {{/* at the moment there is no easy way to get poster candidates on this page, so only show a username input, search for what the end user enters */}} {{if ne $.ViewType "created_by"}} {{template "repo/issue/filter_item_user_fetch" dict "QueryParamKey" "poster" @@ -70,6 +71,7 @@ }} {{end}} + {{/* at the moment there is no easy way to get assignee candidates on this page, so only show a username input, search for what the end user enters */}} {{if ne $.ViewType "assigned"}} {{template "repo/issue/filter_item_user_fetch" dict "QueryParamKey" "assignee" diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue index 2f66336a66..4b7ca1429d 100644 --- a/web_src/js/components/RepoBranchTagSelector.vue +++ b/web_src/js/components/RepoBranchTagSelector.vue @@ -113,6 +113,16 @@ const sfc = { if (this.menuVisible) this.menuVisible = false; }); }, + + mounted() { + if (this.refFormActionTemplate) { + // if the selector is used in a form and needs to change the form action, + // make a mock item and select it to update the form action + const item: ListItem = {selected: true, refType: this.currentRefType, refShortName: this.currentRefShortName, rssFeedLink: ''}; + this.selectItem(item); + } + }, + methods: { selectItem(item: ListItem) { this.menuVisible = false; From e6186bc447e174bcbdb697d2b99c3767b8f3a321 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Wed, 18 Dec 2024 00:33:18 +0000 Subject: [PATCH 03/50] [skip ci] Updated translations via Crowdin --- options/locale/locale_id-ID.ini | 48 +++ options/locale/locale_zh-TW.ini | 680 +++++++++++++++++++++++++++++++- 2 files changed, 710 insertions(+), 18 deletions(-) diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index 237323a0fc..00a406aae4 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -22,11 +22,25 @@ toc=Daftar Isi username=Nama Pengguna email=Alamat Email password=Kata Sandi +re_type=Konfirmasi Kata Sandi captcha=CAPTCHA twofa=Otentikasi Dua Faktor twofa_scratch=Kode Awal Dua Faktor passcode=Kode Akses +webauthn_insert_key=Masukkan kunci keamanan anda +webauthn_sign_in=Tekan tombol pada kunci keamanan Anda. Jika kunci keamanan Anda tidak memiliki tombol, masukkan kembali. +webauthn_press_button=Silakan tekan tombol pada kunci keamanan Anda… +webauthn_use_twofa=Gunakan kode dua faktor dari telepon Anda +webauthn_error=Tidak dapat membaca kunci keamanan Anda. +webauthn_unsupported_browser=Browser Anda saat ini tidak mendukung WebAuthn. +webauthn_error_unknown=Terdapat kesalahan yang tidak diketahui. Mohon coba lagi. +webauthn_error_insecure=`WebAuthn hanya mendukung koneksi aman. Untuk pengujian melalui HTTP, Anda dapat menggunakan "localhost" atau "127.0.0.1"` +webauthn_error_unable_to_process=Server tidak dapat memproses permintaan Anda. +webauthn_error_duplicated=Kunci keamanan tidak diperbolehkan untuk permintaan ini. Pastikan bahwa kunci ini belum terdaftar sebelumnya. +webauthn_error_empty=Anda harus menetapkan nama untuk kunci ini. +webauthn_error_timeout=Waktu habis sebelum kunci Anda dapat dibaca. Mohon muat ulang halaman ini dan coba lagi. +webauthn_reload=Muat ulang repository=Repositori organization=Organisasi @@ -36,6 +50,8 @@ new_migrate=Migrasi Baru new_mirror=Duplikat Baru new_fork=Fork Repositori Baru new_org=Organisasi Baru +new_project=Proyek Baru +new_project_column=Kolom Baru manage_org=Mengelola Organisasi admin_panel=Administrasi Situs account_settings=Pengaturan Akun @@ -55,29 +71,58 @@ pull_requests=Tarik Permintaan issues=Masalah milestones=Tonggak +ok=Oke cancel=Batal +retry=Coba lagi +rerun=Jalankan ulang +rerun_all=Jalankan ulang semua job save=Simpan add=Tambah add_all=Tambah Semua remove=Buang remove_all=Buang Semua +remove_label_str=`Hapus item "%s"` edit=Edit +view=Tampilan +test=Pengujian enabled=Aktif disabled=Nonaktif +locked=Terkunci +copy=Salin +copy_url=Salin URL +copy_hash=Salin hash +copy_content=Salin konten +copy_branch=Salin nama branch +copy_success=Tersalin! +copy_error=Gagal menyalin +copy_type_unsupported=Tipe berkas ini tidak dapat disalin write=Tulis preview=Pratinjau loading=Memuat… +error=Gangguan +error404=Halaman yang akan kamu akses tidak dapat ditemukan atau kamu tidak memiliki akses untuk melihatnya. +go_back=Kembali +invalid_data=Data invalid: %v +never=Tidak Pernah +unknown=Tidak diketahui +rss_feed=Umpan Berita +pin=Sematkan +unpin=Lepas sematan +artifacts=Artefak +confirm_delete_artifact=Apakah Anda yakin ingin menghapus artefak '%s' ? archived=Diarsipkan +concept_system_global=Global +concept_user_individual=Perorangan concept_code_repository=Repositori show_full_screen=Tampilkan layar penuh @@ -682,13 +727,16 @@ commits.newer=Terbaru commits.signed_by=Ditandai oleh +commitstatus.error=Gangguan projects.description_placeholder=Deskripsi projects.title=Judul +projects.new=Proyek Baru projects.template.desc=Contoh projects.column.edit_title=Nama projects.column.new_title=Nama +projects.column.new=Kolom Baru issues.new=Masalah Baru issues.new.labels=Label diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 3b1d37a322..fb93a2ff07 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -4,6 +4,7 @@ explore=探索 help=說明 logo=標誌 sign_in=登入 +sign_in_with_provider=使用 %s 帳戶登入 sign_in_or=或 sign_out=登出 sign_up=註冊 @@ -16,6 +17,7 @@ template=模板 language=語言 notifications=通知 active_stopwatch=進行中的時間追蹤 +tracked_time_summary=目前的 issue 累計時長 create_new=建立... user_profile_and_more=個人資料和設定... signed_in_as=已登入 @@ -23,6 +25,7 @@ enable_javascript=本網站需要 JavaScript。 toc=目錄 licenses=授權條款 return_to_gitea=返回 Gitea +more_items=更多項目 username=帳號 email=電子信箱 @@ -80,6 +83,8 @@ milestones=里程碑 ok=確認 cancel=取消 retry=重試 +rerun=重新執行 +rerun_all=重新執行所有工作 save=儲存 add=增加 add_all=全部增加 @@ -87,14 +92,19 @@ remove=移除 remove_all=全部移除 remove_label_str=移除項目「%s」 edit=編輯 +view=檢視 +test=測試 enabled=已啟用 disabled=已停用 +locked=已上鎖 copy=複製 copy_url=複製 URL +copy_hash=複製哈希值 copy_content=複製內容 copy_branch=複製分支名稱 +copy_path=複製路徑 copy_success=複製成功! copy_error=複製失敗 copy_type_unsupported=無法複製此類型的檔案 @@ -105,6 +115,8 @@ loading=載入中… error=錯誤 error404=您正嘗試訪問的頁面 不存在您尚未被授權 查看該頁面。 +go_back=返回 +invalid_data=無效的資料: %v never=從來沒有 unknown=未知 @@ -114,25 +126,67 @@ rss_feed=RSS 摘要 pin=固定 unpin=取消固定 +artifacts=檔案或工件 +confirm_delete_artifact=你確定要刪除這個檔案 '%s' 嗎? archived=已封存 +concept_system_global=全域 +concept_user_individual=個人 concept_code_repository=儲存庫 concept_user_organization=組織 +show_timestamps=顯示時間戳記 +show_log_seconds=顯示秒數 +show_full_screen=全螢幕顯示 +download_logs=下載記錄 +confirm_delete_selected=確定要刪除所有已選取的項目嗎? name=名稱 value=值 filter=篩選 +filter.clear=清除篩選器 filter.is_archived=已封存 +filter.not_archived=未歸檔 +filter.is_fork=已分支 +filter.not_fork=未分支 +filter.is_mirror=已鏡像 +filter.not_mirror=不是鏡像 filter.is_template=模板 +filter.not_template=不是範本 filter.public=公開 filter.private=私有 +no_results_found=找不到結果。 +internal_error_skipped=已略過內部錯誤:%s [search] +search=搜尋… +type_tooltip=搜尋類型 +fuzzy=模糊 +fuzzy_tooltip=包含與搜尋詞接近的結果 +exact=精確 +exact_tooltip=只包含完全符合關鍵字的結果 +repo_kind=搜尋儲存庫… +user_kind=搜尋使用者… +org_kind=搜尋組織… +team_kind=搜尋團隊… +code_kind=搜尋代碼… +code_search_unavailable=現在無法使用原始碼搜尋。請與網站管理員聯絡。 +code_search_by_git_grep=目前的原始碼搜尋結果是由「git grep」提供。如果網站管理者啟用 Repository Indexer 可能可以提供更好的結果。 +package_kind=搜尋軟體包... +project_kind=搜尋專案… +branch_kind=搜尋分支… +tag_kind=搜尋標籤… +tag_tooltip=搜尋符合的標籤。使用「%」以比對任意長度的數字。 +commit_kind=搜尋提交歷史… +runner_kind=搜尋 Runner... +no_results=找不到符合的結果。 +issue_kind=搜尋議題… +pull_kind=搜尋合併請求... +keyword_search_unavailable=現在無法使用關鍵字搜尋。請與網站管理員聯絡。 [aria] navbar=導航列 @@ -156,9 +210,13 @@ buttons.link.tooltip=新增連結 buttons.list.unordered.tooltip=新增項目符號清單 buttons.list.ordered.tooltip=新增編號清單 buttons.list.task.tooltip=新增工作項目清單 +buttons.table.add.tooltip=新增表格 buttons.table.add.insert=增加 +buttons.table.rows=行 +buttons.table.cols=列 buttons.mention.tooltip=提及使用者或團隊 buttons.ref.tooltip=參考問題或合併請求 +buttons.switch_to_legacy.tooltip=使用舊版編輯器代替 buttons.enable_monospace_font=啟用等寬字型 buttons.disable_monospace_font=停用等寬字型 @@ -168,16 +226,20 @@ string.desc=Z - A [error] occurred=發生錯誤 +report_message=如果你確定這是一個 Gitea 的 bug,請去 GitHub 搜尋相關的問題,如果有需要你也可以開一個新的問題 not_found=找不到目標。 network_error=網路錯誤 [startpage] app_desc=一套極易架設的 Git 服務 install=安裝容易 +install_desc=直接用 執行檔安裝,還可以透過 Docker部屬,或是取得 套件。 platform=跨平台 +platform_desc=Gitea 可以在所有能編譯 Go 語言的平台上執行: Windows, macOS, Linux, ARM 等等。挑一個您喜歡的吧! lightweight=輕量級 lightweight_desc=一片便宜的 Raspberry Pi 就可以滿足 Gitea 的最低需求。節省您的機器資源! license=開放原始碼 +license_desc=取得 code.gitea.io/gitea !成為一名貢獻者和我們一起讓 Gitea 更好,快點加入我們吧! [install] install=安裝頁面 @@ -216,6 +278,7 @@ repo_path_helper=所有遠端 Git 儲存庫會儲存到此目錄。 lfs_path=Git LFS 根目錄 lfs_path_helper=以 Git LFS 儲存檔案時會被儲存在此目錄中。請留空以停用 LFS 功能。 run_user=以使用者名稱執行 +run_user_helper=輸入 Gitea 執行的作業系統使用者名稱。請注意,此使用者必須擁有存儲庫根目錄的存取權限。 domain=伺服器域名 domain_helper=伺服器的域名或主機位置。 ssh_port=SSH 伺服器埠 @@ -232,6 +295,7 @@ email_title=電子郵件設定 smtp_addr=SMTP 主機 smtp_port=SMTP 連接埠 smtp_from=電子郵件寄件者 +smtp_from_invalid=「以此電子信箱寄送」的地址無效 smtp_from_helper=Gitea 將會使用的電子信箱,直接輸入電子信箱或使用「"名稱" 」的格式。 mailer_user=SMTP 帳號 mailer_password=SMTP 密碼 @@ -287,8 +351,12 @@ invalid_password_algorithm=無效的密碼雜湊演算法 password_algorithm_helper=設定密碼雜湊演算法。演算法有不同的需求與強度。argon2 演算法雖然較安全但會使用大量記憶體,可能不適用於小型系統。 enable_update_checker=啟用更新檢查器 enable_update_checker_helper=定期連線到 gitea.io 檢查更新。 +env_config_keys=環境組態設定 +env_config_keys_prompt=下列環境變數也會套用到您的組態檔: +config_write_file_prompt=這些配置選項將被寫入到: %s [home] +nav_menu=導覽選單 uname_holder=帳號或電子信箱 password_holder=密碼 switch_dashboard_context=切換資訊主頁帳戶 @@ -318,6 +386,7 @@ issues.in_your_repos=在您的儲存庫中 repos=儲存庫 users=使用者 organizations=組織 +go_to=前往 code=程式碼 code_last_indexed_at=最後索引 %s relevant_repositories_tooltip=已隱藏缺少主題、圖示、說明、Fork 的儲存庫。 @@ -325,27 +394,38 @@ relevant_repositories=只顯示相關的儲存庫,顯示未篩選 [auth] create_new_account=註冊帳戶 +already_have_account=已經有帳號嗎? +sign_in_now=立即登入! disable_register_prompt=註冊功能已停用。 請聯繫您的網站管理員。 disable_register_mail=已停用註冊確認電子郵件。 manual_activation_only=請聯絡您的網站管理員以完成啟用程序。 remember_me=記得這個裝置 +remember_me.compromised=這個登入 token 已經失效,可能代表著帳號已被入侵。請確認您的帳號是否有不尋常的活動。 forgot_password_title=忘記密碼 forgot_password=忘記密碼? +need_account=需要一個帳號? +sign_up_now=還沒有帳戶?馬上註冊。 +sign_up_successful=帳戶已成功建立。歡迎您! +confirmation_mail_sent_prompt_ex=新的確認信已寄到%s。請在接下來的 %s 確認您的收件夾來完成註冊手續。如果您的註冊地址有錯誤,您可以再次登入並修改它。 must_change_password=更新您的密碼 allow_password_change=要求使用者更改密碼 (推薦) reset_password_mail_sent_prompt=確認信已發送至 %s。請在 %s內檢查您的收件匣並完成帳戶救援作業。 active_your_account=啟用您的帳戶 account_activated=帳戶已啟用 prohibit_login=禁止登入 +prohibit_login_desc=您的帳戶被禁止登入,請聯絡網站管理員 resent_limit_prompt=抱歉,您請求發送驗證電子郵件太過頻繁,請等待 3 分鐘後再試一次。 has_unconfirmed_mail=%s 您好,您有一封發送至( %s) 但未被確認的郵件。如果您未收到啟用郵件,或需要重新發送,請單擊下方的按鈕。 +change_unconfirmed_mail_address=如果您註冊的電子郵件地址有錯誤,您可以在這邊更正,並重新寄送確認郵件。 resend_mail=單擊此處重新發送確認郵件 email_not_associate=此電子信箱未與任何帳戶連結 send_reset_mail=發送帳戶救援信 reset_password=帳戶救援 invalid_code=您的確認代碼無效或已過期。 +invalid_code_forgot_password=您的確認代碼無效或已過期。開啟 這個連結 開始新的 session。 invalid_password=您的密碼和用來建立帳戶的不符。 reset_password_helper=帳戶救援 +reset_password_wrong_user=您已經使用 %s 的帳戶登入,但帳戶救援連結是給 %s 的 password_too_short=密碼長度不能少於 %d 個字! non_local_account=非本地帳戶無法透過 Gitea 的網頁介面更改密碼。 verify=驗證 @@ -365,11 +445,13 @@ oauth_signin_submit=連結帳戶 oauth.signin.error=處理授權請求時發生錯誤。如果這個問題持續發生,請聯絡網站管理員。 oauth.signin.error.access_denied=授權請求被拒絕。 oauth.signin.error.temporarily_unavailable=授權失敗,因為認證伺服器暫時無法使用。請稍後再試。 +oauth_callback_unable_auto_reg=自助註冊已啟用,但是 OAuth2 提供者 %[1]s 回傳的結果缺少欄位:%[2]s,導致無法自動建立帳號。請建立新帳號或是連結至既存的帳號,或是聯絡網站管理者。 openid_connect_submit=連接 openid_connect_title=連接到現有帳戶 openid_connect_desc=所選的 OpenID URI 未知。在這裡連結一個新帳戶。 openid_register_title=建立新帳戶 openid_register_desc=所選的 OpenID URI 未知。在這裡連結一個新帳戶。 +openid_signin_desc=輸入您的 OpenID 位址。例如:alice.openid.example.org 或是 https://openid.example.org/alice。 disable_forgot_password_mail=由於未設定電子郵件功能,帳戶救援功能已被停用。請與網站管理員聯絡。 disable_forgot_password_mail_admin=帳戶救援功能需要設定電子郵件功能才能使用。請設定電子郵件功能以啟用帳戶救援功能。 email_domain_blacklisted=您無法使用您的電子信箱註冊帳號。 @@ -379,8 +461,13 @@ authorize_application_created_by=此應用程式是由 %s 建立的。 authorize_application_description=如果您允許,它將能夠讀取和修改您的所有帳戶資訊,包括私有儲存庫和組織。 authorize_title=授權「%s」存取您的帳戶? authorization_failed=授權失效 +authorization_failed_desc=授權失敗,因為我們偵測到無效的請求。請聯絡您欲授權之應用程式的維護人員。 sspi_auth_failed=SSPI 認證失敗 +password_pwned=您選擇的密碼已被列於被盜密碼清單中,該清單因公共資料外洩而暴露。請試試其他密碼。 password_pwned_err=無法完成對 HaveIBeenPwned 的請求。 +last_admin=您無法移除最後一個管理員。至少需要保留一個管理員帳戶。 +signin_passkey=使用 Passkey 登入 +back_to_sign_in=返回至登入 [mail] view_it_on=在 %s 上查看 @@ -394,8 +481,10 @@ activate_account.text_1=%[1]s 您好,感謝您註冊 %[2]s! activate_account.text_2=請在 %s內點擊下列連結以啟用您的帳戶: activate_email=請驗證您的電子信箱 +activate_email.title=%s,請驗證您的電子信箱 activate_email.text=請在 %s內點擊下列連結以驗證您的電子信箱: +register_notify=歡迎來到 Gitea register_notify.title=%[1]s,歡迎來到 %[2]s register_notify.text_1=這是您在 %s 的註冊確認信! register_notify.text_2=您現在可以用帳號 %s 登入。 @@ -497,6 +586,9 @@ lang_select_error=從清單中選擇一個語言。 username_been_taken=帳號已被使用 username_change_not_local_user=非本地使用者不允許更改他們的帳號。詳細資訊請聯絡您的系統管理員。 +change_username_disabled=更改使用者名稱功能已被停用。 +change_full_name_disabled=更改完整名稱功能已被停用。 +username_has_not_been_changed=使用者名稱並未變更 repo_name_been_taken=儲存庫名稱已被使用。 repository_force_private=已啟用「強制私有」:私有儲存庫不能被公開。 repository_files_already_exist=此儲存庫的檔案已存在,請聯絡系統管理有。 @@ -510,6 +602,7 @@ team_name_been_taken=團隊名稱已被使用。 team_no_units_error=請至少選擇一個儲存庫區域。 email_been_used=此電子信箱已被使用 email_invalid=此電子信箱無效。 +email_domain_is_not_allowed=使用者的電子郵件地址 %s 與電子郵件域名允許清單或是電子郵件域名禁止清單有衝突。請確認您預期執行這個動作。 openid_been_used=OpenID 位址「%s」已被使用。 username_password_incorrect=帳號或密碼不正確 password_complexity=密碼複雜度沒有通過以下的要求: @@ -521,6 +614,8 @@ enterred_invalid_repo_name=您輸入的儲存庫名稱不正確。 enterred_invalid_org_name=您輸入的組織名稱不正確。 enterred_invalid_owner_name=新的擁有者名稱無效。 enterred_invalid_password=您輸入的密碼不正確。 +unset_password=登入的使用者並未設定密碼。 +unsupported_login_type=這個登入方式並不支援刪除帳號。 user_not_exist=該用戶名不存在 team_not_exist=團隊不存在 last_org_owner=你不能從「Owners」團隊中刪除最後一個使用者。每個組織中至少要有一個擁有者。 @@ -542,10 +637,13 @@ org_still_own_repo=此組織仍然擁有一個以上的儲存庫,請先刪除 org_still_own_packages=此組織仍然擁有一個以上的套件,請先刪除它們。 target_branch_not_exist=目標分支不存在 +target_ref_not_exist=目標參考不存在 %s +admin_cannot_delete_self=當您是管理者時,您無法移除自己。請先移除您的管理者權限。 [user] change_avatar=更改大頭貼... +joined_on=加入於 %s repositories=儲存庫 activity=公開動態 followers=追蹤者 @@ -561,11 +659,36 @@ user_bio=個人簡介 disabled_public_activity=這個使用者已對外隱藏動態 email_visibility.limited=所有已驗證的使用者都可以看到您的電子信箱地址 email_visibility.private=只有您和系統管理員可以看到您的電子信箱地址 +show_on_map=在地圖上顯示此位置 +settings=使用者設定 form.name_reserved=「%s」是保留的帳號。 form.name_pattern_not_allowed=帳號不可包含字元「%s」。 form.name_chars_not_allowed=帳號「%s」包含無效字元。 +block.block=封鎖 +block.block.user=封鎖使用者 +block.block.org=為組織阻擋使用者 +block.block.failure=無法封鎖使用者: %s +block.unblock=解除封鎖 +block.unblock.failure=無法解除封鎖使用者: %s +block.blocked=您已封鎖該使用者。 +block.title=封鎖使用者 +block.info=阻擋使用者可避免他們與儲存庫互動,如在建立合併請求或是問題以及於其上留言。了解更多關於阻擋一個使用者。 +block.info_1=阻擋一個使用者可避免其對您的帳號或是儲存庫進行以下動作: +block.info_2=正在追蹤你的帳戶 +block.info_3=透過 @mentioning 標記您的使用者名稱來通知您 +block.info_4=邀請您成為他們的儲存庫的協作者 +block.info_5=對儲存庫加上星號、建立分之或是關注儲存庫 +block.info_6=建立問題或是合併請求,或是對其進行留言 +block.info_7=對您在問題或是合併請求中的留言送出反應 +block.user_to_block=欲阻擋的使用者 +block.note=備註 +block.note.title=選用附註: +block.note.info=被阻擋的使用者不會看到這個附註。 +block.note.edit=編輯備註 +block.list=已封鎖的使用者 +block.list.none=您尚未封鎖任何使用者。 [settings] profile=個人資料 @@ -580,10 +703,17 @@ applications=應用程式 orgs=管理組織 repos=儲存庫 delete=刪除帳戶 +twofa=兩步驟驗證 account_link=已連結帳號 organization=組織 +webauthn=安全金鑰 public_profile=公開的個人資料 +biography_placeholder=告訴我們一些關於您的事情吧! (您可以使用 Markdown) +location_placeholder=與其他人分享您的大概位置 +profile_desc=控制您的個人檔案會如何呈現給其她使用者。您的主要電子郵件地址會被用於通知、密碼救援以及網頁上的 Git 操作。 +password_username_disabled=非本地使用者不允許更改他們的帳號。詳細資訊請聯絡您的系統管理員。 +password_full_name_disabled=您不被允許更改他們的全名。詳細資訊請聯絡您的系統管理員。 full_name=全名 website=個人網站 location=所在地區 @@ -594,11 +724,16 @@ update_language_not_found=無法使用語言「%s」。 update_language_success=已更新語言。 update_profile_success=已更新您的個人資料。 change_username=您的帳號已更改。 +change_username_prompt=註:更改您的使用名稱也會更改您的帳號網址。 +change_username_redirect_prompt=舊的帳號被領用前,會重新導向您的新帳號。 continue=繼續 cancel=取消 language=語言 ui=佈景主題 hidden_comment_types=隱藏的留言類型 +hidden_comment_types_description=此處勾選的評論類型將不會顯示在問題頁面內。例如勾選「標籤」將移除所有的"{user} 新增/移除{label}" 評論。 +hidden_comment_types.ref_tooltip=當這個問題在其他的問題、提交…等地方被引用時的留言 +hidden_comment_types.issue_ref_tooltip=當使用者更改與這個問題相關聯的分支、標籤時的留言 comment_type_group_reference=參考 comment_type_group_label=標籤 comment_type_group_milestone=里程碑 @@ -615,6 +750,7 @@ comment_type_group_project=專案 comment_type_group_issue_ref=問題參考 saved_successfully=您的設定已成功儲存。 privacy=隱私 +keep_activity_private=隱藏個人檔案上的動態 keep_activity_private_popup=讓動態只有你和管理員看得到 lookup_avatar_by_mail=以電子信箱查詢大頭貼 @@ -624,8 +760,10 @@ choose_new_avatar=選擇新的大頭貼 update_avatar=更新大頭貼 delete_current_avatar=刪除目前的大頭貼 uploaded_avatar_not_a_image=上傳的檔案不是圖片 +uploaded_avatar_is_too_big=上傳檔案大小 (%d KiB) 超過大小上限 (%d KiB) update_avatar_success=您的大頭貼已更新 update_user_avatar_success=已更新使用者的大頭貼。 +cropper_prompt=您可以在儲存前編輯圖片。編輯後的圖片將以 PNG 格式儲存。 change_password=更新密碼 old_password=目前的密碼 @@ -639,13 +777,17 @@ emails=電子信箱 manage_emails=管理電子信箱 manage_themes=選擇預設佈景主題 manage_openid=管理 OpenID 位址 +email_desc=您的主要電子信箱將用於通知、密碼恢復以及(如果未隱藏)基於網頁的 Git 操作。 theme_desc=這將是您在整個網站上的預設佈景主題。 +theme_colorblindness_help=色盲主題支援 +theme_colorblindness_prompt=Gitea 剛取得了一些具有基本色盲支援的主題,其中僅定義了幾種顏色。這項工作仍在進行中。透過在主題 CSS 檔案中定義更多顏色可以完成更多改進。 primary=主要 activated=已啟用 requires_activation=需要啟動 primary_email=設為主要 activate_email=寄出啟用信 activations_pending=等待啟用中 +can_not_add_email_activations_pending=有一個待處理的啟用,若要新增電子信箱,請幾分鐘後再試。 delete_email=移除 email_deletion=移除電子信箱 email_deletion_desc=電子信箱和相關資訊將從您的帳戶中刪除,由此電子信箱所提交的 Git 將保持不變,是否繼續? @@ -659,11 +801,13 @@ add_new_email=新增電子信箱 add_new_openid=新增 OpenID URI add_email=新增電子信箱 add_openid=新增 OpenID URI +add_email_confirmation_sent=確認信已發送至「%s」,請在 %s內檢查您的收件匣並確認您的電子信箱。 add_email_success=已加入新的電子信箱。 email_preference_set_success=已套用郵件偏好設定 add_openid_success=已加入新的 OpenID 地址。 keep_email_private=隱藏電子信箱 -openid_desc=OpenID 讓你可以授權認證給外部服務。 +keep_email_private_popup=這將隱藏您的電子郵件地址,無論是在您的個人資料中,還是在您使用網頁介面進行 pull request 或編輯文件時。推送的提交將不會被修改。使用 %s 在提交中將其與您的帳戶關聯。 +openid_desc=OpenID 讓您可以授權認證給外部服務。 manage_ssh_keys=管理 SSH 金鑰 manage_ssh_principals=管理 SSH 認證主體 @@ -724,6 +868,8 @@ ssh_principal_deletion_desc=移除 SSH 認證主體將撤銷其對您帳戶的 ssh_key_deletion_success=SSH 金鑰已被移除。 gpg_key_deletion_success=GPG 金鑰已被移除。 ssh_principal_deletion_success=已移除主體。 +added_on=新增於 %s +valid_until_date=有效直到 %s valid_forever=永遠有效 last_used=上次使用在 no_activity=沒有近期動態 @@ -735,9 +881,12 @@ principal_state_desc=此主體在過去 7 天內曾被使用 show_openid=在個人資料顯示 hide_openid=從個人資料隱藏 ssh_disabled=已停用 SSH +ssh_signonly=目前已停用 SSH,因此這些金鑰僅用於 commit 簽章驗證。 ssh_externally_managed=此 SSH 金鑰由此使用者的外部服務所管理 manage_social=管理關聯的社群帳戶 +social_desc=這些帳號可以用已登入你的帳號,請確認你知道它們。 unbind=解除連結 +unbind_success=社群帳號刪除成功 manage_access_token=管理 Access Token generate_new_token=產生新的 Token @@ -752,8 +901,17 @@ access_token_deletion_cancel_action=取消 access_token_deletion_confirm_action=刪除 access_token_deletion_desc=刪除 Token 後,使用此 Token 的應用程式將無法再存取您的帳戶,此動作不可還原。是否繼續? delete_token_success=已刪除 Token。使用此 Token 的應用程式無法再存取您的帳戶。 +repo_and_org_access=儲存庫和組織存取 +permissions_public_only=僅公開 +permissions_access_all=全部 (公開、私有與受限) +select_permissions=選擇權限 +permission_not_set=尚未設定 permission_no_access=沒有權限 permission_read=讀取 +permission_write=讀取和寫入 +access_token_desc=選擇的 token 權限僅限於對應的 API 路徑授權。閱讀 文件 以了解更多資訊。 +at_least_one_permission=您必須選擇至少一個權限來建立 token +permissions_list=權限: manage_oauth2_applications=管理 OAuth2 應用程式 edit_oauth2_application=編輯 OAuth2 應用程式 @@ -763,38 +921,56 @@ remove_oauth2_application_desc=刪除 OAuth2 應用程式將會撤銷所有已 remove_oauth2_application_success=已刪除應用程式。 create_oauth2_application=新增 OAuth2 應用程式 create_oauth2_application_button=建立應用程式 +create_oauth2_application_success=您已成功建立新的 OAuth2 應用程式。 +update_oauth2_application_success=您已成功更新 OAuth2 應用程式。 oauth2_application_name=應用程式名稱 oauth2_confidential_client=機密客戶端 (Confidential Client)。請為能保持機密性的程式勾選,例如網頁應用程式。使用原生程式時不要勾選,包含桌面、行動應用程式。 +oauth2_skip_secondary_authorization=授權一次後,跳過公用客戶端的二次授權。可能會帶來安全風險。 +oauth2_redirect_uris=重新導向 URI,每行一個 URI。 save_application=儲存 oauth2_client_id=客戶端 ID oauth2_client_secret=客戶端密鑰 oauth2_regenerate_secret=重新產生密鑰 oauth2_regenerate_secret_hint=遺失您的密鑰? +oauth2_client_secret_hint=離開或重新整理此頁面後將不會再顯示密鑰。請確保您已保存它。 oauth2_application_edit=編輯 oauth2_application_create_description=OAuth2 應用程式讓您的第三方應用程式可以存取此 Gitea 上的帳戶。 +oauth2_application_remove_description=移除 OAuth2 應用程式將阻止其存取此實例上的授權使用者帳戶。是否繼續? +oauth2_application_locked=Gitea 在啟動時會預先註冊一些 OAuth2 應用程式(如果在配置中啟用)。為防止意外行為,這些應用程式無法編輯或刪除。請參閱 OAuth2 文件以獲取更多資訊。 authorized_oauth2_applications=已授權的 OAuth2 應用程式 +authorized_oauth2_applications_description=您已授權這些第三方應用程式存取您的 Gitea 帳戶。請撤銷不再需要的應用程式的存取權。 revoke_key=撤銷 revoke_oauth2_grant=撤銷存取權 -revoke_oauth2_grant_description=撤銷此第三方應用程式的存取權,此應用程式就無法再存取您的資料?您確定嗎? +revoke_oauth2_grant_description=撤銷此第三方應用程式的存取權,此應用程式就無法再存取您的資料。您確定嗎? +revoke_oauth2_grant_success=成功撤銷存取權。 +twofa_desc=兩步驟驗證可以增強您的帳戶安全性。 +twofa_recovery_tip=如果您遺失設備,您可以使用一次性恢復密鑰重新獲取帳戶存取權。 twofa_is_enrolled=您的帳戶已經啟用兩步驟驗證。 twofa_not_enrolled=您的帳戶目前尚未啟用兩步驟驗證。 twofa_disable=停用兩步驟驗證 +twofa_scratch_token_regenerate=重新產生備用驗證碼 +twofa_scratch_token_regenerated=您的單次使用恢復密鑰現在是 %s。請將其存放在安全的地方,因為它不會再次顯示。 twofa_enroll=啟用兩步驟驗證 twofa_disable_note=如有需要,您可以停用兩步驟驗證。 twofa_disable_desc=關閉兩步驟驗證會使您的帳戶安全性降低,是否繼續? +regenerate_scratch_token_desc=如果您遺失了備用驗證碼或已經使用它登入,您可以在此重新設定。 twofa_disabled=兩步驟驗證已經被關閉。 scan_this_image=使用您的授權應用程式來掃瞄圖片: or_enter_secret=或者輸入密碼: %s then_enter_passcode=然後輸入應用程式中顯示的驗證碼: passcode_invalid=無效的驗證碼,請重試。 +twofa_enrolled=您的帳戶已經啟用了兩步驟驗證。請將備用驗證碼 (%s) 保存到安全的地方,它只會顯示這麼一次! twofa_failed_get_secret=取得密鑰 (Secret) 失敗。 +webauthn_desc=安全金鑰是包含加密密鑰的硬體設備,它們可以用於兩步驟驗證。安全金鑰必須支援 WebAuthn Authenticator 標準。 webauthn_register_key=新增安全金鑰 webauthn_nickname=暱稱 webauthn_delete_key=移除安全金鑰 webauthn_delete_key_desc=如果您移除安全金鑰,將不能再使用它登入。是否繼續? +webauthn_key_loss_warning=如果您遺失了安全金鑰,您將無法存取您的帳戶。 +webauthn_alternative_tip=您可能需要設定其他的驗證方法。 manage_account_links=管理已連結的帳戶 manage_account_links_desc=這些外部帳戶已連結到您的 Gitea 帳戶。 @@ -804,8 +980,10 @@ remove_account_link=刪除已連結的帳戶 remove_account_link_desc=刪除連結帳戶將撤銷其對 Gitea 帳戶的存取權限。是否繼續? remove_account_link_success=已移除連結的帳戶。 +hooks.desc=新增 Webhook,當您擁有的所有儲存庫觸發事件時將會執行。 orgs_none=您尚未成為任一組織的成員。 +repos_none=您尚未擁有任何儲存庫。 delete_account=刪除您的帳戶 delete_prompt=此動作將永久刪除您的使用者帳戶,而且無法復原。 @@ -824,9 +1002,12 @@ visibility=使用者瀏覽權限 visibility.public=公開 visibility.public_tooltip=所有人都可以看到 visibility.limited=受限 +visibility.limited_tooltip=只有已授權的使用者可見 visibility.private=私人 +visibility.private_tooltip=僅對您已加入的組織成員可見 [repo] +new_repo_helper=儲存庫包含所有專案檔案,包括修訂歷史。已經在其他地方託管了嗎?遷移儲存庫。 owner=擁有者 owner_helper=組織可能因為儲存庫數量上限而未列入此選單。 repo_name=儲存庫名稱 @@ -838,6 +1019,7 @@ template_helper=將儲存庫設為範本 template_description=儲存庫範本讓使用者可新增相同目錄結構、檔案以及設定的儲存庫。 visibility=瀏覽權限 visibility_description=只有組織擁有者或有權限的組織成員才能看到。 +visibility_helper=將儲存庫設為私有 visibility_helper_forced=您的網站管理員強制新的存儲庫必需設定為私有。 visibility_fork_helper=(修改本值將會影響所有 fork 儲存庫) clone_helper=需要有關 Clone 的協助嗎?查看幫助 。 @@ -846,7 +1028,14 @@ fork_from=Fork 自 already_forked=您已經 fork 過 %s fork_to_different_account=Fork 到其他帳戶 fork_visibility_helper=無法更改 fork 儲存庫的瀏覽權限。 +fork_branch=要克隆到 fork 的分支 +all_branches=所有分支 +view_all_branches=查看所有分支 +view_all_tags=查看所有標籤 +fork_no_valid_owners=此儲存庫無法 fork,因為沒有有效的擁有者。 +fork.blocked_user=無法 fork 儲存庫,因為您被儲存庫擁有者封鎖。 use_template=使用此範本 +open_with_editor=以 %s 開啟 download_zip=下載 ZIP download_tar=下載 TAR.GZ download_bundle=下載 BUNDLE @@ -854,6 +1043,8 @@ generate_repo=產生儲存庫 generate_from=產生自 repo_desc=描述 repo_desc_helper=輸入簡介 (選用) +repo_no_desc=未提供描述 +repo_lang=儲存庫語言 repo_gitignore_helper=選擇 .gitignore 範本 repo_gitignore_helper_desc=從常見語言範本清單中挑選忽略追蹤的檔案。預設情況下各種語言建置工具產生的特殊檔案都包含在 .gitignore 中。 issue_labels=問題標籤 @@ -861,6 +1052,9 @@ issue_labels_helper=選擇問題標籤集 license=授權條款 license_helper=請選擇授權條款檔案 license_helper_desc=授權條款定義了他人使用您原始碼的允許和禁止事項。不確定哪個適用於您的專案?查看選擇授權條款。 +multiple_licenses=多重授權 +object_format=物件格式 +object_format_helper=儲存庫的物件格式。無法更改。SHA1 是最兼容的。 readme=讀我檔案 readme_helper=選擇讀我檔案範本。 readme_helper_desc=這是您能為專案撰寫完整描述的地方。 @@ -872,15 +1066,19 @@ trust_model_helper_collaborator_committer=協作者 + 提交者: 信任協作者 trust_model_helper_default=預設: 使用此 Gitea 的預設儲存庫信任模式 create_repo=建立儲存庫 default_branch=預設分支 +default_branch_label=預設 default_branch_helper=預設分支是合併請求和提交程式碼的基礎分支。 mirror_prune=裁減 mirror_prune_desc=刪除過時的遠端追蹤參考 mirror_interval=鏡像間隔 (有效時間單位為 'h'、'm'、's'),設為 0 以停用定期同步。(最小間隔: %s) mirror_interval_invalid=鏡像週期無效 +mirror_sync=已同步 mirror_sync_on_commit=推送提交後進行同步 mirror_address=從 URL Clone mirror_address_desc=在授權資訊中填入必要的資料。 -mirror_lfs=Large File Storage (LFS) +mirror_address_url_invalid=提供的 URL 無效。您必須正確轉義 URL 的所有組件。 +mirror_address_protocol_invalid=提供的 URL 無效。僅可使用 http(s):// 或 git:// 位置進行鏡像。 +mirror_lfs=大型檔案存儲 (LFS) mirror_lfs_desc=啟動 LFS 檔案的鏡像功能。 mirror_lfs_endpoint=LFS 端點 mirror_lfs_endpoint_desc=同步將會嘗試使用 Clone URL 來確認 LFS 伺服器。如果存儲庫的 LFS 資料放在其他地方,您也可以指定自訂的端點。 @@ -890,7 +1088,9 @@ mirror_password_blank_placeholder=(未設定) mirror_password_help=修改帳號以清除已儲存的密碼。 watchers=關注者 stargazers=占星術師 +stars_remove_warning=這將移除此儲存庫的所有星標。 forks=Fork +stars=星 reactions_more=再多添加 %d個 unit_disabled=網站管理員已經停用這個儲存庫區域。 language_other=其他 @@ -904,10 +1104,20 @@ delete_preexisting=刪除既有的檔案 delete_preexisting_content=刪除 %s 中的檔案 delete_preexisting_success=刪除 %s 中未接管的檔案 blame_prior=檢視此變更前的 Blame +blame.ignore_revs=忽略 .git-blame-ignore-revs 中的修訂。點擊 這裡 以繞過並查看正常的 Blame 視圖。 +blame.ignore_revs.failed=忽略 .git-blame-ignore-revs 中的修訂失敗。 +user_search_tooltip=顯示最多 30 個使用者 +tree_path_not_found_commit=路徑 %[1]s 在提交 %[2]s 中不存在 +tree_path_not_found_branch=路徑 %[1]s 在分支 %[2]s 中不存在 +tree_path_not_found_tag=路徑 %[1]s 在標籤 %[2]s 中不存在 transfer.accept=同意轉移 +transfer.accept_desc=轉移到「%s」 transfer.reject=拒絕轉移 +transfer.reject_desc=取消轉移到「%s」 +transfer.no_permission_to_accept=您沒有權限接受此轉移。 +transfer.no_permission_to_reject=您沒有權限拒絕此轉移。 desc.private=私有 desc.public=公開 @@ -926,12 +1136,15 @@ template.issue_labels=問題標籤 template.one_item=至少須選擇一個範本項目 template.invalid=必須選擇一個儲存庫範本 -archive.issue.nocomment=此存儲庫已封存,您不能在問題上留言。 -archive.pull.nocomment=此存儲庫已封存,您不能在合併請求上留言。 +archive.title=此儲存庫已封存。您可以查看檔案並進行 Clone,但無法推送或開啟問題或合併請求。 +archive.title_date=此儲存庫已於 %s 封存。您可以查看檔案並進行 Clone,但無法推送或開啟問題或合併請求。 +archive.issue.nocomment=此儲存庫已封存,您不能在問題上留言。 +archive.pull.nocomment=此儲存庫已封存,您不能在合併請求上留言。 form.reach_limit_of_creation_1=您已經達到了您儲存庫的數量上限 (%d 個)。 form.reach_limit_of_creation_n=您已經達到了您儲存庫的數量上限 (%d 個)。 form.name_reserved=「%s」是保留的儲存庫名稱。 +form.name_pattern_not_allowed=儲存庫名稱不可包含字元「%s」。 need_auth=授權 migrate_options=遷移選項 @@ -941,6 +1154,7 @@ migrate_options_lfs=遷移 LFS 檔案 migrate_options_lfs_endpoint.label=LFS 端點 migrate_options_lfs_endpoint.description=遷移將會嘗試使用您的 Git Remote 來確認 LFS 伺服器。如果存儲庫的 LFS 資料放在其他地方,您也可以指定自訂的端點。 migrate_options_lfs_endpoint.description.local=同時也支援本地伺服器路徑。 +migrate_options_lfs_endpoint.placeholder=如果留空,端點將從 Clone URL 中推導出來 migrate_items=遷移項目 migrate_items_wiki=Wiki migrate_items_milestones=里程碑 @@ -965,6 +1179,7 @@ migrated_from_fake=已從 %[1]s 遷移 migrate.migrate=從 %s 遷移 migrate.migrating=正在從 %s 遷移... migrate.migrating_failed=從 %s 遷移失敗 +migrate.migrating_failed.error=遷移失敗: %s migrate.migrating_failed_no_addr=遷移失敗。 migrate.github.description=從 github.com 或其他 GitHub 執行個體遷移資料。 migrate.git.description=從任何 Git 服務遷移儲存庫。 @@ -974,6 +1189,9 @@ migrate.gogs.description=從 notabug.org 或其他 Gogs 執行個體遷移資料 migrate.onedev.description=從 code.onedev.io 或其他 OneDev 執行個體遷移資料。 migrate.codebase.description=從 codebasehq.com 遷移資料。 migrate.gitbucket.description=從 GitBucket 執行個體遷移資料。 +migrate.codecommit.description=從 AWS CodeCommit 遷移資料。 +migrate.codecommit.https_git_credentials_username=HTTPS Git 憑證使用者名稱 +migrate.codecommit.https_git_credentials_password=HTTPS Git 憑證密碼 migrate.migrating_git=正在遷移 Git 資料 migrate.migrating_topics=正在遷移主題 migrate.migrating_milestones=正在遷移里程碑 @@ -981,6 +1199,8 @@ migrate.migrating_labels=正在遷移標籤 migrate.migrating_releases=正在遷移版本發布 migrate.migrating_issues=正在遷移問題 migrate.migrating_pulls=正在遷移合併請求 +migrate.cancel_migrating_title=取消遷移 +migrate.cancel_migrating_confirm=您要取消遷移嗎? mirror_from=鏡像自 forked_from=fork 自 @@ -994,6 +1214,7 @@ watch=關注 unstar=移除星號 star=加上星號 fork=Fork +action.blocked_user=無法執行操作,因為您被儲存庫擁有者封鎖。 download_archive=下載此儲存庫 more_operations=更多操作 @@ -1031,6 +1252,7 @@ releases=版本發布 tag=標籤 released_this=發布了此版本 tagged_this=標記了此標籤 +file.title=%s 於 %s file_raw=原始文件 file_history=歷史記錄 file_view_source=檢視原始碼 @@ -1038,22 +1260,36 @@ file_view_rendered=檢視渲染圖 file_view_raw=查看原始文件 file_permalink=永久連結 file_too_large=檔案太大,無法顯示。 -invisible_runes_line=`這一行有看不見的 Unicode 字元` -ambiguous_runes_line=`這一行有易混淆的 Unicode 字元` +file_is_empty=檔案是空的。 +code_preview_line_from_to=第 %[1]d 行到第 %[2]d 行在 %[3]s +code_preview_line_in=第 %[1]d 行在 %[2]s +invisible_runes_header=此檔案包含不可見的 Unicode 字元 +invisible_runes_description=此檔案包含不可見的 Unicode 字元,這些字元對人類來說是無法區分的,但電腦可能會以不同方式處理。如果您認為這是有意的,可以安全地忽略此警告。使用 Escape 鍵來顯示它們。 +ambiguous_runes_header=此檔案包含易混淆的 Unicode 字元 +ambiguous_runes_description=此檔案包含可能與其他字元混淆的 Unicode 字元。如果您認為這是有意的,可以安全地忽略此警告。使用 Escape 鍵來顯示它們。 +invisible_runes_line=這一行有看不見的 Unicode 字元 +ambiguous_runes_line=這一行有易混淆的 Unicode 字元 +ambiguous_character=%[1]c [U+%04[1]X] 容易與 %[2]c [U+%04[2]X] 混淆 -escape_control_characters=Escape -unescape_control_characters=Unescape +escape_control_characters=轉義控制字元 +unescape_control_characters=取消轉義控制字元 file_copy_permalink=複製固定連結 view_git_blame=檢視 Git Blame video_not_supported_in_browser=您的瀏覽器不支援使用 HTML5 播放影片。 audio_not_supported_in_browser=您的瀏覽器不支援 HTML5 的「audio」標籤 stored_lfs=已使用 Git LFS 儲存 symbolic_link=符號連結 +executable_file=可執行檔 +vendored=已供應 +generated=已產生 commit_graph=提交線圖 commit_graph.select=選擇分支 commit_graph.hide_pr_refs=隱藏合併請求 commit_graph.monochrome=單色 commit_graph.color=彩色 +commit.contained_in=此提交包含在: +commit.contained_in_default_branch=此提交是預設分支的一部分 +commit.load_referencing_branches_and_tags=載入引用此提交的分支和標籤 blame=Blame download_file=下載檔案 normal_view=標準檢視 @@ -1081,6 +1317,7 @@ editor.or=或 editor.cancel_lower=取消 editor.commit_signed_changes=提交簽署過的變更 editor.commit_changes=提交變更 +editor.add_tmpl=新增「」 editor.add=新增 %s editor.update=更新 %s editor.delete=刪除 %s @@ -1101,8 +1338,15 @@ editor.filename_cannot_be_empty=檔案名稱不能為空。 editor.filename_is_invalid=檔名無效:「%s」。 editor.branch_does_not_exist=此儲存庫沒有名為「%s」的分支。 editor.branch_already_exists=此儲存庫已有名為「%s」的分支。 +editor.directory_is_a_file=目錄名稱「%s」已被此儲存庫的檔案使用。 +editor.file_is_a_symlink=`"%s" 是一個符號連結。符號連結無法在網頁編輯器中編輯` +editor.filename_is_a_directory=檔名「%s」已被此儲存庫的目錄名稱使用。 +editor.file_editing_no_longer_exists=正要編輯的檔案「%s」已不存在此儲存庫中。 +editor.file_deleting_no_longer_exists=正要刪除的檔案「%s」已不存在此儲存庫中。 editor.file_changed_while_editing=檔案內容在您編輯的途中已被變更。按一下此處查看更動的地方或再次提交以覆蓋這些變更。 editor.file_already_exists=此儲存庫已有名為「%s」的檔案。 +editor.commit_id_not_matching=提交 ID 與您開始編輯時的 ID 不匹配。請提交到一個補丁分支然後合併。 +editor.push_out_of_date=推送似乎已過時。 editor.commit_empty_file_header=提交空白檔案 editor.commit_empty_file_text=你準備提交的檔案是空白的,是否繼續? editor.no_changes_to_show=沒有可以顯示的變更。 @@ -1127,6 +1371,7 @@ commits.commits=次程式碼提交 commits.no_commits=沒有共同的提交。「%s」和「%s」的歷史完全不同。 commits.nothing_to_compare=這些分支是相同的。 commits.search.tooltip=你可以用「author:」、「committer:」、「after:」、「before:」等作為關鍵字的前綴,例如: 「revert author:Alice before:2019-01-13」。 +commits.search_branch=此分支 commits.search_all=所有分支 commits.author=作者 commits.message=備註 @@ -1138,6 +1383,7 @@ commits.signed_by_untrusted_user=由不信任的使用者簽署 commits.signed_by_untrusted_user_unmatched=由不受信任且與提交者不相符的使用者簽署 commits.gpg_key_id=GPG 金鑰 ID commits.ssh_key_fingerprint=SSH 金鑰指紋 +commits.view_path=檢視此歷史時刻 commit.operations=操作 commit.revert=還原 @@ -1155,6 +1401,7 @@ commitstatus.success=成功 ext_issues=存取外部問題 ext_issues.desc=連結到外部問題追蹤器。 +projects.desc=在專案看板中管理問題與合併請求。 projects.description=描述 (選用) projects.description_placeholder=描述 projects.create=建立專案 @@ -1182,6 +1429,7 @@ projects.column.new=新增欄位 projects.column.set_default=設為預設 projects.column.set_default_desc=將此欄位設定為未分類問題及合併請求的預設預設值 projects.column.delete=刪除欄位 +projects.column.deletion_desc=刪除專案欄位會將所有相關的問題移動到「未分類」,是否繼續? projects.column.color=顏色 projects.open=開啟 projects.close=關閉 @@ -1213,6 +1461,10 @@ issues.new.clear_milestone=清除已選取里程碑 issues.new.assignees=負責人 issues.new.clear_assignees=清除負責人 issues.new.no_assignees=沒有負責人 +issues.new.no_reviewers=沒有審核者 +issues.new.blocked_user=無法建立問題,因為您被儲存庫擁有者封鎖。 +issues.edit.already_changed=無法儲存問題的變更。看起來內容已被其他使用者更改。請重新整理頁面並再次嘗試編輯以避免覆蓋他們的變更。 +issues.edit.blocked_user=無法編輯內容,因為您被發文者或儲存庫擁有者封鎖。 issues.choose.get_started=開始 issues.choose.open_external_link=開啟 issues.choose.blank=預設 @@ -1238,6 +1490,7 @@ issues.remove_labels=移除了 %s 標籤 %s issues.add_remove_labels=加入了 %s 並移除了 %s 標籤 %s issues.add_milestone_at=`新增到 %s 里程碑 %s` issues.add_project_at=`將此加入到 %s 專案 %s` +issues.move_to_column_of_project=`將此移動到 %s 的 %s 中 %s` issues.change_milestone_at=`%[3]s 修改了里程碑 %[1]s%[2]s` issues.change_project_at=`將專案從 %s 修改為 %s %s` issues.remove_milestone_at=`從 %s 里程碑移除 %s` @@ -1258,6 +1511,10 @@ issues.filter_label_exclude=`使用 alt + click/enter issues.filter_label_no_select=所有標籤 issues.filter_label_select_no_label=沒有標籤 issues.filter_milestone=里程碑 +issues.filter_milestone_all=所有里程碑 +issues.filter_milestone_none=無里程碑 +issues.filter_milestone_open=開放中的里程碑 +issues.filter_milestone_closed=已關閉的里程碑 issues.filter_project=專案 issues.filter_project_all=所有專案 issues.filter_project_none=未選擇專案 @@ -1265,6 +1522,8 @@ issues.filter_assignee=負責人 issues.filter_assginee_no_select=所有負責人 issues.filter_assginee_no_assignee=沒有負責人 issues.filter_poster=作者 +issues.filter_user_placeholder=搜尋使用者 +issues.filter_user_no_select=所有使用者 issues.filter_type=類型 issues.filter_type.all_issues=所有問題 issues.filter_type.assigned_to_you=指派給您的 @@ -1305,6 +1564,7 @@ issues.next=下一頁 issues.open_title=開放中 issues.closed_title=已關閉 issues.draft_title=草稿 +issues.num_comments_1=%d 則評論 issues.num_comments=%d 則留言 issues.commented_at=`已留言 %s` issues.delete_comment_confirm=您確定要刪除這則留言嗎? @@ -1313,9 +1573,15 @@ issues.context.quote_reply=引用回覆 issues.context.reference_issue=新增問題並參考 issues.context.edit=編輯 issues.context.delete=刪除 +issues.no_content=沒有提供描述。 issues.close=關閉問題 +issues.comment_pull_merged_at=合併提交 %[1]s 到 %[2]s %[3]s +issues.comment_manually_pull_merged_at=手動合併提交 %[1]s 到 %[2]s %[3]s +issues.close_comment_issue=留言並關閉 issues.reopen_issue=重新開放 +issues.reopen_comment_issue=留言並重新開放 issues.create_comment=留言 +issues.comment.blocked_user=無法建立或編輯留言,因為您被發文者或儲存庫擁有者封鎖。 issues.closed_at=`關閉了這個問題 %[2]s` issues.reopened_at=`重新開放了這個問題 %[2]s` issues.commit_ref_at=`在提交中關聯了這個問題 %[2]s` @@ -1327,8 +1593,17 @@ issues.ref_closed_from=`關閉了這個問題 %[4]s 重新開放了這個問題 %[4]s %[2]s` issues.ref_from=`自 %[1]s` issues.author=作者 +issues.author_helper=此使用者是作者。 issues.role.owner=擁有者 +issues.role.owner_helper=此使用者是此儲存庫的擁有者。 issues.role.member=普通成員 +issues.role.member_helper=此使用者是擁有此儲存庫的組織成員。 +issues.role.collaborator=協作者 +issues.role.collaborator_helper=此使用者已被邀請協作此儲存庫。 +issues.role.first_time_contributor=首次貢獻者 +issues.role.first_time_contributor_helper=此使用者是首次對此儲存庫進行貢獻。 +issues.role.contributor=貢獻者 +issues.role.contributor_helper=此使用者之前已提交過此儲存庫。 issues.re_request_review=再次請求審核 issues.is_stale=經過此審核以後,此合併請求有被修改 issues.remove_request_review=移除審核請求 @@ -1343,6 +1618,9 @@ issues.label_title=名稱 issues.label_description=描述 issues.label_color=顏色 issues.label_exclusive=互斥 +issues.label_archive=封存標籤 +issues.label_archived_filter=顯示封存標籤 +issues.label_archive_tooltip=封存標籤在搜尋標籤時預設會被排除在建議之外。 issues.label_exclusive_desc=請以此格式命名標籤: scope/item,使它和其他 scope/ (相同範圍) 標籤互斥。 issues.label_exclusive_warning=在編輯問題及合併請求的標籤時,將會刪除任何有相同範圍的標籤。 issues.label_count=%d 個標籤 @@ -1390,11 +1668,25 @@ issues.delete.title=刪除此問題? issues.delete.text=您真的要刪除此問題嗎?(這將會永久移除所有內容。若您還想保留,請考慮改為關閉它。) issues.tracker=時間追蹤 +issues.timetracker_timer_start=開始計時 +issues.timetracker_timer_stop=停止計時 +issues.timetracker_timer_discard=捨棄計時 +issues.timetracker_timer_manually_add=手動新增時間 +issues.time_estimate_set=設定預估時間 +issues.time_estimate_display=預估時間:%s +issues.change_time_estimate_at=將預估時間更改為 %s %s +issues.remove_time_estimate_at=移除預估時間 %s +issues.time_estimate_invalid=預估時間格式無效 +issues.start_tracking_history=`開始工作 %s` issues.tracker_auto_close=當這個問題被關閉時,自動停止計時器 issues.tracking_already_started=`您已在另一個問題上開始時間追蹤!` +issues.stop_tracking_history=`結束工作 %s` +issues.cancel_tracking_history=`取消時間追蹤 %s` issues.del_time=刪除此時間記錄 +issues.add_time_history=`加入了花費時間 %s` issues.del_time_history=`刪除了花費時間 %s` +issues.add_time_manually=手動新增時間 issues.add_time_hours=小時 issues.add_time_minutes=分鐘 issues.add_time_sum_to_small=沒有輸入時間。 @@ -1413,6 +1705,7 @@ issues.due_date_form=yyyy年mm月dd日 issues.due_date_form_add=新增截止日期 issues.due_date_form_edit=編輯 issues.due_date_form_remove=移除 +issues.due_date_not_writer=您需要對此儲存庫的寫入權限才能更新問題的截止日期。 issues.due_date_not_set=未設定截止日期。 issues.due_date_added=新增了截止日期 %s %s issues.due_date_modified=將截止日期從 %[2]s 修改為 %[1]s %[3]s @@ -1436,6 +1729,7 @@ issues.dependency.issue_closing_blockedby=此問題被下列問題阻擋而無 issues.dependency.issue_close_blocks=因為此問題的阻擋,下列問題無法被關閉 issues.dependency.pr_close_blocks=因為此合併請求的阻擋,下列問題無法被關閉 issues.dependency.issue_close_blocked=在您關閉此問題以前,您必須先關閉所有阻擋它的問題。 +issues.dependency.issue_batch_close_blocked=無法批次關閉您選擇的問題,因為問題 #%d 還有開放中的先決條件。 issues.dependency.pr_close_blocked=在您合併以前,您必須先關閉所有阻擋它的問題。 issues.dependency.blocks_short=阻擋 issues.dependency.blocked_by_short=先決於 @@ -1452,6 +1746,7 @@ issues.dependency.add_error_dep_not_same_repo=這兩個問題必須在同一個 issues.review.self.approval=您不能核可自己的合併請求。 issues.review.self.rejection=您不能對自己的合併請求提出請求變更。 issues.review.approve=核可了這些變更 %s +issues.review.comment=已審核 %s issues.review.dismissed=取消 %s 的審核 %s issues.review.dismissed_label=已取消 issues.review.left_comment=留下了回應 @@ -1462,9 +1757,13 @@ issues.review.add_review_request=請求了 %s 來審核 %s issues.review.remove_review_request=移除了對 %s 的審核請求 %s issues.review.remove_review_request_self=拒絕了審核 %s issues.review.pending=待處理 +issues.review.pending.tooltip=目前其他使用者還不能看見此留言。要送出您待定的留言請在頁面最上方選擇「%s」->「%s/%s/%s」。 issues.review.review=審核 issues.review.reviewers=審核者 issues.review.outdated=過時的 +issues.review.outdated_description=此留言發表後內容已變更 +issues.review.option.show_outdated_comments=顯示過時的留言 +issues.review.option.hide_outdated_comments=隱藏過時的留言 issues.review.show_outdated=顯示過時的 issues.review.hide_outdated=隱藏過時的 issues.review.show_resolved=顯示已解決 @@ -1473,6 +1772,11 @@ issues.review.resolve_conversation=解決對話 issues.review.un_resolve_conversation=取消解決對話 issues.review.resolved_by=標記了此對話為已解決 issues.review.commented=留言 +issues.review.official=核准 +issues.review.requested=審核待處理 +issues.review.rejected=請求變更 +issues.review.stale=核准後已更新 +issues.review.unofficial=未計入的核准 issues.assignee.error=因為未預期的錯誤,未能成功加入所有負責人。 issues.reference_issue.body=內容 issues.content_history.deleted=刪除 @@ -1488,6 +1792,9 @@ compare.compare_head=比較 pulls.desc=啟用合併請求和程式碼審核。 pulls.new=建立合併請求 +pulls.new.blocked_user=無法建立合併請求,因為您被儲存庫擁有者封鎖。 +pulls.new.must_collaborator=您必須是協作者才能建立合併請求。 +pulls.edit.already_changed=無法儲存合併請求的變更。看起來內容已被其他使用者更改。請重新整理頁面並再次嘗試編輯以避免覆蓋他們的變更。 pulls.view=檢視合併請求 pulls.compare_changes=建立合併請求 pulls.allow_edits_from_maintainers=允許維護者編輯 @@ -1504,20 +1811,31 @@ pulls.compare_compare=拉取自 pulls.switch_comparison_type=切換比較類型 pulls.switch_head_and_base=切換 head 和 base pulls.filter_branch=過濾分支 +pulls.show_all_commits=顯示所有提交 +pulls.show_changes_since_your_last_review=顯示自上次審核以來的變更 +pulls.showing_only_single_commit=僅顯示提交 %[1]s 的變更 +pulls.showing_specified_commit_range=僅顯示介於 %[1]s 和 %[2]s 之間的變更 +pulls.select_commit_hold_shift_for_range=選擇提交。按住 Shift 並點擊以選擇範圍 +pulls.review_only_possible_for_full_diff=僅在查看完整差異時才能進行審核 +pulls.filter_changes_by_commit=按提交篩選變更 pulls.nothing_to_compare=這些分支的內容相同,無需建立合併請求。 +pulls.nothing_to_compare_have_tag=所選的分支/標籤相同。 pulls.nothing_to_compare_and_allow_empty_pr=這些分支的內容相同,此合併請求將會是空白的。 pulls.has_pull_request=`已有介於這些分支間的合併請求:%[2]s#%[3]d` pulls.create=建立合併請求 -pulls.title_desc=請求將 %[1]d 次程式碼提交從 %[2]s 合併至 %[3]s +pulls.title_desc=請求將 %[1]d 次提交從 %[2]s 合併至 %[3]s pulls.merged_title_desc=將 %[1]d 次提交從 %[2]s 合併至 %[3]s %[4]s pulls.change_target_branch_at=`將目標分支從 %s 更改為 %s %s` pulls.tab_conversation=對話內容 -pulls.tab_commits=程式碼提交 +pulls.tab_commits=提交 pulls.tab_files=檔案變動 pulls.reopen_to_merge=請重新開放此合併請求以進行合併作業。 pulls.cant_reopen_deleted_branch=無法重新開放此合併請求,因為該分支已刪除。 pulls.merged=已合併 +pulls.merged_success=合併請求已成功合併並關閉 +pulls.closed=關閉合併請求 pulls.manually_merged=手動合併 +pulls.merged_info_text=現在可以刪除分支 %s。 pulls.is_closed=合併請求已被關閉。 pulls.title_wip_desc=`標題用 %s 開頭以避免意外地合併此合併請求。` pulls.cannot_merge_work_in_progress=此合併請求被標記為還在進行中 (WIP)。 @@ -1532,6 +1850,13 @@ pulls.is_empty=在這個分支上的更動都已經套用在目標分支上。 pulls.required_status_check_failed=未通過某些必要的檢查。 pulls.required_status_check_missing=遺失某些必要的檢查。 pulls.required_status_check_administrator=身為系統管理員,您依然可以進行合併。 +pulls.blocked_by_approvals=此合併請求尚未獲得足夠的核可。已獲得 %d 個核可中的 %d 個。 +pulls.blocked_by_approvals_whitelisted=此合併請求尚未獲得足夠的核可。已獲得允許名單中的 %d 個核可中的 %d 個。 +pulls.blocked_by_rejection=此合併請求有官方審核者請求變更。 +pulls.blocked_by_official_review_requests=此合併請求有官方審核請求。 +pulls.blocked_by_outdated_branch=此合併請求被阻擋,因為它已過時。 +pulls.blocked_by_changed_protected_files_1=此合併請求被阻擋,因為它更改了受保護的檔案: +pulls.blocked_by_changed_protected_files_n=此合併請求被阻擋,因為它更改了受保護的檔案: pulls.can_auto_merge_desc=這個合併請求可以自動合併。 pulls.cannot_auto_merge_desc=此合併請求無法自動合併,因為有衝突。 pulls.cannot_auto_merge_helper=手動合併以解決此衝突。 @@ -1566,7 +1891,10 @@ pulls.rebase_conflict_summary=錯誤訊息 pulls.unrelated_histories=合併失敗:要合併的 HEAD 和基底分支沒有共同的歷史。 提示:請嘗試不同的策略 pulls.merge_out_of_date=合併失敗:產生合併時,基底已被更新。提示:再試一次。 pulls.head_out_of_date=合併失敗:產生合併時,head 已被更新。提示:再試一次。 +pulls.has_merged=失敗:此合併請求已被合併,您不能再次合併或更改目標分支。 +pulls.push_rejected=合併失敗:此推送被拒絕。請檢查此儲存庫的 Git Hook。 pulls.push_rejected_summary=完整的拒絕訊息 +pulls.push_rejected_no_message=合併失敗:此推送被拒絕但未提供其他資訊。
請檢查此儲存庫的 Git Hook。 pulls.open_unmerged_pull_exists=`您不能重新開放,因為目前有相同的合併請求 (#%d) 正在進行中。` pulls.status_checking=還在進行一些檢查 pulls.status_checks_success=已通過所有檢查 @@ -1575,6 +1903,8 @@ pulls.status_checks_failure=一些檢查失敗了 pulls.status_checks_error=一些檢查回報了錯誤 pulls.status_checks_requested=必要 pulls.status_checks_details=詳情 +pulls.status_checks_hide_all=隱藏所有檢查 +pulls.status_checks_show_all=顯示所有檢查 pulls.update_branch=以合併更新分支 pulls.update_branch_rebase=以 Rebase 更新分支 pulls.update_branch_success=分支更新成功 @@ -1583,6 +1913,12 @@ pulls.outdated_with_base_branch=相對於基底分支,此分支已過時 pulls.close=關閉合併請求 pulls.closed_at=`關閉了這個合併請求 %[2]s` pulls.reopened_at=`重新開放了這個合併請求 %[2]s` +pulls.cmd_instruction_hint=`檢視 命令列指示。` +pulls.cmd_instruction_checkout_title=檢出 +pulls.cmd_instruction_checkout_desc=從您的專案儲存庫中,檢出一個新分支並測試變更。 +pulls.cmd_instruction_merge_title=合併 +pulls.cmd_instruction_merge_desc=合併變更並在 Gitea 上更新。 +pulls.cmd_instruction_merge_warning=警告:此操作無法合併合併請求,因為未啟用「自動檢測手動合併」 pulls.clear_merge_message=清除合併訊息 pulls.clear_merge_message_hint=清除合併訊息將僅移除提交訊息內容,留下產生的 git 結尾,如「Co-Authored-By …」。 @@ -1601,8 +1937,16 @@ pulls.auto_merge_canceled_schedule_comment=`取消了在通過所有檢查後自 pulls.delete.title=刪除此合併請求? pulls.delete.text=您真的要刪除此合併請求嗎?(這將會永久移除所有內容。若您還想保留,請考慮改為關閉它。) +pulls.recently_pushed_new_branches=您在分支 %[1]s 上推送了 %[2]s +pulls.upstream_diverging_prompt_behind_1=此分支落後 %s %d 次提交 +pulls.upstream_diverging_prompt_behind_n=此分支落後 %s %d 次提交 +pulls.upstream_diverging_prompt_base_newer=基底分支 %s 有新變更 +pulls.upstream_diverging_merge=同步 fork +pull.deleted_branch=(已刪除): %s +pull.agit_documentation=查看 AGit 的文件 +comments.edit.already_changed=無法儲存留言的變更。看起來內容已被其他使用者更改。請重新整理頁面並再次嘗試編輯以避免覆蓋他們的變更 milestones.new=新增里程碑 milestones.closed=於 %s關閉 @@ -1610,6 +1954,8 @@ milestones.update_ago=已更新 %s milestones.no_due_date=暫無截止日期 milestones.open=開啟 milestones.close=關閉 +milestones.new_subheader=里程碑可用來組織問題和追蹤進度。 +milestones.completeness=%d%% 完成 milestones.create=建立里程碑 milestones.title=標題 milestones.desc=描述 @@ -1626,11 +1972,26 @@ milestones.deletion=刪除里程碑 milestones.deletion_desc=刪除里程碑會從所有相關的問題移除它。是否繼續? milestones.deletion_success=里程碑已刪除 milestones.filter_sort.name=名稱 +milestones.filter_sort.earliest_due_data=截止日期由遠到近 +milestones.filter_sort.latest_due_date=截止日期由近到遠 milestones.filter_sort.least_complete=完成度由低到高 milestones.filter_sort.most_complete=完成度由高到低 milestones.filter_sort.most_issues=問題由多到少 milestones.filter_sort.least_issues=問題由少到多 +signing.will_sign=此提交將使用金鑰「%s」簽署。 +signing.wont_sign.error=檢查提交是否可以簽署時發生錯誤。 +signing.wont_sign.nokey=沒有可用的金鑰來簽署此提交。 +signing.wont_sign.never=提交從不簽署。 +signing.wont_sign.always=提交總是簽署。 +signing.wont_sign.pubkey=提交不會被簽署,因為您的帳戶沒有關聯的公鑰。 +signing.wont_sign.twofa=您必須啟用雙因素驗證才能簽署提交。 +signing.wont_sign.parentsigned=提交不會被簽署,因為父提交未簽署。 +signing.wont_sign.basesigned=合併不會被簽署,因為基底提交未簽署。 +signing.wont_sign.headsigned=合併不會被簽署,因為 head 提交未簽署。 +signing.wont_sign.commitssigned=合併不會被簽署,因為所有相關的提交都未簽署。 +signing.wont_sign.approved=合併不會被簽署,因為 PR 未被核准。 +signing.wont_sign.not_signed_in=你還沒有登入。 ext_wiki=存取外部 Wiki ext_wiki.desc=連結外部 Wiki。 @@ -1660,8 +2021,13 @@ wiki.reserved_page=「%s」是保留的 Wiki 頁面名稱。 wiki.pages=所有頁面 wiki.last_updated=最後更新於 %s wiki.page_name_desc=輸入此 Wiki 頁面的名稱。一些特殊名稱有:「Home」、「_Sidebar」、「_Footer」等。 +wiki.original_git_entry_tooltip=檢視原始 Git 檔案而不是使用友善連結。 activity=動態 +activity.navbar.pulse=脈搏 +activity.navbar.code_frequency=程式碼頻率 +activity.navbar.contributors=貢獻者 +activity.navbar.recent_commits=最近提交 activity.period.filter_label=期間: activity.period.daily=1 天 activity.period.halfweekly=3 天 @@ -1727,7 +2093,10 @@ activity.git_stats_and_deletions=和 activity.git_stats_deletion_1=刪除 %d 行 activity.git_stats_deletion_n=刪除 %d 行 +contributors.contribution_type.filter_label=貢獻類型: contributors.contribution_type.commits=提交歷史 +contributors.contribution_type.additions=新增 +contributors.contribution_type.deletions=刪除 settings=設定 settings.desc=設定是您可以管理儲存庫設定的地方 @@ -1742,7 +2111,20 @@ settings.hooks=Webhook settings.githooks=Git Hook settings.basic_settings=基本設定 settings.mirror_settings=鏡像設定 +settings.mirror_settings.docs=設定您的儲存庫自動同步其他儲存庫的提交、標籤、分支。 +settings.mirror_settings.docs.disabled_pull_mirror.instructions=設定您的專案自動將提交、標籤、分支推送到其他儲存庫。您的網站管理員已停用了拉取鏡像。 +settings.mirror_settings.docs.disabled_push_mirror.instructions=設定您的專案自動從其他儲存庫拉取提交、標籤、分支。 +settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning=現在這個功能只能從「遷移外部儲存庫」進行設定,詳情請參考: +settings.mirror_settings.docs.disabled_push_mirror.info=您的網站管理員已停用了推送鏡像。 +settings.mirror_settings.docs.no_new_mirrors=您的儲存庫正在鏡像變更到或從另一個儲存庫。請注意,您目前無法建立任何新的鏡像。 +settings.mirror_settings.docs.can_still_use=雖然您無法修改現有的鏡像或建立新的鏡像,但您仍然可以使用現有的鏡像。 +settings.mirror_settings.docs.pull_mirror_instructions=設定拉取鏡像請參考: +settings.mirror_settings.docs.more_information_if_disabled=您可以在這裡找到有關推送和拉取鏡像的更多資訊: +settings.mirror_settings.docs.doc_link_title=如何鏡像儲存庫? +settings.mirror_settings.docs.doc_link_pull_section=文件中的「從遠端儲存庫拉取」部分。 +settings.mirror_settings.docs.pulling_remote_title=正在從遠端儲存庫拉取 settings.mirror_settings.mirrored_repository=已鏡像的儲存庫 +settings.mirror_settings.pushed_repository=推送的儲存庫 settings.mirror_settings.direction=方向 settings.mirror_settings.direction.pull=拉取 settings.mirror_settings.direction.push=推送 @@ -1750,15 +2132,23 @@ settings.mirror_settings.last_update=最近更新時間 settings.mirror_settings.push_mirror.none=未設定推送鏡像 settings.mirror_settings.push_mirror.remote_url=Git 遠端儲存庫 URL settings.mirror_settings.push_mirror.add=新增推送鏡像 +settings.mirror_settings.push_mirror.edit_sync_time=編輯鏡像同步間隔 settings.sync_mirror=立即同步 +settings.pull_mirror_sync_in_progress=目前正在從遠端 %s 拉取變更。 +settings.push_mirror_sync_in_progress=目前正在推送變更到遠端 %s。 settings.site=網站 settings.update_settings=更新設定 +settings.update_mirror_settings=更新鏡像設定 +settings.branches.switch_default_branch=切換預設分支 settings.branches.update_default_branch=更新預設分支 settings.branches.add_new_rule=加入新規則 settings.advanced_settings=進階設定 settings.wiki_desc=啟用儲存庫 Wiki settings.use_internal_wiki=使用內建 Wiki +settings.default_wiki_branch_name=預設 Wiki 分支名稱 +settings.default_wiki_everyone_access=登入使用者的預設存取權限: +settings.failed_to_change_default_wiki_branch=更改預設 Wiki 分支失敗。 settings.use_external_wiki=使用外部 Wiki settings.external_wiki_url=外部 Wiki 連結 settings.external_wiki_url_error=外部 Wiki 網址不是有效的網址。 @@ -1788,6 +2178,10 @@ settings.pulls.default_delete_branch_after_merge=預設在合併後刪除合併 settings.pulls.default_allow_edits_from_maintainers=預設允許維護者進行編輯 settings.releases_desc=啟用儲存庫版本發佈 settings.packages_desc=啟用儲存庫套件註冊中心 +settings.projects_desc=啟用儲存庫專案 +settings.projects_mode_desc=專案模式 (顯示哪種類型的專案) +settings.projects_mode_repo=僅儲存庫專案 +settings.projects_mode_owner=僅使用者或組織專案 settings.projects_mode_all=所有專案 settings.actions_desc=啟用儲存庫 Actions settings.admin_settings=管理員設定 @@ -1814,14 +2208,17 @@ settings.convert_fork_succeed=此 fork 已轉換成普通儲存庫。 settings.transfer=轉移儲存庫所有權 settings.transfer.rejected=儲存庫轉移被拒絕。 settings.transfer.success=儲存庫已成功轉移。 +settings.transfer.blocked_user=無法轉移儲存庫,因為您被新擁有者封鎖。 settings.transfer_abort=取消轉移 settings.transfer_abort_invalid=您無法取消不存在的儲存庫轉移。 +settings.transfer_abort_success=成功取消轉移儲存庫至 %s。 settings.transfer_desc=將此儲存庫轉移給其他使用者或受您管理的組織。 settings.transfer_form_title=輸入儲存庫名稱以確認: settings.transfer_in_progress=目前正在進行轉移。如果您想要將此儲存庫轉移給其他使用者,請取消他。 settings.transfer_notices_1=- 如果將此儲存庫轉移給個別使用者,您將會失去此儲存庫的存取權。 settings.transfer_notices_2=- 如果將此儲存庫轉移到您(共同)擁有的組織,您將能繼續保有此儲存庫的存取權。 settings.transfer_notices_3=- 如果此儲存庫為私有儲存庫且將轉移給個別使用者,此動作確保該使用者至少擁有讀取權限 (必要時將會修改權限)。 +settings.transfer_notices_4=- 如果此儲存庫屬於組織,並且您將其轉移給另一個組織或個人,您將失去儲存庫問題與組織專案板之間的連結。 settings.transfer_owner=新擁有者 settings.transfer_perform=進行轉移 settings.transfer_started=此儲存庫已被標記為待轉移且正在等待「%s」的確認 @@ -1851,12 +2248,14 @@ settings.delete_notices_2=- 此操作將永久刪除 %s 儲存 settings.delete_notices_fork_1=- 在此儲存庫刪除後,它的 fork 將會變成獨立儲存庫。 settings.deletion_success=這個儲存庫已被刪除。 settings.update_settings_success=已更新儲存庫的設定。 +settings.update_settings_no_unit=儲存庫應允許至少某種形式的互動。 settings.confirm_delete=刪除儲存庫 settings.add_collaborator=增加協作者 settings.add_collaborator_success=成功增加協作者! settings.add_collaborator_inactive_user=無法將未啟用的使用者加入為協作者。 settings.add_collaborator_owner=無法將擁有者加入為協作者。 settings.add_collaborator_duplicate=此協作者早已被加入此儲存庫。 +settings.add_collaborator.blocked_user=協作者被儲存庫擁有者封鎖或反之亦然。 settings.delete_collaborator=移除 settings.collaborator_deletion=移除協作者 settings.collaborator_deletion_desc=移除協作者將拒絕他存取此儲存庫。是否繼續? @@ -1879,12 +2278,14 @@ settings.webhook_deletion_desc=移除 Webhook 將刪除它的設定及傳送記 settings.webhook_deletion_success=Webhook 已移除。 settings.webhook.test_delivery=傳送測試資料 settings.webhook.test_delivery_desc=使用假事件測試此 Webhook。 +settings.webhook.test_delivery_desc_disabled=要使用假事件測試此 Webhook,請啟用它。 settings.webhook.request=請求 settings.webhook.response=回應 settings.webhook.headers=標頭 settings.webhook.payload=內容 settings.webhook.body=本體 settings.webhook.replay.description=再次執行此 Webhook。 +settings.webhook.replay.description_disabled=要重新執行此 Webhook,請啟用它。 settings.webhook.delivery.success=已將事件加入到傳送佇列,可能需要等待幾分鐘才會出現於傳送紀錄。 settings.githooks_desc=Git Hook 是 Git 本身提供的功能。您可以在下方編輯 hook 檔案以設定自訂作業。 settings.githook_edit_desc=如果 Hook 未啟動,則會顯示範例文件中的內容。如果想要刪除某個 Hook,則送出空白內容即可。 @@ -1894,8 +2295,8 @@ settings.update_githook=更新 Hook settings.add_webhook_desc=Gitea 會發送含有指定 Content Type 的 POST 請求到目標 URL。 在 Webhook 指南閱讀更多內容。 settings.payload_url=目標 URL settings.http_method=HTTP 請求方法 -settings.content_type=POST Content Type -settings.secret=Secret +settings.content_type=POST 內容類型 +settings.secret=密鑰 settings.slack_username=服務名稱 settings.slack_icon_url=圖示 URL settings.slack_color=顏色 @@ -1917,6 +2318,7 @@ settings.event_wiki_desc=建立、重新命名、編輯、刪除 Wiki 頁面。 settings.event_release=版本發布 settings.event_release_desc=在儲存庫中發布、更新或刪除版本。 settings.event_push=推送 +settings.event_force_push=強制推送 settings.event_push_desc=推送到儲存庫。 settings.event_repository=儲存庫 settings.event_repository_desc=建立或刪除儲存庫。 @@ -1946,9 +2348,14 @@ settings.event_pull_request_review=合併請求審核 settings.event_pull_request_review_desc=核准、退回或提出審核留言。 settings.event_pull_request_sync=合併請求同步 settings.event_pull_request_sync_desc=合併請求同步。 +settings.event_pull_request_review_request=合併請求審核請求 +settings.event_pull_request_review_request_desc=合併請求審核請求或審核請求已移除。 +settings.event_pull_request_approvals=合併請求核可 +settings.event_pull_request_merge=合併請求合併 settings.event_package=套件 settings.event_package_desc=套件已在儲存庫中建立或刪除。 settings.branch_filter=分支篩選 +settings.branch_filter_desc=推送、建立分支、刪除分支事件的白名單,請使用 glob 比對模式。如果留白或輸入*,所有分支的事件都會被回報。語法參見 github.com/gobwas/glob。範例:master, {master,release*}。 settings.authorization_header=Authorization 標頭 settings.authorization_header_desc=存在時將將包含此 Authorization 標頭在請求中。例: %s。 settings.active=啟用 @@ -1994,26 +2401,72 @@ settings.deploy_key_deletion=刪除部署金鑰 settings.deploy_key_deletion_desc=移除部署金鑰將拒絕它存取此儲存庫。是否繼續? settings.deploy_key_deletion_success=部署金鑰已移除。 settings.branches=分支 +settings.protected_branch=分支保護 settings.protected_branch.save_rule=儲存規則 settings.protected_branch.delete_rule=刪除規則 +settings.protected_branch_can_push=允許推送? +settings.protected_branch_can_push_yes=你可以推送 +settings.protected_branch_can_push_no=你不能推送 +settings.branch_protection=%s 的分支保護 settings.protect_this_branch=啟用分支保護 settings.protect_this_branch_desc=防止刪除分支,並限制 Git 推送與合併到分支。 settings.protect_disable_push=停用推送 settings.protect_disable_push_desc=不允許推送到此分支。 +settings.protect_disable_force_push=停用強制推送 +settings.protect_disable_force_push_desc=不允許強制推送到此分支。 settings.protect_enable_push=啟用推送 settings.protect_enable_push_desc=任何擁有寫入權限的使用者將可推送至該分支(但不可使用force push)。 +settings.protect_enable_force_push_all=啟用強制推送 +settings.protect_enable_force_push_all_desc=任何有推送權限的人都可以強制推送到此分支。 +settings.protect_enable_force_push_allowlist=允許名單限制強制推送 +settings.protect_enable_force_push_allowlist_desc=只有推送權限的允許名單內的使用者或團隊可以強制推送到此分支。 settings.protect_enable_merge=啟用合併 settings.protect_enable_merge_desc=任何有寫入權限的人都可將合併請求合併到此分支 +settings.protect_whitelist_committers=使用白名單控管推送 +settings.protect_whitelist_committers_desc=僅允許白名單內的使用者或團隊推送至該分支(但不可使用force push)。 +settings.protect_whitelist_deploy_keys=將擁有寫入權限的部署金鑰加入白名單。 +settings.protect_whitelist_users=允許推送的使用者: +settings.protect_whitelist_teams=允許推送的團隊: +settings.protect_force_push_allowlist_users=允許強制推送的使用者: +settings.protect_force_push_allowlist_teams=允許強制推送的團隊: +settings.protect_force_push_allowlist_deploy_keys=允許強制推送的部署金鑰。 +settings.protect_merge_whitelist_committers=啟用合併白名單 +settings.protect_merge_whitelist_committers_desc=僅允許白名單內的使用者或團隊將合併請求合併至該分支。 +settings.protect_merge_whitelist_users=允許合併的使用者: +settings.protect_merge_whitelist_teams=允許合併的團隊: settings.protect_check_status_contexts=啟用狀態檢查 +settings.protect_status_check_patterns=狀態檢查模式: +settings.protect_status_check_patterns_desc=輸入模式以指定其他分支在合併到受此規則保護的分支前必須通過的狀態檢查。每行指定一個模式,模式不得為空白。 +settings.protect_check_status_contexts_desc=合併前必須先通過狀態檢查。選擇合併前必須通過的檢查。啟用時,必須先將提交推送到另一個分支,通過狀態檢查後再合併或直接推送到符合規則的分支。如果未選擇任何項目,最一個提交必將成功通過狀態檢查。 settings.protect_check_status_contexts_list=此儲存庫一週內曾進行過狀態檢查 +settings.protect_status_check_matched=符合 +settings.protect_invalid_status_check_pattern=狀態檢查模式無效: 「%s」。 +settings.protect_no_valid_status_check_patterns=沒有有效的狀態檢查模式。 settings.protect_required_approvals=需要的核可數量: +settings.protect_required_approvals_desc=只有在獲得足夠數量的核可後才能進行合併。 +settings.protect_approvals_whitelist_enabled=使用白名單控管審核人員與團隊 +settings.protect_approvals_whitelist_enabled_desc=只有白名單內的使用者與團隊會被計入需要的核可數量。未使用白名單時,將計算任何有寫入權限之人的核可。 +settings.protect_approvals_whitelist_users=審核者白名單: +settings.protect_approvals_whitelist_teams=審核團隊白名單: settings.dismiss_stale_approvals=捨棄過時的核可 settings.dismiss_stale_approvals_desc=當新的提交有修改到合併請求的內容,並被推送到此分支時,將捨棄舊的核可。 +settings.ignore_stale_approvals=忽略過時的核可 +settings.ignore_stale_approvals_desc=不計算在較舊提交上進行的核可(過時的審核)作為合併請求的核可數量。如果過時的審核已經被捨棄,則無關緊要。 settings.require_signed_commits=僅接受經簽署的提交 settings.require_signed_commits_desc=拒絕未經簽署或未經驗證的提交推送到此分支。 settings.protect_branch_name_pattern=受保護的分支名稱模式 +settings.protect_branch_name_pattern_desc=受保護的分支名稱模式。請參閱 文件 以了解模式語法。範例:main, release/** +settings.protect_patterns=模式 settings.protect_protected_file_patterns=受保護的檔案模式 (以分號區隔「;」): +settings.protect_protected_file_patterns_desc=即便使用者有權限新增、修改、刪除此分支的檔案,仍不允許直接修改受保護的檔案。可以用半形分號「;」分隔多個模式。請於 github.com/gobwas/glob 文件查看模式格式。範例: .drone.yml, /docs/**/*.txt。 settings.protect_unprotected_file_patterns=未受保護的檔案模式 (以分號區隔「;」): +settings.protect_unprotected_file_patterns_desc=當使用者有寫入權限時,可繞過推送限制,直接修改未受保護的檔案。可以用半形分號「;」分隔多個模式。請於 github.com/gobwas/glob 文件查看模式格式。範例: .drone.yml, /docs/**/*.txt。 +settings.add_protected_branch=啟用保護 +settings.delete_protected_branch=停用保護 +settings.update_protect_branch_success=已更新「%s」的分支保護規則。 +settings.remove_protected_branch_success=已刪除「%s」的分支保護規則。 +settings.remove_protected_branch_failed=刪除分支保護規則「%s」失敗。 +settings.protected_branch_deletion=停用分支保護 settings.protected_branch_deletion_desc=停用分支保護將允許有寫入權限的使用者推送至該分支,是否繼續? settings.block_rejected_reviews=有退回的審核時阻擋合併 settings.block_rejected_reviews_desc=如果官方審核人員提出變更請求,即使有足夠的核可也不允許進行合併。 @@ -2021,6 +2474,8 @@ settings.block_on_official_review_requests=有官方的審核請求時阻擋合 settings.block_on_official_review_requests_desc=如果有官方的審核請求時,即使有足夠的核可也不允許進行合併。 settings.block_outdated_branch=如果合併請求已經過時則阻擋合併 settings.block_outdated_branch_desc=當 head 分支落後於基礎分支時不得合併。 +settings.block_admin_merge_override=管理員必須遵守分支保護規則 +settings.block_admin_merge_override_desc=管理員必須遵守分支保護規則,不能繞過它。 settings.default_branch_desc=請選擇用來提交程式碼和合併請求的預設分支。 settings.merge_style_desc=合併方式 settings.default_merge_style_desc=預設合併方式 @@ -2039,18 +2494,39 @@ settings.tags.protection.allowed.teams=允許的團隊 settings.tags.protection.allowed.noone=無 settings.tags.protection.create=保護標籤 settings.tags.protection.none=沒有受保護的標籤。 -settings.bot_token=Bot Token -settings.chat_id=Chat ID -settings.matrix.homeserver_url=Homeserver 網址 +settings.tags.protection.pattern.description=您可以使用單一名稱或 glob 模式或正則表達式來匹配多個標籤。詳情請參閱 受保護標籤指南。 +settings.bot_token=機器人 Token +settings.chat_id=聊天 ID +settings.thread_id=線程 ID +settings.matrix.homeserver_url=主伺服器網址 settings.matrix.room_id=聊天室 ID settings.matrix.message_type=訊息類型 +settings.visibility.private.button=設為私人 +settings.visibility.private.text=將可見性更改為私人不僅會使儲存庫僅對允許的成員可見,還可能會移除它與 fork、關注者和星標之間的關係。 +settings.visibility.private.bullet_title=更改可見性為私人將: +settings.visibility.private.bullet_one=使儲存庫僅對允許的成員可見。 +settings.visibility.private.bullet_two=可能會移除它與 fork關注者星標 之間的關係。 +settings.visibility.public.button=設為公開 +settings.visibility.public.text=將可見性更改為公開將使儲存庫對任何人可見。 +settings.visibility.public.bullet_title=更改可見性為公開將: +settings.visibility.public.bullet_one=使儲存庫對任何人可見。 +settings.visibility.success=儲存庫可見性已更改。 +settings.visibility.error=嘗試更改儲存庫可見性時發生錯誤。 +settings.visibility.fork_error=無法更改 fork 儲存庫的可見性。 settings.archive.button=封存儲存庫 settings.archive.header=封存本儲存庫 +settings.archive.text=封存儲存庫將使其完全變為唯讀。它將從儀表板中隱藏。沒有人(甚至包括您!)將能夠進行新的提交,或打開任何問題或合併請求。 settings.archive.success=此儲存庫已被封存 settings.archive.error=嘗試封存儲存庫時發生錯誤。查看日誌檔以獲得更多資訊。 settings.archive.error_ismirror=無法封存鏡像儲存庫。 settings.archive.branchsettings_unavailable=已封存的儲存庫無法使用分支設定。 settings.archive.tagsettings_unavailable=已封存的儲存庫無法使用標籤設定。 +settings.archive.mirrors_unavailable=如果儲存庫已封存,則無法使用鏡像。 +settings.unarchive.button=取消封存儲存庫 +settings.unarchive.header=取消封存此儲存庫 +settings.unarchive.text=取消封存儲存庫將恢復其接收提交和推送的能力,以及新問題和合併請求。 +settings.unarchive.success=儲存庫已成功取消封存。 +settings.unarchive.error=嘗試取消封存儲存庫時發生錯誤。查看日誌檔以獲得更多資訊。 settings.update_avatar_success=已更新儲存庫的大頭貼。 settings.lfs=LFS settings.lfs_filelist=存放在本儲存庫的 LFS 檔案 @@ -2115,8 +2591,9 @@ diff.file_suppressed_line_too_long=檔案差異因為一行或多行太長而無 diff.too_many_files=本差異變更的檔案數量過多導致部分檔案未顯示 diff.show_more=顯示更多 diff.load=載入差異 -diff.generated=generated -diff.vendored=vendored +diff.generated=已產生 +diff.vendored=已供應 +diff.comment.add_line_comment=新增行評論 diff.comment.placeholder=留言... diff.comment.add_single_comment=加入單獨的留言 diff.comment.add_review_comment=新增留言 @@ -2147,6 +2624,7 @@ release.new_release=發布新版本 release.draft=草稿 release.prerelease=預發布版本 release.stable=穩定 +release.latest=最新 release.compare=比較 release.edit=編輯 release.ahead.commits=%d 次提交 @@ -2160,7 +2638,9 @@ release.target=目標分支 release.tag_helper=新增或選擇現有的標籤。 release.tag_helper_new=新標籤,將在目標上建立此標籤。 release.tag_helper_existing=現有的標籤。 +release.title=版本標題 release.title_empty=標題不可為空。 +release.message=描述此版本 release.prerelease_desc=標記為 Pre-Release release.prerelease_helper=標記此版本不適合生產使用。 release.cancel=取消 @@ -2170,6 +2650,7 @@ release.edit_release=更新發布 release.delete_release=刪除發布 release.delete_tag=刪除標籤 release.deletion=刪除發布 +release.deletion_desc=刪除版本發布只會將其從 Gitea 中移除。它不會影響 Git 標籤、儲存庫的內容或其歷史。是否繼續? release.deletion_success=已刪除此版本發布。 release.deletion_tag_desc=即將從儲存庫移除此標籤。儲存庫內容和歷史將保持不變,是否繼續? release.deletion_tag_success=已刪除此標籤。 @@ -2189,6 +2670,7 @@ branch.already_exists=已存在名為「%s」的分支。 branch.delete_head=刪除 branch.delete=刪除分支「%s」 branch.delete_html=刪除分支 +branch.delete_desc=刪除分支是永久的。雖然被刪除的分支可能會在實際移除前繼續存在一段時間,但在大多數情況下無法撤銷。是否繼續? branch.deletion_success=已刪除分支「%s」。 branch.deletion_failed=刪除分支「%s」失敗。 branch.delete_branch_has_new_commits=因為合併後已加入了新的提交,「%s」分支無法被刪除。 @@ -2197,6 +2679,7 @@ branch.create_from=從「%s」 branch.create_success=已建立分支「%s」。 branch.branch_already_exists=此儲存庫已有名為「%s」的分支。 branch.branch_name_conflict=分支名稱「%s」與現有分支「%s」衝突。 +branch.tag_collision=無法建立「%s」分支,因為此儲存庫中已有同名的標籤。 branch.deleted_by=由 %s 刪除 branch.restore_success=已還原分支「%s」。 branch.restore_failed=還原分支「%s」失敗。 @@ -2204,10 +2687,13 @@ branch.protected_deletion_failed=分支「%s」已被保護,不能刪除。 branch.default_deletion_failed=分支「%s」為預設分支,不能刪除。 branch.restore=還原分支「%s」 branch.download=下載分支「%s」 +branch.rename=重新命名分支「%s」 branch.included_desc=此分支是預設分支的一部分 branch.included=包含 branch.create_new_branch=從下列分支建立分支: branch.confirm_create_branch=建立分支 +branch.warning_rename_default_branch=您正在重新命名預設分支。 +branch.rename_branch_to=重新命名「%s」為: branch.confirm_rename_branch=重新命名分支 branch.create_branch_operation=建立分支 branch.new_branch=建立新分支 @@ -2223,6 +2709,8 @@ tag.create_success=已建立標籤「%s」。 topic.manage_topics=管理主題 topic.done=完成 +topic.count_prompt=您最多能選擇 25 個主題 +topic.format_prompt=主題必須以字母或數字開頭,可以包含破折號 ('-') 和點 ('.'),最多可以有 35 個字元。字母必須是小寫。 find_file.go_to_file=移至檔案 find_file.no_matching=找不到符合的檔案 @@ -2230,8 +2718,16 @@ find_file.no_matching=找不到符合的檔案 error.csv.too_large=無法渲染此檔案,因為它太大了。 error.csv.unexpected=無法渲染此檔案,因為它包含了未預期的字元,於第 %d 行第 %d 列。 error.csv.invalid_field_count=無法渲染此檔案,因為它第 %d 行的欄位數量有誤。 +error.broken_git_hook=此儲存庫的 Git hooks 似乎已損壞。請按照 文件 進行修復,然後推送一些提交以刷新狀態。 [graphs] +component_loading=正在載入 %s... +component_loading_failed=無法載入 %s +component_loading_info=這可能需要一點時間… +component_failed_to_load=發生意外錯誤。 +code_frequency.what=程式碼頻率 +contributors.what=貢獻 +recent_commits.what=最近提交 [org] org_name_holder=組織名稱 @@ -2257,11 +2753,13 @@ team_unit_desc=允許存取的儲存庫區域 team_unit_disabled=(已停用) form.name_reserved=「%s」是保留的組織名稱。 +form.name_pattern_not_allowed=組織名稱不可包含字元「%s」。 form.create_org_not_allowed=此帳號禁止建立組織。 settings=設定 settings.options=組織 settings.full_name=組織全名 +settings.email=聯絡電子郵件 settings.website=官方網站 settings.location=所在地區 settings.permission=權限 @@ -2275,6 +2773,7 @@ settings.visibility.private_shortname=私有 settings.update_settings=更新設定 settings.update_setting_success=組織設定已更新。 +settings.change_orgname_prompt=注意:更改組織名稱將同時更改組織的 URL 並釋放舊名稱。 settings.change_orgname_redirect_prompt=舊的名稱被領用前,會重新導向新名稱。 settings.update_avatar_success=已更新組織的大頭貼。 settings.delete=刪除組織 @@ -2342,6 +2841,7 @@ teams.add_nonexistent_repo=您嘗試新增的儲存庫不存在,請先建立 teams.add_duplicate_users=使用者已經是團隊成員了。 teams.repos.none=這個團隊沒有可以存取的儲存庫。 teams.members.none=這個團隊沒有任何成員。 +teams.members.blocked_user=無法新增使用者,因為它被組織封鎖。 teams.specific_repositories=指定儲存庫 teams.specific_repositories_helper=成員只能存取明確加入此團隊的儲存庫。選擇這個選項不會自動移除透過所有儲存庫加入的儲存庫。 teams.all_repositories=所有儲存庫 @@ -2349,15 +2849,21 @@ teams.all_repositories_helper=團隊擁有可存取所有儲存庫。選擇此 teams.all_repositories_read_permission_desc=這個團隊擁有所有儲存庫讀取 權限:成員可以查看和 Clone 儲存庫。 teams.all_repositories_write_permission_desc=這個團隊擁有所有儲存庫寫入 權限:成員可以讀取和推送到儲存庫。 teams.all_repositories_admin_permission_desc=這個團隊擁有所有儲存庫管理員 權限:成員可以讀取、推送和增加協作者到儲存庫。 +teams.invite.title=您已被邀請加入組織 %s 中的團隊 %s。 teams.invite.by=邀請人 %s teams.invite.description=請點擊下方按鈕加入團隊。 [admin] +maintenance=維護 dashboard=資訊主頁 +self_check=自我檢查 +identity_access=身份與存取 users=使用者帳戶 organizations=組織 +assets=程式碼資產 repositories=儲存庫 hooks=Webhook +integrations=整合 authentication=認證來源 emails=使用者電子信箱 config=組態 @@ -2368,8 +2874,11 @@ monitor=應用監控面版 first_page=首頁 last_page=末頁 total=總計:%d +settings=管理員設定 +dashboard.new_version_hint=現已推出 Gitea %s,您正在執行 %s。詳情請參閱部落格的說明。 dashboard.statistic=摘要 +dashboard.maintenance_operations=維護操作 dashboard.system_status=系統狀態 dashboard.operation_name=作業名稱 dashboard.operation_switch=開關 @@ -2378,11 +2887,13 @@ dashboard.clean_unbind_oauth=清理未綁定的 OAuth 連結 dashboard.clean_unbind_oauth_success=所有未綁定的 OAuth 連結已刪除。 dashboard.task.started=已開始的任務: %[1]s dashboard.task.process=任務: %[1]s +dashboard.task.cancelled=任務: %[1]s 已取消: %[3]s dashboard.task.error=任務中的錯誤: %[1]s: %[3]s dashboard.task.finished=任務: 已完成由 %[2]s 啟動的 %[1]s dashboard.task.unknown=未知的任務: %[1]s dashboard.cron.started=已開始的 Cron: %[1]s dashboard.cron.process=Cron: %[1]s +dashboard.cron.cancelled=Cron: %[1]s 已取消: %[3]s dashboard.cron.error=Cron 中的錯誤: %s: %[3]s dashboard.cron.finished=Cron: %[1]s 已完成 dashboard.delete_inactive_accounts=刪除所有未啟用帳戶 @@ -2392,6 +2903,8 @@ dashboard.delete_repo_archives.started=刪除所有儲存庫存檔的任務已 dashboard.delete_missing_repos=刪除所有遺失 Git 檔案的儲存庫 dashboard.delete_missing_repos.started=刪除所有遺失 Git 檔案的儲存庫的任務已啟動。 dashboard.delete_generated_repository_avatars=刪除產生的儲存庫大頭貼 +dashboard.sync_repo_branches=從 Git 資料同步遺漏的分支到資料庫 +dashboard.sync_repo_tags=從 Git 資料同步標籤到資料庫 dashboard.update_mirrors=更新鏡像 dashboard.repo_health_check=對所有儲存庫進行健康檢查 dashboard.check_repo_stats=檢查所有儲存庫的統計資料 @@ -2406,6 +2919,7 @@ dashboard.reinit_missing_repos=重新初始化所有記錄存在但遺失的 Git dashboard.sync_external_users=同步外部使用者資料 dashboard.cleanup_hook_task_table=清理 hook_task 資料表 dashboard.cleanup_packages=清理已過期的套件 +dashboard.cleanup_actions=清理過期的操作資源 dashboard.server_uptime=服務執行時間 dashboard.current_goroutine=目前的 Goroutines 數量 dashboard.current_memory_usage=目前記憶體使用量 @@ -2435,9 +2949,19 @@ dashboard.total_gc_time=總 GC 暫停時間 dashboard.total_gc_pause=總 GC 暫停時間 dashboard.last_gc_pause=上次 GC 暫停時間 dashboard.gc_times=GC 執行次數 +dashboard.delete_old_actions=從資料庫刪除所有舊行為 +dashboard.delete_old_actions.started=從資料庫刪除所有舊行為的任務已啟動。 dashboard.update_checker=更新檢查器 dashboard.delete_old_system_notices=從資料庫刪除所有舊系統提示 dashboard.gc_lfs=對 LFS meta objects 進行垃圾回收 +dashboard.stop_zombie_tasks=停止殭屍任務 +dashboard.stop_endless_tasks=停止永不停止的任務 +dashboard.cancel_abandoned_jobs=取消已放棄的工作 +dashboard.start_schedule_tasks=啟動動作排程任務 +dashboard.sync_branch.started=分支同步已開始 +dashboard.sync_tag.started=標籤同步已開始 +dashboard.rebuild_issue_indexer=重建問題索引器 +dashboard.sync_repo_licenses=同步儲存庫許可證 users.user_manage_panel=使用者帳戶管理 users.new_account=建立使用者帳戶 @@ -2446,6 +2970,9 @@ users.full_name=全名 users.activated=已啟用 users.admin=管理員 users.restricted=受限 +users.reserved=保留 +users.bot=機器人 (Bot) +users.remote=遠端 users.2fa=兩步驟驗證 users.repos=儲存庫數 users.created=建立時間 @@ -2492,6 +3019,7 @@ users.list_status_filter.is_prohibit_login=禁止登入 users.list_status_filter.not_prohibit_login=允許登入 users.list_status_filter.is_2fa_enabled=已啟用兩步驟驗證 users.list_status_filter.not_2fa_enabled=未啟用兩步驟驗證 +users.details=使用者詳細資訊 emails.email_manage_panel=使用者電子信箱管理 emails.primary=主要 @@ -2504,6 +3032,11 @@ emails.updated=信箱已更新 emails.not_updated=電子信箱更新失敗: %v emails.duplicate_active=此信箱已被其他使用者使用 emails.change_email_header=更新電子信箱屬性 +emails.change_email_text=您確定要更新此電子郵件地址嗎? +emails.delete=刪除電子郵件 +emails.delete_desc=您確定要刪除此電子郵件地址嗎? +emails.deletion_success=電子郵件地址已被刪除。 +emails.delete_primary_email_error=您不能刪除主要的電子郵件地址。 orgs.org_manage_panel=組織管理 orgs.name=名稱 @@ -2519,10 +3052,13 @@ repos.name=名稱 repos.private=私有 repos.issues=問題數 repos.size=大小 +repos.lfs_size=LFS 大小 packages.package_manage_panel=套件管理 packages.total_size=總大小: %s packages.unreferenced_size=未參考大小: %s +packages.cleanup=清理已逾期的資料 +packages.cleanup.success=已成功清理過期的資料 packages.owner=擁有者 packages.creator=建立者 packages.name=名稱 @@ -2533,10 +3069,12 @@ packages.size=大小 packages.published=已發布 defaulthooks=預設 Webhook +defaulthooks.desc=當某些 Gitea 事件觸發時,Webhook 會自動發出 HTTP POST 請求到伺服器。此處定義的 Webhook 是預設值,將會複製到所有新儲存庫中。詳情請參閱 webhooks 指南。 defaulthooks.add_webhook=新增預設 Webhook defaulthooks.update_webhook=更新預設 Webhook systemhooks=系統 Webhook +systemhooks.desc=當某些 Gitea 事件觸發時,Webhook 會自動發出 HTTP POST 請求到伺服器。此處定義的 Webhook 將作用於系統上的所有儲存庫,因此請考慮這可能對效能產生的影響。詳情請參閱 webhooks 指南。 systemhooks.add_webhook=新增系統 Webhook systemhooks.update_webhook=更新系統 Webhook @@ -2629,8 +3167,20 @@ auths.sspi_default_language=使用者預設語言 auths.sspi_default_language_helper=SSPI 認證方法自動建立之使用者的預設語言,留白以自動偵測。 auths.tips=幫助提示 auths.tips.oauth2.general=OAuth2 認證 +auths.tips.oauth2.general.tip=註冊新的 OAuth2 認證時,回調/重定向 URL 應為: auths.tip.oauth2_provider=OAuth2 提供者 +auths.tip.bitbucket=註冊新的 OAuth 客戶端並加入權限「Account - Read」。網址:https://bitbucket.org/account/user//oauth-consumers/new auths.tip.nextcloud=在您的執行個體中,於選單「設定 -> 安全性 -> OAuth 2.0 客戶端」註冊新的 OAuth 客戶端 +auths.tip.dropbox=建立新的 App。網址:https://www.dropbox.com/developers/apps +auths.tip.facebook=註冊新的應用程式並新增產品「Facebook 登入」。網址:https://developers.facebook.com/apps +auths.tip.github=註冊新的 OAuth 應用程式。網址:https://github.com/settings/applications/new +auths.tip.gitlab_new=註冊新的應用程式。網址:https://discordapp.com/developers/applications/me +auths.tip.google_plus=從 Google API 控制台取得 OAuth2 用戶端憑證。網址:https://console.developers.google.com/ +auths.tip.openid_connect=使用 OpenID 連接探索 URL (/.well-known/openid-configuration) 來指定節點 +auths.tip.twitter=建立應用程式並確保有啟用「Allow this application to be used to Sign in with Twitter」。網址:https://dev.twitter.com/apps +auths.tip.discord=註冊新的應用程式。網址:https://discordapp.com/developers/applications/me +auths.tip.gitea=註冊新的 OAuth2 應用程式。指南可在 %s 找到 +auths.tip.yandex=建立新的應用程式,從「Yandex.Passport API」區塊選擇「Access to email address」、「Access to user avatar」和「Access to username, first name and surname, gender」。網址:https://oauth.yandex.com/client/new auths.tip.mastodon=輸入您欲認證的 Mastodon 執行個體的自訂網址 (或使用預設值) auths.edit=修改認證來源 auths.activated=該認證來源已啟用 @@ -2659,6 +3209,7 @@ config.disable_router_log=關閉路由日誌 config.run_user=以使用者名稱執行 config.run_mode=執行模式 config.git_version=Git 版本 +config.app_data_path=應用程式資料路徑 config.repo_root_path=儲存庫目錄 config.lfs_root_path=LFS 根目錄 config.log_file_root_path=日誌路徑 @@ -2733,6 +3284,7 @@ config.mailer_sendmail_timeout=Sendmail 逾時 config.mailer_use_dummy=Dummy config.test_email_placeholder=電子信箱 (例:test@example.com) config.send_test_mail=傳送測試郵件 +config.send_test_mail_submit=傳送 config.test_mail_failed=傳送測試郵件到「%s」時失敗: %v config.test_mail_sent=測試郵件已傳送到「%s」。 @@ -2744,6 +3296,10 @@ config.cache_adapter=Cache 適配器 config.cache_interval=Cache 週期 config.cache_conn=Cache 連接字符串 config.cache_item_ttl=快取項目 TTL +config.cache_test=測試快取 +config.cache_test_failed=測試快取失敗: %v +config.cache_test_slow=快取測試成功,但回應速度慢: %s +config.cache_test_succeeded=快取測試成功,回應時間為 %s config.session_config=Session 組態 config.session_provider=Session 提供者 @@ -2758,6 +3314,7 @@ config.picture_config=圖片和大頭貼組態 config.picture_service=圖片服務 config.disable_gravatar=停用 Gravatar config.enable_federated_avatar=啟用 Federated Avatars +config.open_with_editor_app_help=「開啟方式」編輯器用於克隆選單。如果留空,將使用預設值。展開以查看預設值。 config.git_config=Git 組態 config.git_disable_diff_highlight=停用比較語法高亮 @@ -2772,12 +3329,15 @@ config.git_pull_timeout=Pull 作業逾時 config.git_gc_timeout=GC 作業逾時 config.log_config=日誌組態 +config.logger_name_fmt=記錄器: %s config.disabled_logger=已停用 config.access_log_mode=存取日誌模式 +config.access_log_template=存取日誌範本 config.xorm_log_sql=記錄 SQL config.set_setting_failed=寫入設定值 %s 失敗 +monitor.stats=統計 monitor.cron=Cron 任務 monitor.name=名稱 @@ -2786,11 +3346,16 @@ monitor.next=下次執行時間 monitor.previous=上次執行時間 monitor.execute_times=執行次數 monitor.process=執行中的處理程序 +monitor.stacktrace=堆疊追蹤 +monitor.processes_count=%d 個處理程序 +monitor.download_diagnosis_report=下載診斷報告 monitor.desc=描述 monitor.start=開始時間 monitor.execute_time=已執行時間 monitor.last_execution_result=結果 monitor.process.cancel=結束處理程序 +monitor.process.cancel_desc=結束處理程序可能造成資料遺失 +monitor.process.cancel_notices=結束: %s? monitor.process.children=子程序 monitor.queues=佇列 @@ -2799,14 +3364,19 @@ monitor.queue.name=名稱 monitor.queue.type=類型 monitor.queue.exemplar=型別 monitor.queue.numberworkers=工作者數量 +monitor.queue.activeworkers=活躍工作者 monitor.queue.maxnumberworkers=最大工作者數量 monitor.queue.numberinqueue=佇列中的數量 +monitor.queue.review_add=審查 / 增加工作者 monitor.queue.settings.title=集區設定 +monitor.queue.settings.desc=集區會根據工作者佇列的阻塞情況動態增長。 monitor.queue.settings.maxnumberworkers=最大工作者數量 monitor.queue.settings.maxnumberworkers.placeholder=目前 %[1]d monitor.queue.settings.maxnumberworkers.error=最大工作者數量必須是數字 monitor.queue.settings.submit=更新設定 monitor.queue.settings.changed=已更新設定 +monitor.queue.settings.remove_all_items=全部移除 +monitor.queue.settings.remove_all_items_done=佇列中的所有項目已被移除。 notices.system_notice_list=系統提示 notices.view_detail_header=查看提示細節 @@ -2823,6 +3393,14 @@ notices.desc=描述 notices.op=操作 notices.delete_success=已刪除系統提示。 +self_check.no_problem_found=尚未發現任何問題。 +self_check.startup_warnings=啟動警告: +self_check.database_collation_mismatch=預期資料庫使用排序規則:%s +self_check.database_collation_case_insensitive=資料庫正在使用排序規則 %s,這是一個不區分大小寫的排序規則。雖然 Gitea 可以正常運作,但在某些罕見情況下可能會出現預期外的問題。 +self_check.database_inconsistent_collation_columns=資料庫正在使用排序規則 %s,但這些欄位使用了不匹配的排序規則。這可能會導致一些預期外的問題。 +self_check.database_fix_mysql=對於 MySQL/MariaDB 使用者,您可以使用 "gitea doctor convert" 命令來修復排序規則問題,或者也可以手動使用 "ALTER ... COLLATE ..." SQL 語句來修復問題。 +self_check.database_fix_mssql=對於 MSSQL 使用者,目前您只能手動使用 "ALTER ... COLLATE ..." SQL 語句來修復問題。 +self_check.location_origin_mismatch=當前 URL (%[1]s) 與 Gitea 看到的 URL (%[2]s) 不匹配。如果您使用了反向代理,請確保 "Host" 和 "X-Forwarded-Proto" 標頭設置正確。 [action] create_repo=建立了儲存庫 %s @@ -2850,6 +3428,7 @@ mirror_sync_create=從鏡像同步了新參考 %[3]s%[3]s 刪除了參考 %[2]s approve_pull_request=`核可了 %[3]s#%[2]s` reject_pull_request=`提出了修改建議 %[3]s#%[2]s` +publish_release=`發布了 %[3]s "%[4]s" ` review_dismissed=`取消了 %[4]s%[3]s#%[2]s 的審核` review_dismissed_reason=原因: create_branch=在 %[4]s 中建立了分支 %[3]s @@ -2878,6 +3457,7 @@ raw_minutes=分鐘 [dropzone] default_message=拖放檔案或是點擊此處上傳。 +invalid_input_type=您無法上傳此類型的檔案 file_too_big=檔案大小({{filesize}} MB) 超過了最大允許大小({{maxFilesize}} MB) remove_file=移除文件 @@ -2915,8 +3495,10 @@ error.unit_not_allowed=您未被允許訪問此儲存庫區域 title=套件 desc=管理儲存庫套件。 empty=目前還沒有套件。 +no_metadata=沒有元數據。 empty.documentation=關於套件註冊中心的詳情請參閱說明文件。 empty.repo=已經上傳了一個套件,但是沒有顯示在這裡嗎?打開套件設定並將其連結到這個儲存庫。 +registry.documentation=有關 %s 註冊中心的更多資訊,請參閱說明文件。 filter.type=類型 filter.type.all=所有 filter.no_result=沒有篩選結果。 @@ -2948,6 +3530,8 @@ alpine.repository=儲存庫資訊 alpine.repository.branches=分支 alpine.repository.repositories=儲存庫 alpine.repository.architectures=架構 +arch.registry=在 /etc/pacman.conf 中新增伺服器及相關儲存庫和架構: +arch.install=使用 pacman 同步套件: arch.repository=儲存庫資訊 arch.repository.repositories=儲存庫 arch.repository.architectures=架構 @@ -2973,12 +3557,17 @@ container.layers=映像檔 Layers container.labels=標籤 container.labels.key=鍵 container.labels.value=值 +cran.registry=在您的 Rprofile.site 檔設定此註冊中心: cran.install=執行下列命令安裝此套件: debian.registry=透過下列命令設定此註冊中心: +debian.registry.info=從下列清單選擇$distribution和$component debian.install=執行下列命令安裝此套件: debian.repository=儲存庫資訊 +debian.repository.distributions=發行版 +debian.repository.components=元件 debian.repository.architectures=架構 generic.download=透過下列命令下載套件: +go.install=透過下列命令安裝套件: helm.registry=透過下列命令設定此註冊中心: helm.install=執行下列命令安裝此套件: maven.registry=在您專案的 pom.xml 檔設定此註冊中心: @@ -2993,6 +3582,7 @@ npm.install=執行下列命令以使用 npm 安裝此套件: npm.install2=或將它加到 package.json 檔: npm.dependencies=相依性 npm.dependencies.development=開發相依性 +npm.dependencies.bundle=捆綁相依性 npm.dependencies.peer=Peer 相依性 npm.dependencies.optional=選用相依性 npm.details.tag=標籤 @@ -3000,9 +3590,12 @@ pub.install=執行下列命令以使用 Dart 安裝此套件: pypi.requires=需要 Python pypi.install=執行下列命令以使用 pip 安裝此套件: rpm.registry=透過下列命令設定此註冊中心: +rpm.distros.redhat=在基於 RedHat 的發行版上 +rpm.distros.suse=在基於 SUSE 的發行版上 rpm.install=執行下列命令安裝此套件: rpm.repository=儲存庫資訊 rpm.repository.architectures=架構 +rpm.repository.multiple_groups=此套件在多個群組中可用。 rubygems.install=執行下列命令以使用 gem 安裝此套件: rubygems.install2=或將它加到 Gemfile: rubygems.dependencies.runtime=執行階段相依性 @@ -3026,14 +3619,17 @@ settings.delete.success=已刪除該套件。 settings.delete.error=刪除套件失敗。 owner.settings.cargo.title=Cargo Registry 索引 owner.settings.cargo.initialize=初始化索引 +owner.settings.cargo.initialize.description=使用 Cargo 註冊中心需要一個特殊的索引 Git 儲存庫。使用此選項將會 (重新) 建立儲存庫並自動配置它。 owner.settings.cargo.initialize.error=初始化 Cargo 索引失敗: %v owner.settings.cargo.initialize.success=成功建立了 Cargo 索引。 owner.settings.cargo.rebuild=重建索引 +owner.settings.cargo.rebuild.description=如果索引與儲存的 Cargo 套件不同步,重建索引可能會有幫助。 owner.settings.cargo.rebuild.error=重建 Cargo 索引失敗: %v owner.settings.cargo.rebuild.success=成功重建了 Cargo 索引。 owner.settings.cleanuprules.title=管理清理規則 owner.settings.cleanuprules.add=加入清理規則 owner.settings.cleanuprules.edit=編輯清理規則 +owner.settings.cleanuprules.none=沒有清理規則可用。請參閱說明文件。 owner.settings.cleanuprules.preview=清理規則預覽 owner.settings.cleanuprules.preview.overview=已排定要移除 %d 個套件。 owner.settings.cleanuprules.preview.none=清理規則不符合任何套件。 @@ -3052,6 +3648,7 @@ owner.settings.cleanuprules.success.update=已更新清理規則。 owner.settings.cleanuprules.success.delete=已刪除清理規則。 owner.settings.chef.title=Chef Registry owner.settings.chef.keypair=產生密鑰組 +owner.settings.chef.keypair.description=驗證 Chef 註冊中心需要一個密鑰組。如果您之前已生成過密鑰組,生成新密鑰組將會丟棄舊的密鑰組。 [secrets] secrets=Secret @@ -3066,6 +3663,7 @@ deletion=移除 Secret deletion.description=移除 Secret 是永久的且不可還原,是否繼續? deletion.success=已移除此 Secret。 deletion.failed=移除 Secret 失敗。 +management=Secret 管理 [actions] actions=Actions @@ -3077,6 +3675,7 @@ status.waiting=正在等候 status.running=正在執行 status.success=成功 status.failure=失敗 +status.cancelled=已取消 status.skipped=已略過 status.blocked=已阻塞 @@ -3093,6 +3692,7 @@ runners.labels=標籤 runners.last_online=最後上線時間 runners.runner_title=Runner runners.task_list=最近在此 Runner 上的任務 +runners.task_list.no_tasks=目前還沒有任務。 runners.task_list.run=執行 runners.task_list.status=狀態 runners.task_list.repository=儲存庫 @@ -3113,25 +3713,69 @@ runners.status.idle=閒置 runners.status.active=啟用 runners.status.offline=離線 runners.version=版本 +runners.reset_registration_token=重設註冊 Token runners.reset_registration_token_success=成功重設了 Runner 註冊 Token runs.all_workflows=所有工作流程 runs.commit=提交 +runs.scheduled=已排程 +runs.pushed_by=推送者 runs.invalid_workflow_helper=工作流程設定檔無效。請檢查您的設定檔: %s +runs.no_matching_online_runner_helper=沒有符合標籤的線上 Runner: %s +runs.no_job_without_needs=工作流程必須包含至少一個沒有依賴的工作。 +runs.no_job=工作流程必須包含至少一個工作 +runs.actor=執行者 runs.status=狀態 +runs.actors_no_select=所有執行者 +runs.status_no_select=所有狀態 +runs.no_results=沒有符合的結果。 +runs.no_workflows=目前還沒有工作流程。 +runs.no_workflows.quick_start=不知道如何開始使用 Gitea Actions?請參閱快速入門指南。 +runs.no_workflows.documentation=有關 Gitea Actions 的更多資訊,請參閱文件。 runs.no_runs=工作流程沒有執行過。 +runs.empty_commit_message=(空的提交訊息) +runs.expire_log_message=日誌已被清除,因為它們太舊了。 workflow.disable=停用工作流程 workflow.disable_success=已成功停用工作流程「%s」。 workflow.enable=啟用工作流程 workflow.enable_success=已成功啟用工作流程「%s」。 +workflow.disabled=工作流程已停用。 +workflow.run=執行工作流程 +workflow.not_found=找不到工作流程「%s」。 +workflow.run_success=工作流程「%s」執行成功。 +workflow.from_ref=使用工作流程來自 +workflow.has_workflow_dispatch=此工作流程有一個 workflow_dispatch 事件觸發器。 need_approval_desc=來自 Frok 儲存庫的合併請求需要核可才能執行工作流程。 +variables=變數 +variables.management=變數管理 +variables.creation=新增變數 +variables.none=還沒有任何變數。 +variables.deletion=移除變數 +variables.deletion.description=移除變數是永久的且不可還原,是否繼續? +variables.description=變數會被傳送到某些 Action 且無法以其他方式讀取。 +variables.id_not_exist=ID 為 %d 的變數不存在。 +variables.edit=編輯變數 +variables.deletion.failed=移除變數失敗。 +variables.deletion.success=已刪除變數。 +variables.creation.failed=新增變數失敗。 +variables.creation.success=已新增變數「%s」。 +variables.update.failed=編輯變數失敗。 +variables.update.success=已編輯變數。 [projects] +deleted.display_name=已刪除的專案 +type-1.display_name=個人專案 +type-2.display_name=儲存庫專案 +type-3.display_name=組織專案 [git.filemode] ; Ordered by git filemode value, ascending. E.g. directory has "040000", normal file has "100644", … +directory=目錄 +normal_file=一般檔案 +executable_file=可執行檔 symbolic_link=符號連結 +submodule=子模組 From 195fccd6177d37fabe42d19b4764f5c0fd1b66c0 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 18 Dec 2024 01:51:11 +0100 Subject: [PATCH 04/50] Move eslint config to javascript (#32869) In preparation for migrating to eslint flat config, move the config file to javascript. Additional changes: - `no-undef` is now disabled as it's no longer needed with typescript - `no-restricted-globals` config is simplified --- .eslintrc.cjs | 999 +++++++++++++++++++++++++++++++++++++++++++++++++ .eslintrc.yaml | 967 ----------------------------------------------- 2 files changed, 999 insertions(+), 967 deletions(-) create mode 100644 .eslintrc.cjs delete mode 100644 .eslintrc.yaml diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000000..af744db3e1 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,999 @@ +const restrictedSyntax = ['WithStatement', 'ForInStatement', 'LabeledStatement', 'SequenceExpression']; + +module.exports = { + root: true, + reportUnusedDisableDirectives: true, + ignorePatterns: [ + '/web_src/js/vendor', + '/web_src/fomantic', + '/public/assets/js', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + sourceType: 'module', + ecmaVersion: 'latest', + project: true, + extraFileExtensions: ['.vue'], + parser: '@typescript-eslint/parser', // for vue plugin - https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser + }, + settings: { + 'import-x/extensions': ['.js', '.ts'], + 'import-x/parsers': { + '@typescript-eslint/parser': ['.js', '.ts'], + }, + 'import-x/resolver': { + typescript: true, + }, + }, + plugins: [ + '@eslint-community/eslint-plugin-eslint-comments', + '@stylistic/eslint-plugin-js', + '@typescript-eslint/eslint-plugin', + 'eslint-plugin-array-func', + 'eslint-plugin-github', + 'eslint-plugin-import-x', + 'eslint-plugin-no-jquery', + 'eslint-plugin-no-use-extend-native', + 'eslint-plugin-regexp', + 'eslint-plugin-sonarjs', + 'eslint-plugin-unicorn', + 'eslint-plugin-vitest', + 'eslint-plugin-vitest-globals', + 'eslint-plugin-wc', + ], + env: { + es2024: true, + node: true, + }, + overrides: [ + { + files: ['web_src/**/*'], + globals: { + __webpack_public_path__: true, + process: false, // https://github.com/webpack/webpack/issues/15833 + }, + }, + { + files: ['web_src/**/*', 'docs/**/*'], + env: { + browser: true, + node: false, + }, + }, + { + files: ['*.config.*'], + rules: { + 'import-x/no-unused-modules': [0], + }, + }, + { + files: ['**/*.d.ts'], + rules: { + 'import-x/no-unused-modules': [0], + '@typescript-eslint/consistent-type-definitions': [0], + '@typescript-eslint/consistent-type-imports': [0], + }, + }, + { + files: ['web_src/js/types.ts'], + rules: { + 'import-x/no-unused-modules': [0], + }, + }, + { + files: ['**/*.test.*', 'web_src/js/test/setup.ts'], + env: { + 'vitest-globals/env': true, + }, + rules: { + 'vitest/consistent-test-filename': [0], + 'vitest/consistent-test-it': [0], + 'vitest/expect-expect': [0], + 'vitest/max-expects': [0], + 'vitest/max-nested-describe': [0], + 'vitest/no-alias-methods': [0], + 'vitest/no-commented-out-tests': [0], + 'vitest/no-conditional-expect': [0], + 'vitest/no-conditional-in-test': [0], + 'vitest/no-conditional-tests': [0], + 'vitest/no-disabled-tests': [0], + 'vitest/no-done-callback': [0], + 'vitest/no-duplicate-hooks': [0], + 'vitest/no-focused-tests': [0], + 'vitest/no-hooks': [0], + 'vitest/no-identical-title': [2], + 'vitest/no-interpolation-in-snapshots': [0], + 'vitest/no-large-snapshots': [0], + 'vitest/no-mocks-import': [0], + 'vitest/no-restricted-matchers': [0], + 'vitest/no-restricted-vi-methods': [0], + 'vitest/no-standalone-expect': [0], + 'vitest/no-test-prefixes': [0], + 'vitest/no-test-return-statement': [0], + 'vitest/prefer-called-with': [0], + 'vitest/prefer-comparison-matcher': [0], + 'vitest/prefer-each': [0], + 'vitest/prefer-equality-matcher': [0], + 'vitest/prefer-expect-resolves': [0], + 'vitest/prefer-hooks-in-order': [0], + 'vitest/prefer-hooks-on-top': [2], + 'vitest/prefer-lowercase-title': [0], + 'vitest/prefer-mock-promise-shorthand': [0], + 'vitest/prefer-snapshot-hint': [0], + 'vitest/prefer-spy-on': [0], + 'vitest/prefer-strict-equal': [0], + 'vitest/prefer-to-be': [0], + 'vitest/prefer-to-be-falsy': [0], + 'vitest/prefer-to-be-object': [0], + 'vitest/prefer-to-be-truthy': [0], + 'vitest/prefer-to-contain': [0], + 'vitest/prefer-to-have-length': [0], + 'vitest/prefer-todo': [0], + 'vitest/require-hook': [0], + 'vitest/require-to-throw-message': [0], + 'vitest/require-top-level-describe': [0], + 'vitest/valid-describe-callback': [2], + 'vitest/valid-expect': [2], + 'vitest/valid-title': [2], + }, + }, + { + files: ['web_src/js/modules/fetch.ts', 'web_src/js/standalone/**/*'], + rules: { + 'no-restricted-syntax': [2, ...restrictedSyntax], + }, + }, + { + files: ['**/*.vue'], + plugins: [ + 'eslint-plugin-vue', + 'eslint-plugin-vue-scoped-css', + ], + extends: [ + 'plugin:vue/vue3-recommended', + 'plugin:vue-scoped-css/vue3-recommended', + ], + rules: { + 'vue/attributes-order': [0], + 'vue/html-closing-bracket-spacing': [2, {startTag: 'never', endTag: 'never', selfClosingTag: 'never'}], + 'vue/max-attributes-per-line': [0], + 'vue/singleline-html-element-content-newline': [0], + }, + }, + { + files: ['tests/e2e/**'], + plugins: [ + 'eslint-plugin-playwright' + ], + extends: [ + 'plugin:playwright/recommended', + ], + }, + ], + rules: { + '@eslint-community/eslint-comments/disable-enable-pair': [2], + '@eslint-community/eslint-comments/no-aggregating-enable': [2], + '@eslint-community/eslint-comments/no-duplicate-disable': [2], + '@eslint-community/eslint-comments/no-restricted-disable': [0], + '@eslint-community/eslint-comments/no-unlimited-disable': [2], + '@eslint-community/eslint-comments/no-unused-disable': [2], + '@eslint-community/eslint-comments/no-unused-enable': [2], + '@eslint-community/eslint-comments/no-use': [0], + '@eslint-community/eslint-comments/require-description': [0], + '@stylistic/js/array-bracket-newline': [0], + '@stylistic/js/array-bracket-spacing': [2, 'never'], + '@stylistic/js/array-element-newline': [0], + '@stylistic/js/arrow-parens': [2, 'always'], + '@stylistic/js/arrow-spacing': [2, {before: true, after: true}], + '@stylistic/js/block-spacing': [0], + '@stylistic/js/brace-style': [2, '1tbs', {allowSingleLine: true}], + '@stylistic/js/comma-dangle': [2, 'always-multiline'], + '@stylistic/js/comma-spacing': [2, {before: false, after: true}], + '@stylistic/js/comma-style': [2, 'last'], + '@stylistic/js/computed-property-spacing': [2, 'never'], + '@stylistic/js/dot-location': [2, 'property'], + '@stylistic/js/eol-last': [2], + '@stylistic/js/function-call-argument-newline': [0], + '@stylistic/js/function-call-spacing': [2, 'never'], + '@stylistic/js/function-paren-newline': [0], + '@stylistic/js/generator-star-spacing': [0], + '@stylistic/js/implicit-arrow-linebreak': [0], + '@stylistic/js/indent': [2, 2, {ignoreComments: true, SwitchCase: 1}], + '@stylistic/js/key-spacing': [2], + '@stylistic/js/keyword-spacing': [2], + '@stylistic/js/line-comment-position': [0], + '@stylistic/js/linebreak-style': [2, 'unix'], + '@stylistic/js/lines-around-comment': [0], + '@stylistic/js/lines-between-class-members': [0], + '@stylistic/js/max-len': [0], + '@stylistic/js/max-statements-per-line': [0], + '@stylistic/js/multiline-comment-style': [0], + '@stylistic/js/multiline-ternary': [0], + '@stylistic/js/new-parens': [2], + '@stylistic/js/newline-per-chained-call': [0], + '@stylistic/js/no-confusing-arrow': [0], + '@stylistic/js/no-extra-parens': [0], + '@stylistic/js/no-extra-semi': [2], + '@stylistic/js/no-floating-decimal': [0], + '@stylistic/js/no-mixed-operators': [0], + '@stylistic/js/no-mixed-spaces-and-tabs': [2], + '@stylistic/js/no-multi-spaces': [2, {ignoreEOLComments: true, exceptions: {Property: true}}], + '@stylistic/js/no-multiple-empty-lines': [2, {max: 1, maxEOF: 0, maxBOF: 0}], + '@stylistic/js/no-tabs': [2], + '@stylistic/js/no-trailing-spaces': [2], + '@stylistic/js/no-whitespace-before-property': [2], + '@stylistic/js/nonblock-statement-body-position': [2], + '@stylistic/js/object-curly-newline': [0], + '@stylistic/js/object-curly-spacing': [2, 'never'], + '@stylistic/js/object-property-newline': [0], + '@stylistic/js/one-var-declaration-per-line': [0], + '@stylistic/js/operator-linebreak': [2, 'after'], + '@stylistic/js/padded-blocks': [2, 'never'], + '@stylistic/js/padding-line-between-statements': [0], + '@stylistic/js/quote-props': [0], + '@stylistic/js/quotes': [2, 'single', {avoidEscape: true, allowTemplateLiterals: true}], + '@stylistic/js/rest-spread-spacing': [2, 'never'], + '@stylistic/js/semi': [2, 'always', {omitLastInOneLineBlock: true}], + '@stylistic/js/semi-spacing': [2, {before: false, after: true}], + '@stylistic/js/semi-style': [2, 'last'], + '@stylistic/js/space-before-blocks': [2, 'always'], + '@stylistic/js/space-before-function-paren': [2, {anonymous: 'ignore', named: 'never', asyncArrow: 'always'}], + '@stylistic/js/space-in-parens': [2, 'never'], + '@stylistic/js/space-infix-ops': [2], + '@stylistic/js/space-unary-ops': [2], + '@stylistic/js/spaced-comment': [2, 'always'], + '@stylistic/js/switch-colon-spacing': [2], + '@stylistic/js/template-curly-spacing': [2, 'never'], + '@stylistic/js/template-tag-spacing': [2, 'never'], + '@stylistic/js/wrap-iife': [2, 'inside'], + '@stylistic/js/wrap-regex': [0], + '@stylistic/js/yield-star-spacing': [2, 'after'], + '@typescript-eslint/adjacent-overload-signatures': [0], + '@typescript-eslint/array-type': [0], + '@typescript-eslint/await-thenable': [2], + '@typescript-eslint/ban-ts-comment': [2, {'ts-expect-error': false, 'ts-ignore': true, 'ts-nocheck': false, 'ts-check': false}], + '@typescript-eslint/ban-tslint-comment': [0], + '@typescript-eslint/class-literal-property-style': [0], + '@typescript-eslint/class-methods-use-this': [0], + '@typescript-eslint/consistent-generic-constructors': [0], + '@typescript-eslint/consistent-indexed-object-style': [0], + '@typescript-eslint/consistent-return': [0], + '@typescript-eslint/consistent-type-assertions': [2, {assertionStyle: 'as', objectLiteralTypeAssertions: 'allow'}], + '@typescript-eslint/consistent-type-definitions': [2, 'type'], + '@typescript-eslint/consistent-type-exports': [2, {fixMixedExportsWithInlineTypeSpecifier: false}], + '@typescript-eslint/consistent-type-imports': [2, {prefer: 'type-imports', fixStyle: 'separate-type-imports', disallowTypeAnnotations: true}], + '@typescript-eslint/default-param-last': [0], + '@typescript-eslint/dot-notation': [0], + '@typescript-eslint/explicit-function-return-type': [0], + '@typescript-eslint/explicit-member-accessibility': [0], + '@typescript-eslint/explicit-module-boundary-types': [0], + '@typescript-eslint/init-declarations': [0], + '@typescript-eslint/max-params': [0], + '@typescript-eslint/member-ordering': [0], + '@typescript-eslint/method-signature-style': [0], + '@typescript-eslint/naming-convention': [0], + '@typescript-eslint/no-array-constructor': [2], + '@typescript-eslint/no-array-delete': [2], + '@typescript-eslint/no-base-to-string': [0], + '@typescript-eslint/no-confusing-non-null-assertion': [2], + '@typescript-eslint/no-confusing-void-expression': [0], + '@typescript-eslint/no-deprecated': [2], + '@typescript-eslint/no-dupe-class-members': [0], + '@typescript-eslint/no-duplicate-enum-values': [2], + '@typescript-eslint/no-duplicate-type-constituents': [2, {ignoreUnions: true}], + '@typescript-eslint/no-dynamic-delete': [0], + '@typescript-eslint/no-empty-function': [0], + '@typescript-eslint/no-empty-interface': [0], + '@typescript-eslint/no-empty-object-type': [2], + '@typescript-eslint/no-explicit-any': [0], + '@typescript-eslint/no-extra-non-null-assertion': [2], + '@typescript-eslint/no-extraneous-class': [0], + '@typescript-eslint/no-floating-promises': [0], + '@typescript-eslint/no-for-in-array': [2], + '@typescript-eslint/no-implied-eval': [2], + '@typescript-eslint/no-import-type-side-effects': [0], // dupe with consistent-type-imports + '@typescript-eslint/no-inferrable-types': [0], + '@typescript-eslint/no-invalid-this': [0], + '@typescript-eslint/no-invalid-void-type': [0], + '@typescript-eslint/no-loop-func': [0], + '@typescript-eslint/no-loss-of-precision': [0], + '@typescript-eslint/no-magic-numbers': [0], + '@typescript-eslint/no-meaningless-void-operator': [0], + '@typescript-eslint/no-misused-new': [2], + '@typescript-eslint/no-misused-promises': [2, {checksVoidReturn: {attributes: false, arguments: false}}], + '@typescript-eslint/no-mixed-enums': [0], + '@typescript-eslint/no-namespace': [2], + '@typescript-eslint/no-non-null-asserted-nullish-coalescing': [0], + '@typescript-eslint/no-non-null-asserted-optional-chain': [2], + '@typescript-eslint/no-non-null-assertion': [0], + '@typescript-eslint/no-redeclare': [0], + '@typescript-eslint/no-redundant-type-constituents': [2], + '@typescript-eslint/no-require-imports': [2], + '@typescript-eslint/no-restricted-imports': [0], + '@typescript-eslint/no-restricted-types': [0], + '@typescript-eslint/no-shadow': [0], + '@typescript-eslint/no-this-alias': [0], // handled by unicorn/no-this-assignment + '@typescript-eslint/no-unnecessary-boolean-literal-compare': [0], + '@typescript-eslint/no-unnecessary-condition': [0], + '@typescript-eslint/no-unnecessary-qualifier': [0], + '@typescript-eslint/no-unnecessary-template-expression': [0], + '@typescript-eslint/no-unnecessary-type-arguments': [0], + '@typescript-eslint/no-unnecessary-type-assertion': [2], + '@typescript-eslint/no-unnecessary-type-constraint': [2], + '@typescript-eslint/no-unsafe-argument': [0], + '@typescript-eslint/no-unsafe-assignment': [0], + '@typescript-eslint/no-unsafe-call': [0], + '@typescript-eslint/no-unsafe-declaration-merging': [2], + '@typescript-eslint/no-unsafe-enum-comparison': [2], + '@typescript-eslint/no-unsafe-function-type': [2], + '@typescript-eslint/no-unsafe-member-access': [0], + '@typescript-eslint/no-unsafe-return': [0], + '@typescript-eslint/no-unsafe-unary-minus': [2], + '@typescript-eslint/no-unused-expressions': [0], + '@typescript-eslint/no-unused-vars': [2, {vars: 'all', args: 'all', caughtErrors: 'all', ignoreRestSiblings: false, argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_'}], + '@typescript-eslint/no-use-before-define': [0], + '@typescript-eslint/no-useless-constructor': [0], + '@typescript-eslint/no-useless-empty-export': [0], + '@typescript-eslint/no-wrapper-object-types': [2], + '@typescript-eslint/non-nullable-type-assertion-style': [0], + '@typescript-eslint/only-throw-error': [2], + '@typescript-eslint/parameter-properties': [0], + '@typescript-eslint/prefer-as-const': [2], + '@typescript-eslint/prefer-destructuring': [0], + '@typescript-eslint/prefer-enum-initializers': [0], + '@typescript-eslint/prefer-find': [2], + '@typescript-eslint/prefer-for-of': [2], + '@typescript-eslint/prefer-function-type': [2], + '@typescript-eslint/prefer-includes': [2], + '@typescript-eslint/prefer-literal-enum-member': [0], + '@typescript-eslint/prefer-namespace-keyword': [0], + '@typescript-eslint/prefer-nullish-coalescing': [0], + '@typescript-eslint/prefer-optional-chain': [2, {requireNullish: true}], + '@typescript-eslint/prefer-promise-reject-errors': [0], + '@typescript-eslint/prefer-readonly': [0], + '@typescript-eslint/prefer-readonly-parameter-types': [0], + '@typescript-eslint/prefer-reduce-type-parameter': [0], + '@typescript-eslint/prefer-regexp-exec': [0], + '@typescript-eslint/prefer-return-this-type': [0], + '@typescript-eslint/prefer-string-starts-ends-with': [2, {allowSingleElementEquality: 'always'}], + '@typescript-eslint/promise-function-async': [0], + '@typescript-eslint/require-array-sort-compare': [0], + '@typescript-eslint/require-await': [0], + '@typescript-eslint/restrict-plus-operands': [2], + '@typescript-eslint/restrict-template-expressions': [0], + '@typescript-eslint/return-await': [0], + '@typescript-eslint/strict-boolean-expressions': [0], + '@typescript-eslint/switch-exhaustiveness-check': [0], + '@typescript-eslint/triple-slash-reference': [2], + '@typescript-eslint/typedef': [0], + '@typescript-eslint/unbound-method': [0], // too many false-positives + '@typescript-eslint/unified-signatures': [2], + 'accessor-pairs': [2], + 'array-callback-return': [2, {checkForEach: true}], + 'array-func/avoid-reverse': [2], + 'array-func/from-map': [2], + 'array-func/no-unnecessary-this-arg': [2], + 'array-func/prefer-array-from': [2], + 'array-func/prefer-flat-map': [0], // handled by unicorn/prefer-array-flat-map + 'array-func/prefer-flat': [0], // handled by unicorn/prefer-array-flat + 'arrow-body-style': [0], + 'block-scoped-var': [2], + 'camelcase': [0], + 'capitalized-comments': [0], + 'class-methods-use-this': [0], + 'complexity': [0], + 'consistent-return': [0], + 'consistent-this': [0], + 'constructor-super': [2], + 'curly': [0], + 'default-case-last': [2], + 'default-case': [0], + 'default-param-last': [0], + 'dot-notation': [0], + 'eqeqeq': [2], + 'for-direction': [2], + 'func-name-matching': [2], + 'func-names': [0], + 'func-style': [0], + 'getter-return': [2], + 'github/a11y-aria-label-is-well-formatted': [0], + 'github/a11y-no-title-attribute': [0], + 'github/a11y-no-visually-hidden-interactive-element': [0], + 'github/a11y-role-supports-aria-props': [0], + 'github/a11y-svg-has-accessible-name': [0], + 'github/array-foreach': [0], + 'github/async-currenttarget': [2], + 'github/async-preventdefault': [2], + 'github/authenticity-token': [0], + 'github/get-attribute': [0], + 'github/js-class-name': [0], + 'github/no-blur': [0], + 'github/no-d-none': [0], + 'github/no-dataset': [2], + 'github/no-dynamic-script-tag': [2], + 'github/no-implicit-buggy-globals': [2], + 'github/no-inner-html': [0], + 'github/no-innerText': [2], + 'github/no-then': [2], + 'github/no-useless-passive': [2], + 'github/prefer-observers': [2], + 'github/require-passive-events': [2], + 'github/unescaped-html-literal': [0], + 'grouped-accessor-pairs': [2], + 'guard-for-in': [0], + 'id-blacklist': [0], + 'id-length': [0], + 'id-match': [0], + 'import-x/consistent-type-specifier-style': [0], + 'import-x/default': [0], + 'import-x/dynamic-import-chunkname': [0], + 'import-x/export': [2], + 'import-x/exports-last': [0], + 'import-x/extensions': [2, 'always', {ignorePackages: true}], + 'import-x/first': [2], + 'import-x/group-exports': [0], + 'import-x/max-dependencies': [0], + 'import-x/named': [2], + 'import-x/namespace': [0], + 'import-x/newline-after-import': [0], + 'import-x/no-absolute-path': [0], + 'import-x/no-amd': [2], + 'import-x/no-anonymous-default-export': [0], + 'import-x/no-commonjs': [2], + 'import-x/no-cycle': [2, {ignoreExternal: true, maxDepth: 1}], + 'import-x/no-default-export': [0], + 'import-x/no-deprecated': [0], + 'import-x/no-dynamic-require': [0], + 'import-x/no-empty-named-blocks': [2], + 'import-x/no-extraneous-dependencies': [2], + 'import-x/no-import-module-exports': [0], + 'import-x/no-internal-modules': [0], + 'import-x/no-mutable-exports': [0], + 'import-x/no-named-as-default-member': [0], + 'import-x/no-named-as-default': [0], + 'import-x/no-named-default': [0], + 'import-x/no-named-export': [0], + 'import-x/no-namespace': [0], + 'import-x/no-nodejs-modules': [0], + 'import-x/no-relative-packages': [0], + 'import-x/no-relative-parent-imports': [0], + 'import-x/no-restricted-paths': [0], + 'import-x/no-self-import': [2], + 'import-x/no-unassigned-import': [0], + 'import-x/no-unresolved': [2, {commonjs: true, ignore: ['\\?.+$']}], + 'import-x/no-unused-modules': [2, {unusedExports: true}], + 'import-x/no-useless-path-segments': [2, {commonjs: true}], + 'import-x/no-webpack-loader-syntax': [2], + 'import-x/order': [0], + 'import-x/prefer-default-export': [0], + 'import-x/unambiguous': [0], + 'init-declarations': [0], + 'line-comment-position': [0], + 'logical-assignment-operators': [0], + 'max-classes-per-file': [0], + 'max-depth': [0], + 'max-lines-per-function': [0], + 'max-lines': [0], + 'max-nested-callbacks': [0], + 'max-params': [0], + 'max-statements': [0], + 'multiline-comment-style': [2, 'separate-lines'], + 'new-cap': [0], + 'no-alert': [0], + 'no-array-constructor': [0], // handled by @typescript-eslint/no-array-constructor + 'no-async-promise-executor': [0], + 'no-await-in-loop': [0], + 'no-bitwise': [0], + 'no-buffer-constructor': [0], + 'no-caller': [2], + 'no-case-declarations': [2], + 'no-class-assign': [2], + 'no-compare-neg-zero': [2], + 'no-cond-assign': [2, 'except-parens'], + 'no-console': [1, {allow: ['debug', 'info', 'warn', 'error']}], + 'no-const-assign': [2], + 'no-constant-binary-expression': [2], + 'no-constant-condition': [0], + 'no-constructor-return': [2], + 'no-continue': [0], + 'no-control-regex': [0], + 'no-debugger': [1], + 'no-delete-var': [2], + 'no-div-regex': [0], + 'no-dupe-args': [2], + 'no-dupe-class-members': [2], + 'no-dupe-else-if': [2], + 'no-dupe-keys': [2], + 'no-duplicate-case': [2], + 'no-duplicate-imports': [0], + 'no-else-return': [2], + 'no-empty-character-class': [2], + 'no-empty-function': [0], + 'no-empty-pattern': [2], + 'no-empty-static-block': [2], + 'no-empty': [2, {allowEmptyCatch: true}], + 'no-eq-null': [2], + 'no-eval': [2], + 'no-ex-assign': [2], + 'no-extend-native': [2], + 'no-extra-bind': [2], + 'no-extra-boolean-cast': [2], + 'no-extra-label': [0], + 'no-fallthrough': [2], + 'no-func-assign': [2], + 'no-global-assign': [2], + 'no-implicit-coercion': [2], + 'no-implicit-globals': [0], + 'no-implied-eval': [0], // handled by @typescript-eslint/no-implied-eval + 'no-import-assign': [2], + 'no-inline-comments': [0], + 'no-inner-declarations': [2], + 'no-invalid-regexp': [2], + 'no-invalid-this': [0], + 'no-irregular-whitespace': [2], + 'no-iterator': [2], + 'no-jquery/no-ajax-events': [2], + 'no-jquery/no-ajax': [2], + 'no-jquery/no-and-self': [2], + 'no-jquery/no-animate-toggle': [2], + 'no-jquery/no-animate': [2], + 'no-jquery/no-append-html': [2], + 'no-jquery/no-attr': [2], + 'no-jquery/no-bind': [2], + 'no-jquery/no-box-model': [2], + 'no-jquery/no-browser': [2], + 'no-jquery/no-camel-case': [2], + 'no-jquery/no-class-state': [2], + 'no-jquery/no-class': [0], + 'no-jquery/no-clone': [2], + 'no-jquery/no-closest': [0], + 'no-jquery/no-constructor-attributes': [2], + 'no-jquery/no-contains': [2], + 'no-jquery/no-context-prop': [2], + 'no-jquery/no-css': [2], + 'no-jquery/no-data': [0], + 'no-jquery/no-deferred': [2], + 'no-jquery/no-delegate': [2], + 'no-jquery/no-done-fail': [2], + 'no-jquery/no-each-collection': [0], + 'no-jquery/no-each-util': [0], + 'no-jquery/no-each': [0], + 'no-jquery/no-error-shorthand': [2], + 'no-jquery/no-error': [2], + 'no-jquery/no-escape-selector': [2], + 'no-jquery/no-event-shorthand': [2], + 'no-jquery/no-extend': [2], + 'no-jquery/no-fade': [2], + 'no-jquery/no-filter': [0], + 'no-jquery/no-find-collection': [0], + 'no-jquery/no-find-util': [2], + 'no-jquery/no-find': [0], + 'no-jquery/no-fx-interval': [2], + 'no-jquery/no-fx': [2], + 'no-jquery/no-global-eval': [2], + 'no-jquery/no-global-selector': [0], + 'no-jquery/no-grep': [2], + 'no-jquery/no-has': [2], + 'no-jquery/no-hold-ready': [2], + 'no-jquery/no-html': [0], + 'no-jquery/no-in-array': [2], + 'no-jquery/no-is-array': [2], + 'no-jquery/no-is-empty-object': [2], + 'no-jquery/no-is-function': [2], + 'no-jquery/no-is-numeric': [2], + 'no-jquery/no-is-plain-object': [2], + 'no-jquery/no-is-window': [2], + 'no-jquery/no-is': [2], + 'no-jquery/no-jquery-constructor': [0], + 'no-jquery/no-live': [2], + 'no-jquery/no-load-shorthand': [2], + 'no-jquery/no-load': [2], + 'no-jquery/no-map-collection': [0], + 'no-jquery/no-map-util': [2], + 'no-jquery/no-map': [2], + 'no-jquery/no-merge': [2], + 'no-jquery/no-node-name': [2], + 'no-jquery/no-noop': [2], + 'no-jquery/no-now': [2], + 'no-jquery/no-on-ready': [2], + 'no-jquery/no-other-methods': [0], + 'no-jquery/no-other-utils': [2], + 'no-jquery/no-param': [2], + 'no-jquery/no-parent': [0], + 'no-jquery/no-parents': [2], + 'no-jquery/no-parse-html-literal': [2], + 'no-jquery/no-parse-html': [2], + 'no-jquery/no-parse-json': [2], + 'no-jquery/no-parse-xml': [2], + 'no-jquery/no-prop': [2], + 'no-jquery/no-proxy': [2], + 'no-jquery/no-ready-shorthand': [2], + 'no-jquery/no-ready': [2], + 'no-jquery/no-selector-prop': [2], + 'no-jquery/no-serialize': [2], + 'no-jquery/no-size': [2], + 'no-jquery/no-sizzle': [2], + 'no-jquery/no-slide': [2], + 'no-jquery/no-sub': [2], + 'no-jquery/no-support': [2], + 'no-jquery/no-text': [2], + 'no-jquery/no-trigger': [0], + 'no-jquery/no-trim': [2], + 'no-jquery/no-type': [2], + 'no-jquery/no-unique': [2], + 'no-jquery/no-unload-shorthand': [2], + 'no-jquery/no-val': [0], + 'no-jquery/no-visibility': [2], + 'no-jquery/no-when': [2], + 'no-jquery/no-wrap': [2], + 'no-jquery/variable-pattern': [2], + 'no-label-var': [2], + 'no-labels': [0], // handled by no-restricted-syntax + 'no-lone-blocks': [2], + 'no-lonely-if': [0], + 'no-loop-func': [0], + 'no-loss-of-precision': [2], + 'no-magic-numbers': [0], + 'no-misleading-character-class': [2], + 'no-multi-assign': [0], + 'no-multi-str': [2], + 'no-negated-condition': [0], + 'no-nested-ternary': [0], + 'no-new-func': [2], + 'no-new-native-nonconstructor': [2], + 'no-new-object': [2], + 'no-new-symbol': [2], + 'no-new-wrappers': [2], + 'no-new': [0], + 'no-nonoctal-decimal-escape': [2], + 'no-obj-calls': [2], + 'no-octal-escape': [2], + 'no-octal': [2], + 'no-param-reassign': [0], + 'no-plusplus': [0], + 'no-promise-executor-return': [0], + 'no-proto': [2], + 'no-prototype-builtins': [2], + 'no-redeclare': [0], // must be disabled for typescript overloads + 'no-regex-spaces': [2], + 'no-restricted-exports': [0], + 'no-restricted-globals': [2, 'addEventListener', 'blur', 'close', 'closed', 'confirm', 'defaultStatus', 'defaultstatus', 'error', 'event', 'external', 'find', 'focus', 'frameElement', 'frames', 'history', 'innerHeight', 'innerWidth', 'isFinite', 'isNaN', 'length', 'locationbar', 'menubar', 'moveBy', 'moveTo', 'name', 'onblur', 'onerror', 'onfocus', 'onload', 'onresize', 'onunload', 'open', 'opener', 'opera', 'outerHeight', 'outerWidth', 'pageXOffset', 'pageYOffset', 'parent', 'print', 'removeEventListener', 'resizeBy', 'resizeTo', 'screen', 'screenLeft', 'screenTop', 'screenX', 'screenY', 'scroll', 'scrollbars', 'scrollBy', 'scrollTo', 'scrollX', 'scrollY', 'status', 'statusbar', 'stop', 'toolbar', 'top'], + 'no-restricted-imports': [0], + 'no-restricted-syntax': [2, ...restrictedSyntax, {selector: 'CallExpression[callee.name="fetch"]', message: 'use modules/fetch.ts instead'}], + 'no-return-assign': [0], + 'no-script-url': [2], + 'no-self-assign': [2, {props: true}], + 'no-self-compare': [2], + 'no-sequences': [2], + 'no-setter-return': [2], + 'no-shadow-restricted-names': [2], + 'no-shadow': [0], + 'no-sparse-arrays': [2], + 'no-template-curly-in-string': [2], + 'no-ternary': [0], + 'no-this-before-super': [2], + 'no-throw-literal': [2], + 'no-undef-init': [2], + 'no-undef': [0], + 'no-undefined': [0], + 'no-underscore-dangle': [0], + 'no-unexpected-multiline': [2], + 'no-unmodified-loop-condition': [2], + 'no-unneeded-ternary': [2], + 'no-unreachable-loop': [2], + 'no-unreachable': [2], + 'no-unsafe-finally': [2], + 'no-unsafe-negation': [2], + 'no-unused-expressions': [2], + 'no-unused-labels': [2], + 'no-unused-private-class-members': [2], + 'no-unused-vars': [0], // handled by @typescript-eslint/no-unused-vars + 'no-use-before-define': [2, {functions: false, classes: true, variables: true, allowNamedExports: true}], + 'no-use-extend-native/no-use-extend-native': [2], + 'no-useless-backreference': [2], + 'no-useless-call': [2], + 'no-useless-catch': [2], + 'no-useless-computed-key': [2], + 'no-useless-concat': [2], + 'no-useless-constructor': [2], + 'no-useless-escape': [2], + 'no-useless-rename': [2], + 'no-useless-return': [2], + 'no-var': [2], + 'no-void': [2], + 'no-warning-comments': [0], + 'no-with': [0], // handled by no-restricted-syntax + 'object-shorthand': [2, 'always'], + 'one-var-declaration-per-line': [0], + 'one-var': [0], + 'operator-assignment': [2, 'always'], + 'operator-linebreak': [2, 'after'], + 'prefer-arrow-callback': [2, {allowNamedFunctions: true, allowUnboundThis: true}], + 'prefer-const': [2, {destructuring: 'all', ignoreReadBeforeAssign: true}], + 'prefer-destructuring': [0], + 'prefer-exponentiation-operator': [2], + 'prefer-named-capture-group': [0], + 'prefer-numeric-literals': [2], + 'prefer-object-has-own': [2], + 'prefer-object-spread': [2], + 'prefer-promise-reject-errors': [2, {allowEmptyReject: false}], + 'prefer-regex-literals': [2], + 'prefer-rest-params': [2], + 'prefer-spread': [2], + 'prefer-template': [2], + 'radix': [2, 'as-needed'], + 'regexp/confusing-quantifier': [2], + 'regexp/control-character-escape': [2], + 'regexp/hexadecimal-escape': [0], + 'regexp/letter-case': [0], + 'regexp/match-any': [2], + 'regexp/negation': [2], + 'regexp/no-contradiction-with-assertion': [0], + 'regexp/no-control-character': [0], + 'regexp/no-dupe-characters-character-class': [2], + 'regexp/no-dupe-disjunctions': [2], + 'regexp/no-empty-alternative': [2], + 'regexp/no-empty-capturing-group': [2], + 'regexp/no-empty-character-class': [0], + 'regexp/no-empty-group': [2], + 'regexp/no-empty-lookarounds-assertion': [2], + 'regexp/no-empty-string-literal': [2], + 'regexp/no-escape-backspace': [2], + 'regexp/no-extra-lookaround-assertions': [0], + 'regexp/no-invalid-regexp': [2], + 'regexp/no-invisible-character': [2], + 'regexp/no-lazy-ends': [2], + 'regexp/no-legacy-features': [2], + 'regexp/no-misleading-capturing-group': [0], + 'regexp/no-misleading-unicode-character': [0], + 'regexp/no-missing-g-flag': [2], + 'regexp/no-non-standard-flag': [2], + 'regexp/no-obscure-range': [2], + 'regexp/no-octal': [2], + 'regexp/no-optional-assertion': [2], + 'regexp/no-potentially-useless-backreference': [2], + 'regexp/no-standalone-backslash': [2], + 'regexp/no-super-linear-backtracking': [0], + 'regexp/no-super-linear-move': [0], + 'regexp/no-trivially-nested-assertion': [2], + 'regexp/no-trivially-nested-quantifier': [2], + 'regexp/no-unused-capturing-group': [0], + 'regexp/no-useless-assertions': [2], + 'regexp/no-useless-backreference': [2], + 'regexp/no-useless-character-class': [2], + 'regexp/no-useless-dollar-replacements': [2], + 'regexp/no-useless-escape': [2], + 'regexp/no-useless-flag': [2], + 'regexp/no-useless-lazy': [2], + 'regexp/no-useless-non-capturing-group': [2], + 'regexp/no-useless-quantifier': [2], + 'regexp/no-useless-range': [2], + 'regexp/no-useless-set-operand': [2], + 'regexp/no-useless-string-literal': [2], + 'regexp/no-useless-two-nums-quantifier': [2], + 'regexp/no-zero-quantifier': [2], + 'regexp/optimal-lookaround-quantifier': [2], + 'regexp/optimal-quantifier-concatenation': [0], + 'regexp/prefer-character-class': [0], + 'regexp/prefer-d': [0], + 'regexp/prefer-escape-replacement-dollar-char': [0], + 'regexp/prefer-lookaround': [0], + 'regexp/prefer-named-backreference': [0], + 'regexp/prefer-named-capture-group': [0], + 'regexp/prefer-named-replacement': [0], + 'regexp/prefer-plus-quantifier': [2], + 'regexp/prefer-predefined-assertion': [2], + 'regexp/prefer-quantifier': [0], + 'regexp/prefer-question-quantifier': [2], + 'regexp/prefer-range': [2], + 'regexp/prefer-regexp-exec': [2], + 'regexp/prefer-regexp-test': [2], + 'regexp/prefer-result-array-groups': [0], + 'regexp/prefer-set-operation': [2], + 'regexp/prefer-star-quantifier': [2], + 'regexp/prefer-unicode-codepoint-escapes': [2], + 'regexp/prefer-w': [0], + 'regexp/require-unicode-regexp': [0], + 'regexp/simplify-set-operations': [2], + 'regexp/sort-alternatives': [0], + 'regexp/sort-character-class-elements': [0], + 'regexp/sort-flags': [0], + 'regexp/strict': [2], + 'regexp/unicode-escape': [0], + 'regexp/use-ignore-case': [0], + 'require-atomic-updates': [0], + 'require-await': [0], // handled by @typescript-eslint/require-await + 'require-unicode-regexp': [0], + 'require-yield': [2], + 'sonarjs/cognitive-complexity': [0], + 'sonarjs/elseif-without-else': [0], + 'sonarjs/max-switch-cases': [0], + 'sonarjs/no-all-duplicated-branches': [2], + 'sonarjs/no-collapsible-if': [0], + 'sonarjs/no-collection-size-mischeck': [2], + 'sonarjs/no-duplicate-string': [0], + 'sonarjs/no-duplicated-branches': [0], + 'sonarjs/no-element-overwrite': [2], + 'sonarjs/no-empty-collection': [2], + 'sonarjs/no-extra-arguments': [2], + 'sonarjs/no-gratuitous-expressions': [2], + 'sonarjs/no-identical-conditions': [2], + 'sonarjs/no-identical-expressions': [2], + 'sonarjs/no-identical-functions': [2, 5], + 'sonarjs/no-ignored-return': [2], + 'sonarjs/no-inverted-boolean-check': [2], + 'sonarjs/no-nested-switch': [0], + 'sonarjs/no-nested-template-literals': [0], + 'sonarjs/no-one-iteration-loop': [2], + 'sonarjs/no-redundant-boolean': [2], + 'sonarjs/no-redundant-jump': [2], + 'sonarjs/no-same-line-conditional': [2], + 'sonarjs/no-small-switch': [0], + 'sonarjs/no-unused-collection': [2], + 'sonarjs/no-use-of-empty-return-value': [2], + 'sonarjs/no-useless-catch': [2], + 'sonarjs/non-existent-operator': [2], + 'sonarjs/prefer-immediate-return': [0], + 'sonarjs/prefer-object-literal': [0], + 'sonarjs/prefer-single-boolean-return': [0], + 'sonarjs/prefer-while': [2], + 'sort-imports': [0], + 'sort-keys': [0], + 'sort-vars': [0], + 'strict': [0], + 'symbol-description': [2], + 'unicode-bom': [2, 'never'], + 'unicorn/better-regex': [0], + 'unicorn/catch-error-name': [0], + 'unicorn/consistent-destructuring': [2], + 'unicorn/consistent-empty-array-spread': [2], + 'unicorn/consistent-existence-index-check': [0], + 'unicorn/consistent-function-scoping': [0], + 'unicorn/custom-error-definition': [0], + 'unicorn/empty-brace-spaces': [2], + 'unicorn/error-message': [0], + 'unicorn/escape-case': [0], + 'unicorn/expiring-todo-comments': [0], + 'unicorn/explicit-length-check': [0], + 'unicorn/filename-case': [0], + 'unicorn/import-index': [0], + 'unicorn/import-style': [0], + 'unicorn/new-for-builtins': [2], + 'unicorn/no-abusive-eslint-disable': [0], + 'unicorn/no-anonymous-default-export': [0], + 'unicorn/no-array-callback-reference': [0], + 'unicorn/no-array-for-each': [2], + 'unicorn/no-array-method-this-argument': [2], + 'unicorn/no-array-push-push': [2], + 'unicorn/no-array-reduce': [2], + 'unicorn/no-await-expression-member': [0], + 'unicorn/no-await-in-promise-methods': [2], + 'unicorn/no-console-spaces': [0], + 'unicorn/no-document-cookie': [2], + 'unicorn/no-empty-file': [2], + 'unicorn/no-for-loop': [0], + 'unicorn/no-hex-escape': [0], + 'unicorn/no-instanceof-array': [0], + 'unicorn/no-invalid-fetch-options': [2], + 'unicorn/no-invalid-remove-event-listener': [2], + 'unicorn/no-keyword-prefix': [0], + 'unicorn/no-length-as-slice-end': [2], + 'unicorn/no-lonely-if': [2], + 'unicorn/no-magic-array-flat-depth': [0], + 'unicorn/no-negated-condition': [0], + 'unicorn/no-negation-in-equality-check': [2], + 'unicorn/no-nested-ternary': [0], + 'unicorn/no-new-array': [0], + 'unicorn/no-new-buffer': [0], + 'unicorn/no-null': [0], + 'unicorn/no-object-as-default-parameter': [0], + 'unicorn/no-process-exit': [0], + 'unicorn/no-single-promise-in-promise-methods': [2], + 'unicorn/no-static-only-class': [2], + 'unicorn/no-thenable': [2], + 'unicorn/no-this-assignment': [2], + 'unicorn/no-typeof-undefined': [2], + 'unicorn/no-unnecessary-await': [2], + 'unicorn/no-unnecessary-polyfills': [2], + 'unicorn/no-unreadable-array-destructuring': [0], + 'unicorn/no-unreadable-iife': [2], + 'unicorn/no-unused-properties': [2], + 'unicorn/no-useless-fallback-in-spread': [2], + 'unicorn/no-useless-length-check': [2], + 'unicorn/no-useless-promise-resolve-reject': [2], + 'unicorn/no-useless-spread': [2], + 'unicorn/no-useless-switch-case': [2], + 'unicorn/no-useless-undefined': [0], + 'unicorn/no-zero-fractions': [2], + 'unicorn/number-literal-case': [0], + 'unicorn/numeric-separators-style': [0], + 'unicorn/prefer-add-event-listener': [2], + 'unicorn/prefer-array-find': [2], + 'unicorn/prefer-array-flat-map': [2], + 'unicorn/prefer-array-flat': [2], + 'unicorn/prefer-array-index-of': [2], + 'unicorn/prefer-array-some': [2], + 'unicorn/prefer-at': [0], + 'unicorn/prefer-blob-reading-methods': [2], + 'unicorn/prefer-code-point': [0], + 'unicorn/prefer-date-now': [2], + 'unicorn/prefer-default-parameters': [0], + 'unicorn/prefer-dom-node-append': [2], + 'unicorn/prefer-dom-node-dataset': [0], + 'unicorn/prefer-dom-node-remove': [2], + 'unicorn/prefer-dom-node-text-content': [2], + 'unicorn/prefer-event-target': [2], + 'unicorn/prefer-export-from': [0], + 'unicorn/prefer-global-this': [0], + 'unicorn/prefer-includes': [2], + 'unicorn/prefer-json-parse-buffer': [0], + 'unicorn/prefer-keyboard-event-key': [2], + 'unicorn/prefer-logical-operator-over-ternary': [2], + 'unicorn/prefer-math-min-max': [2], + 'unicorn/prefer-math-trunc': [2], + 'unicorn/prefer-modern-dom-apis': [0], + 'unicorn/prefer-modern-math-apis': [2], + 'unicorn/prefer-module': [2], + 'unicorn/prefer-native-coercion-functions': [2], + 'unicorn/prefer-negative-index': [2], + 'unicorn/prefer-node-protocol': [2], + 'unicorn/prefer-number-properties': [0], + 'unicorn/prefer-object-from-entries': [2], + 'unicorn/prefer-object-has-own': [0], + 'unicorn/prefer-optional-catch-binding': [2], + 'unicorn/prefer-prototype-methods': [0], + 'unicorn/prefer-query-selector': [2], + 'unicorn/prefer-reflect-apply': [0], + 'unicorn/prefer-regexp-test': [2], + 'unicorn/prefer-set-has': [0], + 'unicorn/prefer-set-size': [2], + 'unicorn/prefer-spread': [0], + 'unicorn/prefer-string-raw': [0], + 'unicorn/prefer-string-replace-all': [0], + 'unicorn/prefer-string-slice': [0], + 'unicorn/prefer-string-starts-ends-with': [2], + 'unicorn/prefer-string-trim-start-end': [2], + 'unicorn/prefer-structured-clone': [2], + 'unicorn/prefer-switch': [0], + 'unicorn/prefer-ternary': [0], + 'unicorn/prefer-text-content': [2], + 'unicorn/prefer-top-level-await': [0], + 'unicorn/prefer-type-error': [0], + 'unicorn/prevent-abbreviations': [0], + 'unicorn/relative-url-style': [2], + 'unicorn/require-array-join-separator': [2], + 'unicorn/require-number-to-fixed-digits-argument': [2], + 'unicorn/require-post-message-target-origin': [0], + 'unicorn/string-content': [0], + 'unicorn/switch-case-braces': [0], + 'unicorn/template-indent': [2], + 'unicorn/text-encoding-identifier-case': [0], + 'unicorn/throw-new-error': [2], + 'use-isnan': [2], + 'valid-typeof': [2, {requireStringLiterals: true}], + 'vars-on-top': [0], + 'wc/attach-shadow-constructor': [2], + 'wc/define-tag-after-class-definition': [0], + 'wc/expose-class-on-global': [0], + 'wc/file-name-matches-element': [2], + 'wc/guard-define-call': [0], + 'wc/guard-super-call': [2], + 'wc/max-elements-per-file': [0], + 'wc/no-child-traversal-in-attributechangedcallback': [2], + 'wc/no-child-traversal-in-connectedcallback': [2], + 'wc/no-closed-shadow-root': [2], + 'wc/no-constructor-attributes': [2], + 'wc/no-constructor-params': [2], + 'wc/no-constructor': [2], + 'wc/no-customized-built-in-elements': [2], + 'wc/no-exports-with-element': [0], + 'wc/no-invalid-element-name': [2], + 'wc/no-invalid-extends': [2], + 'wc/no-method-prefixed-with-on': [2], + 'wc/no-self-class': [2], + 'wc/no-typos': [2], + 'wc/require-listener-teardown': [2], + 'wc/tag-name-matches-class': [2], + 'yoda': [2, 'never'], + }, +}; diff --git a/.eslintrc.yaml b/.eslintrc.yaml deleted file mode 100644 index 0dd9a6687d..0000000000 --- a/.eslintrc.yaml +++ /dev/null @@ -1,967 +0,0 @@ -root: true -reportUnusedDisableDirectives: true - -ignorePatterns: - - /web_src/js/vendor - - /web_src/fomantic - - /public/assets/js - -parser: "@typescript-eslint/parser" - -parserOptions: - sourceType: module - ecmaVersion: latest - project: true - extraFileExtensions: [".vue"] - parser: "@typescript-eslint/parser" # for vue plugin - https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser - -settings: - import-x/extensions: [".js", ".ts"] - import-x/parsers: - "@typescript-eslint/parser": [".js", ".ts"] - import-x/resolver: - typescript: true - -plugins: - - "@eslint-community/eslint-plugin-eslint-comments" - - "@stylistic/eslint-plugin-js" - - "@typescript-eslint/eslint-plugin" - - eslint-plugin-array-func - - eslint-plugin-github - - eslint-plugin-import-x - - eslint-plugin-no-jquery - - eslint-plugin-no-use-extend-native - - eslint-plugin-regexp - - eslint-plugin-sonarjs - - eslint-plugin-unicorn - - eslint-plugin-vitest - - eslint-plugin-vitest-globals - - eslint-plugin-wc - -env: - es2024: true - node: true - -overrides: - - files: ["web_src/**/*"] - globals: - __webpack_public_path__: true - process: false # https://github.com/webpack/webpack/issues/15833 - - files: ["web_src/**/*", "docs/**/*"] - env: - browser: true - node: false - - files: ["web_src/**/*worker.*"] - env: - worker: true - rules: - no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, status, statusbar, stop, toolbar, top] - - files: ["*.config.*"] - rules: - import-x/no-unused-modules: [0] - - files: ["**/*.d.ts"] - rules: - import-x/no-unused-modules: [0] - "@typescript-eslint/consistent-type-definitions": [0] - "@typescript-eslint/consistent-type-imports": [0] - - files: ["web_src/js/types.ts"] - rules: - import-x/no-unused-modules: [0] - - files: ["**/*.test.*", "web_src/js/test/setup.ts"] - env: - vitest-globals/env: true - rules: - vitest/consistent-test-filename: [0] - vitest/consistent-test-it: [0] - vitest/expect-expect: [0] - vitest/max-expects: [0] - vitest/max-nested-describe: [0] - vitest/no-alias-methods: [0] - vitest/no-commented-out-tests: [0] - vitest/no-conditional-expect: [0] - vitest/no-conditional-in-test: [0] - vitest/no-conditional-tests: [0] - vitest/no-disabled-tests: [0] - vitest/no-done-callback: [0] - vitest/no-duplicate-hooks: [0] - vitest/no-focused-tests: [0] - vitest/no-hooks: [0] - vitest/no-identical-title: [2] - vitest/no-interpolation-in-snapshots: [0] - vitest/no-large-snapshots: [0] - vitest/no-mocks-import: [0] - vitest/no-restricted-matchers: [0] - vitest/no-restricted-vi-methods: [0] - vitest/no-standalone-expect: [0] - vitest/no-test-prefixes: [0] - vitest/no-test-return-statement: [0] - vitest/prefer-called-with: [0] - vitest/prefer-comparison-matcher: [0] - vitest/prefer-each: [0] - vitest/prefer-equality-matcher: [0] - vitest/prefer-expect-resolves: [0] - vitest/prefer-hooks-in-order: [0] - vitest/prefer-hooks-on-top: [2] - vitest/prefer-lowercase-title: [0] - vitest/prefer-mock-promise-shorthand: [0] - vitest/prefer-snapshot-hint: [0] - vitest/prefer-spy-on: [0] - vitest/prefer-strict-equal: [0] - vitest/prefer-to-be: [0] - vitest/prefer-to-be-falsy: [0] - vitest/prefer-to-be-object: [0] - vitest/prefer-to-be-truthy: [0] - vitest/prefer-to-contain: [0] - vitest/prefer-to-have-length: [0] - vitest/prefer-todo: [0] - vitest/require-hook: [0] - vitest/require-to-throw-message: [0] - vitest/require-top-level-describe: [0] - vitest/valid-describe-callback: [2] - vitest/valid-expect: [2] - vitest/valid-title: [2] - - files: ["web_src/js/modules/fetch.ts", "web_src/js/standalone/**/*"] - rules: - no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression] - - files: ["**/*.vue"] - plugins: - - eslint-plugin-vue - - eslint-plugin-vue-scoped-css - extends: - - plugin:vue/vue3-recommended - - plugin:vue-scoped-css/vue3-recommended - rules: - vue/attributes-order: [0] - vue/html-closing-bracket-spacing: [2, {startTag: never, endTag: never, selfClosingTag: never}] - vue/max-attributes-per-line: [0] - vue/singleline-html-element-content-newline: [0] - - files: ["tests/e2e/**"] - plugins: - - eslint-plugin-playwright - extends: plugin:playwright/recommended - -rules: - "@eslint-community/eslint-comments/disable-enable-pair": [2] - "@eslint-community/eslint-comments/no-aggregating-enable": [2] - "@eslint-community/eslint-comments/no-duplicate-disable": [2] - "@eslint-community/eslint-comments/no-restricted-disable": [0] - "@eslint-community/eslint-comments/no-unlimited-disable": [2] - "@eslint-community/eslint-comments/no-unused-disable": [2] - "@eslint-community/eslint-comments/no-unused-enable": [2] - "@eslint-community/eslint-comments/no-use": [0] - "@eslint-community/eslint-comments/require-description": [0] - "@stylistic/js/array-bracket-newline": [0] - "@stylistic/js/array-bracket-spacing": [2, never] - "@stylistic/js/array-element-newline": [0] - "@stylistic/js/arrow-parens": [2, always] - "@stylistic/js/arrow-spacing": [2, {before: true, after: true}] - "@stylistic/js/block-spacing": [0] - "@stylistic/js/brace-style": [2, 1tbs, {allowSingleLine: true}] - "@stylistic/js/comma-dangle": [2, always-multiline] - "@stylistic/js/comma-spacing": [2, {before: false, after: true}] - "@stylistic/js/comma-style": [2, last] - "@stylistic/js/computed-property-spacing": [2, never] - "@stylistic/js/dot-location": [2, property] - "@stylistic/js/eol-last": [2] - "@stylistic/js/function-call-argument-newline": [0] - "@stylistic/js/function-call-spacing": [2, never] - "@stylistic/js/function-paren-newline": [0] - "@stylistic/js/generator-star-spacing": [0] - "@stylistic/js/implicit-arrow-linebreak": [0] - "@stylistic/js/indent": [2, 2, {ignoreComments: true, SwitchCase: 1}] - "@stylistic/js/key-spacing": [2] - "@stylistic/js/keyword-spacing": [2] - "@stylistic/js/line-comment-position": [0] - "@stylistic/js/linebreak-style": [2, unix] - "@stylistic/js/lines-around-comment": [0] - "@stylistic/js/lines-between-class-members": [0] - "@stylistic/js/max-len": [0] - "@stylistic/js/max-statements-per-line": [0] - "@stylistic/js/multiline-comment-style": [0] - "@stylistic/js/multiline-ternary": [0] - "@stylistic/js/new-parens": [2] - "@stylistic/js/newline-per-chained-call": [0] - "@stylistic/js/no-confusing-arrow": [0] - "@stylistic/js/no-extra-parens": [0] - "@stylistic/js/no-extra-semi": [2] - "@stylistic/js/no-floating-decimal": [0] - "@stylistic/js/no-mixed-operators": [0] - "@stylistic/js/no-mixed-spaces-and-tabs": [2] - "@stylistic/js/no-multi-spaces": [2, {ignoreEOLComments: true, exceptions: {Property: true}}] - "@stylistic/js/no-multiple-empty-lines": [2, {max: 1, maxEOF: 0, maxBOF: 0}] - "@stylistic/js/no-tabs": [2] - "@stylistic/js/no-trailing-spaces": [2] - "@stylistic/js/no-whitespace-before-property": [2] - "@stylistic/js/nonblock-statement-body-position": [2] - "@stylistic/js/object-curly-newline": [0] - "@stylistic/js/object-curly-spacing": [2, never] - "@stylistic/js/object-property-newline": [0] - "@stylistic/js/one-var-declaration-per-line": [0] - "@stylistic/js/operator-linebreak": [2, after] - "@stylistic/js/padded-blocks": [2, never] - "@stylistic/js/padding-line-between-statements": [0] - "@stylistic/js/quote-props": [0] - "@stylistic/js/quotes": [2, single, {avoidEscape: true, allowTemplateLiterals: true}] - "@stylistic/js/rest-spread-spacing": [2, never] - "@stylistic/js/semi": [2, always, {omitLastInOneLineBlock: true}] - "@stylistic/js/semi-spacing": [2, {before: false, after: true}] - "@stylistic/js/semi-style": [2, last] - "@stylistic/js/space-before-blocks": [2, always] - "@stylistic/js/space-before-function-paren": [2, {anonymous: ignore, named: never, asyncArrow: always}] - "@stylistic/js/space-in-parens": [2, never] - "@stylistic/js/space-infix-ops": [2] - "@stylistic/js/space-unary-ops": [2] - "@stylistic/js/spaced-comment": [2, always] - "@stylistic/js/switch-colon-spacing": [2] - "@stylistic/js/template-curly-spacing": [2, never] - "@stylistic/js/template-tag-spacing": [2, never] - "@stylistic/js/wrap-iife": [2, inside] - "@stylistic/js/wrap-regex": [0] - "@stylistic/js/yield-star-spacing": [2, after] - "@typescript-eslint/adjacent-overload-signatures": [0] - "@typescript-eslint/array-type": [0] - "@typescript-eslint/await-thenable": [2] - "@typescript-eslint/ban-ts-comment": [2, {'ts-expect-error': false, 'ts-ignore': true, 'ts-nocheck': false, 'ts-check': false}] - "@typescript-eslint/ban-tslint-comment": [0] - "@typescript-eslint/class-literal-property-style": [0] - "@typescript-eslint/class-methods-use-this": [0] - "@typescript-eslint/consistent-generic-constructors": [0] - "@typescript-eslint/consistent-indexed-object-style": [0] - "@typescript-eslint/consistent-return": [0] - "@typescript-eslint/consistent-type-assertions": [2, {assertionStyle: as, objectLiteralTypeAssertions: allow}] - "@typescript-eslint/consistent-type-definitions": [2, type] - "@typescript-eslint/consistent-type-exports": [2, {fixMixedExportsWithInlineTypeSpecifier: false}] - "@typescript-eslint/consistent-type-imports": [2, {prefer: type-imports, fixStyle: separate-type-imports, disallowTypeAnnotations: true}] - "@typescript-eslint/default-param-last": [0] - "@typescript-eslint/dot-notation": [0] - "@typescript-eslint/explicit-function-return-type": [0] - "@typescript-eslint/explicit-member-accessibility": [0] - "@typescript-eslint/explicit-module-boundary-types": [0] - "@typescript-eslint/init-declarations": [0] - "@typescript-eslint/max-params": [0] - "@typescript-eslint/member-ordering": [0] - "@typescript-eslint/method-signature-style": [0] - "@typescript-eslint/naming-convention": [0] - "@typescript-eslint/no-array-constructor": [2] - "@typescript-eslint/no-array-delete": [2] - "@typescript-eslint/no-base-to-string": [0] - "@typescript-eslint/no-confusing-non-null-assertion": [2] - "@typescript-eslint/no-confusing-void-expression": [0] - "@typescript-eslint/no-deprecated": [2] - "@typescript-eslint/no-dupe-class-members": [0] - "@typescript-eslint/no-duplicate-enum-values": [2] - "@typescript-eslint/no-duplicate-type-constituents": [2, {ignoreUnions: true}] - "@typescript-eslint/no-dynamic-delete": [0] - "@typescript-eslint/no-empty-function": [0] - "@typescript-eslint/no-empty-interface": [0] - "@typescript-eslint/no-empty-object-type": [2] - "@typescript-eslint/no-explicit-any": [0] - "@typescript-eslint/no-extra-non-null-assertion": [2] - "@typescript-eslint/no-extraneous-class": [0] - "@typescript-eslint/no-floating-promises": [0] - "@typescript-eslint/no-for-in-array": [2] - "@typescript-eslint/no-implied-eval": [2] - "@typescript-eslint/no-import-type-side-effects": [0] # dupe with consistent-type-imports - "@typescript-eslint/no-inferrable-types": [0] - "@typescript-eslint/no-invalid-this": [0] - "@typescript-eslint/no-invalid-void-type": [0] - "@typescript-eslint/no-loop-func": [0] - "@typescript-eslint/no-loss-of-precision": [0] - "@typescript-eslint/no-magic-numbers": [0] - "@typescript-eslint/no-meaningless-void-operator": [0] - "@typescript-eslint/no-misused-new": [2] - "@typescript-eslint/no-misused-promises": [2, {checksVoidReturn: {attributes: false, arguments: false}}] - "@typescript-eslint/no-mixed-enums": [0] - "@typescript-eslint/no-namespace": [2] - "@typescript-eslint/no-non-null-asserted-nullish-coalescing": [0] - "@typescript-eslint/no-non-null-asserted-optional-chain": [2] - "@typescript-eslint/no-non-null-assertion": [0] - "@typescript-eslint/no-redeclare": [0] - "@typescript-eslint/no-redundant-type-constituents": [2] - "@typescript-eslint/no-require-imports": [2] - "@typescript-eslint/no-restricted-imports": [0] - "@typescript-eslint/no-restricted-types": [0] - "@typescript-eslint/no-shadow": [0] - "@typescript-eslint/no-this-alias": [0] # handled by unicorn/no-this-assignment - "@typescript-eslint/no-unnecessary-boolean-literal-compare": [0] - "@typescript-eslint/no-unnecessary-condition": [0] - "@typescript-eslint/no-unnecessary-qualifier": [0] - "@typescript-eslint/no-unnecessary-template-expression": [0] - "@typescript-eslint/no-unnecessary-type-arguments": [0] - "@typescript-eslint/no-unnecessary-type-assertion": [2] - "@typescript-eslint/no-unnecessary-type-constraint": [2] - "@typescript-eslint/no-unsafe-argument": [0] - "@typescript-eslint/no-unsafe-assignment": [0] - "@typescript-eslint/no-unsafe-call": [0] - "@typescript-eslint/no-unsafe-declaration-merging": [2] - "@typescript-eslint/no-unsafe-enum-comparison": [2] - "@typescript-eslint/no-unsafe-function-type": [2] - "@typescript-eslint/no-unsafe-member-access": [0] - "@typescript-eslint/no-unsafe-return": [0] - "@typescript-eslint/no-unsafe-unary-minus": [2] - "@typescript-eslint/no-unused-expressions": [0] - "@typescript-eslint/no-unused-vars": [2, {vars: all, args: all, caughtErrors: all, ignoreRestSiblings: false, argsIgnorePattern: ^_, varsIgnorePattern: ^_, caughtErrorsIgnorePattern: ^_, destructuredArrayIgnorePattern: ^_}] - "@typescript-eslint/no-use-before-define": [0] - "@typescript-eslint/no-useless-constructor": [0] - "@typescript-eslint/no-useless-empty-export": [0] - "@typescript-eslint/no-wrapper-object-types": [2] - "@typescript-eslint/non-nullable-type-assertion-style": [0] - "@typescript-eslint/only-throw-error": [2] - "@typescript-eslint/parameter-properties": [0] - "@typescript-eslint/prefer-as-const": [2] - "@typescript-eslint/prefer-destructuring": [0] - "@typescript-eslint/prefer-enum-initializers": [0] - "@typescript-eslint/prefer-find": [2] - "@typescript-eslint/prefer-for-of": [2] - "@typescript-eslint/prefer-function-type": [2] - "@typescript-eslint/prefer-includes": [2] - "@typescript-eslint/prefer-literal-enum-member": [0] - "@typescript-eslint/prefer-namespace-keyword": [0] - "@typescript-eslint/prefer-nullish-coalescing": [0] - "@typescript-eslint/prefer-optional-chain": [2, {requireNullish: true}] - "@typescript-eslint/prefer-promise-reject-errors": [0] - "@typescript-eslint/prefer-readonly": [0] - "@typescript-eslint/prefer-readonly-parameter-types": [0] - "@typescript-eslint/prefer-reduce-type-parameter": [0] - "@typescript-eslint/prefer-regexp-exec": [0] - "@typescript-eslint/prefer-return-this-type": [0] - "@typescript-eslint/prefer-string-starts-ends-with": [2, {allowSingleElementEquality: always}] - "@typescript-eslint/promise-function-async": [0] - "@typescript-eslint/require-array-sort-compare": [0] - "@typescript-eslint/require-await": [0] - "@typescript-eslint/restrict-plus-operands": [2] - "@typescript-eslint/restrict-template-expressions": [0] - "@typescript-eslint/return-await": [0] - "@typescript-eslint/strict-boolean-expressions": [0] - "@typescript-eslint/switch-exhaustiveness-check": [0] - "@typescript-eslint/triple-slash-reference": [2] - "@typescript-eslint/typedef": [0] - "@typescript-eslint/unbound-method": [0] # too many false-positives - "@typescript-eslint/unified-signatures": [2] - accessor-pairs: [2] - array-callback-return: [2, {checkForEach: true}] - array-func/avoid-reverse: [2] - array-func/from-map: [2] - array-func/no-unnecessary-this-arg: [2] - array-func/prefer-array-from: [2] - array-func/prefer-flat-map: [0] # handled by unicorn/prefer-array-flat-map - array-func/prefer-flat: [0] # handled by unicorn/prefer-array-flat - arrow-body-style: [0] - block-scoped-var: [2] - camelcase: [0] - capitalized-comments: [0] - class-methods-use-this: [0] - complexity: [0] - consistent-return: [0] - consistent-this: [0] - constructor-super: [2] - curly: [0] - default-case-last: [2] - default-case: [0] - default-param-last: [0] - dot-notation: [0] - eqeqeq: [2] - for-direction: [2] - func-name-matching: [2] - func-names: [0] - func-style: [0] - getter-return: [2] - github/a11y-aria-label-is-well-formatted: [0] - github/a11y-no-title-attribute: [0] - github/a11y-no-visually-hidden-interactive-element: [0] - github/a11y-role-supports-aria-props: [0] - github/a11y-svg-has-accessible-name: [0] - github/array-foreach: [0] - github/async-currenttarget: [2] - github/async-preventdefault: [2] - github/authenticity-token: [0] - github/get-attribute: [0] - github/js-class-name: [0] - github/no-blur: [0] - github/no-d-none: [0] - github/no-dataset: [2] - github/no-dynamic-script-tag: [2] - github/no-implicit-buggy-globals: [2] - github/no-inner-html: [0] - github/no-innerText: [2] - github/no-then: [2] - github/no-useless-passive: [2] - github/prefer-observers: [2] - github/require-passive-events: [2] - github/unescaped-html-literal: [0] - grouped-accessor-pairs: [2] - guard-for-in: [0] - id-blacklist: [0] - id-length: [0] - id-match: [0] - import-x/consistent-type-specifier-style: [0] - import-x/default: [0] - import-x/dynamic-import-chunkname: [0] - import-x/export: [2] - import-x/exports-last: [0] - import-x/extensions: [2, always, {ignorePackages: true}] - import-x/first: [2] - import-x/group-exports: [0] - import-x/max-dependencies: [0] - import-x/named: [2] - import-x/namespace: [0] - import-x/newline-after-import: [0] - import-x/no-absolute-path: [0] - import-x/no-amd: [2] - import-x/no-anonymous-default-export: [0] - import-x/no-commonjs: [2] - import-x/no-cycle: [2, {ignoreExternal: true, maxDepth: 1}] - import-x/no-default-export: [0] - import-x/no-deprecated: [0] - import-x/no-dynamic-require: [0] - import-x/no-empty-named-blocks: [2] - import-x/no-extraneous-dependencies: [2] - import-x/no-import-module-exports: [0] - import-x/no-internal-modules: [0] - import-x/no-mutable-exports: [0] - import-x/no-named-as-default-member: [0] - import-x/no-named-as-default: [0] - import-x/no-named-default: [0] - import-x/no-named-export: [0] - import-x/no-namespace: [0] - import-x/no-nodejs-modules: [0] - import-x/no-relative-packages: [0] - import-x/no-relative-parent-imports: [0] - import-x/no-restricted-paths: [0] - import-x/no-self-import: [2] - import-x/no-unassigned-import: [0] - import-x/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$"]}] - import-x/no-unused-modules: [2, {unusedExports: true}] - import-x/no-useless-path-segments: [2, {commonjs: true}] - import-x/no-webpack-loader-syntax: [2] - import-x/order: [0] - import-x/prefer-default-export: [0] - import-x/unambiguous: [0] - init-declarations: [0] - line-comment-position: [0] - logical-assignment-operators: [0] - max-classes-per-file: [0] - max-depth: [0] - max-lines-per-function: [0] - max-lines: [0] - max-nested-callbacks: [0] - max-params: [0] - max-statements: [0] - multiline-comment-style: [2, separate-lines] - new-cap: [0] - no-alert: [0] - no-array-constructor: [0] # handled by @typescript-eslint/no-array-constructor - no-async-promise-executor: [0] - no-await-in-loop: [0] - no-bitwise: [0] - no-buffer-constructor: [0] - no-caller: [2] - no-case-declarations: [2] - no-class-assign: [2] - no-compare-neg-zero: [2] - no-cond-assign: [2, except-parens] - no-console: [1, {allow: [debug, info, warn, error]}] - no-const-assign: [2] - no-constant-binary-expression: [2] - no-constant-condition: [0] - no-constructor-return: [2] - no-continue: [0] - no-control-regex: [0] - no-debugger: [1] - no-delete-var: [2] - no-div-regex: [0] - no-dupe-args: [2] - no-dupe-class-members: [2] - no-dupe-else-if: [2] - no-dupe-keys: [2] - no-duplicate-case: [2] - no-duplicate-imports: [0] - no-else-return: [2] - no-empty-character-class: [2] - no-empty-function: [0] - no-empty-pattern: [2] - no-empty-static-block: [2] - no-empty: [2, {allowEmptyCatch: true}] - no-eq-null: [2] - no-eval: [2] - no-ex-assign: [2] - no-extend-native: [2] - no-extra-bind: [2] - no-extra-boolean-cast: [2] - no-extra-label: [0] - no-fallthrough: [2] - no-func-assign: [2] - no-global-assign: [2] - no-implicit-coercion: [2] - no-implicit-globals: [0] - no-implied-eval: [0] # handled by @typescript-eslint/no-implied-eval - no-import-assign: [2] - no-inline-comments: [0] - no-inner-declarations: [2] - no-invalid-regexp: [2] - no-invalid-this: [0] - no-irregular-whitespace: [2] - no-iterator: [2] - no-jquery/no-ajax-events: [2] - no-jquery/no-ajax: [2] - no-jquery/no-and-self: [2] - no-jquery/no-animate-toggle: [2] - no-jquery/no-animate: [2] - no-jquery/no-append-html: [2] - no-jquery/no-attr: [2] - no-jquery/no-bind: [2] - no-jquery/no-box-model: [2] - no-jquery/no-browser: [2] - no-jquery/no-camel-case: [2] - no-jquery/no-class-state: [2] - no-jquery/no-class: [0] - no-jquery/no-clone: [2] - no-jquery/no-closest: [0] - no-jquery/no-constructor-attributes: [2] - no-jquery/no-contains: [2] - no-jquery/no-context-prop: [2] - no-jquery/no-css: [2] - no-jquery/no-data: [0] - no-jquery/no-deferred: [2] - no-jquery/no-delegate: [2] - no-jquery/no-done-fail: [2] - no-jquery/no-each-collection: [0] - no-jquery/no-each-util: [0] - no-jquery/no-each: [0] - no-jquery/no-error-shorthand: [2] - no-jquery/no-error: [2] - no-jquery/no-escape-selector: [2] - no-jquery/no-event-shorthand: [2] - no-jquery/no-extend: [2] - no-jquery/no-fade: [2] - no-jquery/no-filter: [0] - no-jquery/no-find-collection: [0] - no-jquery/no-find-util: [2] - no-jquery/no-find: [0] - no-jquery/no-fx-interval: [2] - no-jquery/no-fx: [2] - no-jquery/no-global-eval: [2] - no-jquery/no-global-selector: [0] - no-jquery/no-grep: [2] - no-jquery/no-has: [2] - no-jquery/no-hold-ready: [2] - no-jquery/no-html: [0] - no-jquery/no-in-array: [2] - no-jquery/no-is-array: [2] - no-jquery/no-is-empty-object: [2] - no-jquery/no-is-function: [2] - no-jquery/no-is-numeric: [2] - no-jquery/no-is-plain-object: [2] - no-jquery/no-is-window: [2] - no-jquery/no-is: [2] - no-jquery/no-jquery-constructor: [0] - no-jquery/no-live: [2] - no-jquery/no-load-shorthand: [2] - no-jquery/no-load: [2] - no-jquery/no-map-collection: [0] - no-jquery/no-map-util: [2] - no-jquery/no-map: [2] - no-jquery/no-merge: [2] - no-jquery/no-node-name: [2] - no-jquery/no-noop: [2] - no-jquery/no-now: [2] - no-jquery/no-on-ready: [2] - no-jquery/no-other-methods: [0] - no-jquery/no-other-utils: [2] - no-jquery/no-param: [2] - no-jquery/no-parent: [0] - no-jquery/no-parents: [2] - no-jquery/no-parse-html-literal: [2] - no-jquery/no-parse-html: [2] - no-jquery/no-parse-json: [2] - no-jquery/no-parse-xml: [2] - no-jquery/no-prop: [2] - no-jquery/no-proxy: [2] - no-jquery/no-ready-shorthand: [2] - no-jquery/no-ready: [2] - no-jquery/no-selector-prop: [2] - no-jquery/no-serialize: [2] - no-jquery/no-size: [2] - no-jquery/no-sizzle: [2] - no-jquery/no-slide: [2] - no-jquery/no-sub: [2] - no-jquery/no-support: [2] - no-jquery/no-text: [2] - no-jquery/no-trigger: [0] - no-jquery/no-trim: [2] - no-jquery/no-type: [2] - no-jquery/no-unique: [2] - no-jquery/no-unload-shorthand: [2] - no-jquery/no-val: [0] - no-jquery/no-visibility: [2] - no-jquery/no-when: [2] - no-jquery/no-wrap: [2] - no-jquery/variable-pattern: [2] - no-label-var: [2] - no-labels: [0] # handled by no-restricted-syntax - no-lone-blocks: [2] - no-lonely-if: [0] - no-loop-func: [0] - no-loss-of-precision: [2] - no-magic-numbers: [0] - no-misleading-character-class: [2] - no-multi-assign: [0] - no-multi-str: [2] - no-negated-condition: [0] - no-nested-ternary: [0] - no-new-func: [2] - no-new-native-nonconstructor: [2] - no-new-object: [2] - no-new-symbol: [2] - no-new-wrappers: [2] - no-new: [0] - no-nonoctal-decimal-escape: [2] - no-obj-calls: [2] - no-octal-escape: [2] - no-octal: [2] - no-param-reassign: [0] - no-plusplus: [0] - no-promise-executor-return: [0] - no-proto: [2] - no-prototype-builtins: [2] - no-redeclare: [0] # must be disabled for typescript overloads - no-regex-spaces: [2] - no-restricted-exports: [0] - no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, self, status, statusbar, stop, toolbar, top, __dirname, __filename] - no-restricted-imports: [0] - no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression, {selector: "CallExpression[callee.name='fetch']", message: "use modules/fetch.ts instead"}] - no-return-assign: [0] - no-script-url: [2] - no-self-assign: [2, {props: true}] - no-self-compare: [2] - no-sequences: [2] - no-setter-return: [2] - no-shadow-restricted-names: [2] - no-shadow: [0] - no-sparse-arrays: [2] - no-template-curly-in-string: [2] - no-ternary: [0] - no-this-before-super: [2] - no-throw-literal: [2] - no-undef-init: [2] - no-undef: [2, {typeof: true}] # TODO: disable this rule after tsc passes - no-undefined: [0] - no-underscore-dangle: [0] - no-unexpected-multiline: [2] - no-unmodified-loop-condition: [2] - no-unneeded-ternary: [2] - no-unreachable-loop: [2] - no-unreachable: [2] - no-unsafe-finally: [2] - no-unsafe-negation: [2] - no-unused-expressions: [2] - no-unused-labels: [2] - no-unused-private-class-members: [2] - no-unused-vars: [0] # handled by @typescript-eslint/no-unused-vars - no-use-before-define: [2, {functions: false, classes: true, variables: true, allowNamedExports: true}] - no-use-extend-native/no-use-extend-native: [2] - no-useless-backreference: [2] - no-useless-call: [2] - no-useless-catch: [2] - no-useless-computed-key: [2] - no-useless-concat: [2] - no-useless-constructor: [2] - no-useless-escape: [2] - no-useless-rename: [2] - no-useless-return: [2] - no-var: [2] - no-void: [2] - no-warning-comments: [0] - no-with: [0] # handled by no-restricted-syntax - object-shorthand: [2, always] - one-var-declaration-per-line: [0] - one-var: [0] - operator-assignment: [2, always] - operator-linebreak: [2, after] - prefer-arrow-callback: [2, {allowNamedFunctions: true, allowUnboundThis: true}] - prefer-const: [2, {destructuring: all, ignoreReadBeforeAssign: true}] - prefer-destructuring: [0] - prefer-exponentiation-operator: [2] - prefer-named-capture-group: [0] - prefer-numeric-literals: [2] - prefer-object-has-own: [2] - prefer-object-spread: [2] - prefer-promise-reject-errors: [2, {allowEmptyReject: false}] - prefer-regex-literals: [2] - prefer-rest-params: [2] - prefer-spread: [2] - prefer-template: [2] - radix: [2, as-needed] - regexp/confusing-quantifier: [2] - regexp/control-character-escape: [2] - regexp/hexadecimal-escape: [0] - regexp/letter-case: [0] - regexp/match-any: [2] - regexp/negation: [2] - regexp/no-contradiction-with-assertion: [0] - regexp/no-control-character: [0] - regexp/no-dupe-characters-character-class: [2] - regexp/no-dupe-disjunctions: [2] - regexp/no-empty-alternative: [2] - regexp/no-empty-capturing-group: [2] - regexp/no-empty-character-class: [0] - regexp/no-empty-group: [2] - regexp/no-empty-lookarounds-assertion: [2] - regexp/no-empty-string-literal: [2] - regexp/no-escape-backspace: [2] - regexp/no-extra-lookaround-assertions: [0] - regexp/no-invalid-regexp: [2] - regexp/no-invisible-character: [2] - regexp/no-lazy-ends: [2] - regexp/no-legacy-features: [2] - regexp/no-misleading-capturing-group: [0] - regexp/no-misleading-unicode-character: [0] - regexp/no-missing-g-flag: [2] - regexp/no-non-standard-flag: [2] - regexp/no-obscure-range: [2] - regexp/no-octal: [2] - regexp/no-optional-assertion: [2] - regexp/no-potentially-useless-backreference: [2] - regexp/no-standalone-backslash: [2] - regexp/no-super-linear-backtracking: [0] - regexp/no-super-linear-move: [0] - regexp/no-trivially-nested-assertion: [2] - regexp/no-trivially-nested-quantifier: [2] - regexp/no-unused-capturing-group: [0] - regexp/no-useless-assertions: [2] - regexp/no-useless-backreference: [2] - regexp/no-useless-character-class: [2] - regexp/no-useless-dollar-replacements: [2] - regexp/no-useless-escape: [2] - regexp/no-useless-flag: [2] - regexp/no-useless-lazy: [2] - regexp/no-useless-non-capturing-group: [2] - regexp/no-useless-quantifier: [2] - regexp/no-useless-range: [2] - regexp/no-useless-set-operand: [2] - regexp/no-useless-string-literal: [2] - regexp/no-useless-two-nums-quantifier: [2] - regexp/no-zero-quantifier: [2] - regexp/optimal-lookaround-quantifier: [2] - regexp/optimal-quantifier-concatenation: [0] - regexp/prefer-character-class: [0] - regexp/prefer-d: [0] - regexp/prefer-escape-replacement-dollar-char: [0] - regexp/prefer-lookaround: [0] - regexp/prefer-named-backreference: [0] - regexp/prefer-named-capture-group: [0] - regexp/prefer-named-replacement: [0] - regexp/prefer-plus-quantifier: [2] - regexp/prefer-predefined-assertion: [2] - regexp/prefer-quantifier: [0] - regexp/prefer-question-quantifier: [2] - regexp/prefer-range: [2] - regexp/prefer-regexp-exec: [2] - regexp/prefer-regexp-test: [2] - regexp/prefer-result-array-groups: [0] - regexp/prefer-set-operation: [2] - regexp/prefer-star-quantifier: [2] - regexp/prefer-unicode-codepoint-escapes: [2] - regexp/prefer-w: [0] - regexp/require-unicode-regexp: [0] - regexp/simplify-set-operations: [2] - regexp/sort-alternatives: [0] - regexp/sort-character-class-elements: [0] - regexp/sort-flags: [0] - regexp/strict: [2] - regexp/unicode-escape: [0] - regexp/use-ignore-case: [0] - require-atomic-updates: [0] - require-await: [0] # handled by @typescript-eslint/require-await - require-unicode-regexp: [0] - require-yield: [2] - sonarjs/cognitive-complexity: [0] - sonarjs/elseif-without-else: [0] - sonarjs/max-switch-cases: [0] - sonarjs/no-all-duplicated-branches: [2] - sonarjs/no-collapsible-if: [0] - sonarjs/no-collection-size-mischeck: [2] - sonarjs/no-duplicate-string: [0] - sonarjs/no-duplicated-branches: [0] - sonarjs/no-element-overwrite: [2] - sonarjs/no-empty-collection: [2] - sonarjs/no-extra-arguments: [2] - sonarjs/no-gratuitous-expressions: [2] - sonarjs/no-identical-conditions: [2] - sonarjs/no-identical-expressions: [2] - sonarjs/no-identical-functions: [2, 5] - sonarjs/no-ignored-return: [2] - sonarjs/no-inverted-boolean-check: [2] - sonarjs/no-nested-switch: [0] - sonarjs/no-nested-template-literals: [0] - sonarjs/no-one-iteration-loop: [2] - sonarjs/no-redundant-boolean: [2] - sonarjs/no-redundant-jump: [2] - sonarjs/no-same-line-conditional: [2] - sonarjs/no-small-switch: [0] - sonarjs/no-unused-collection: [2] - sonarjs/no-use-of-empty-return-value: [2] - sonarjs/no-useless-catch: [2] - sonarjs/non-existent-operator: [2] - sonarjs/prefer-immediate-return: [0] - sonarjs/prefer-object-literal: [0] - sonarjs/prefer-single-boolean-return: [0] - sonarjs/prefer-while: [2] - sort-imports: [0] - sort-keys: [0] - sort-vars: [0] - strict: [0] - symbol-description: [2] - unicode-bom: [2, never] - unicorn/better-regex: [0] - unicorn/catch-error-name: [0] - unicorn/consistent-destructuring: [2] - unicorn/consistent-empty-array-spread: [2] - unicorn/consistent-existence-index-check: [0] - unicorn/consistent-function-scoping: [0] - unicorn/custom-error-definition: [0] - unicorn/empty-brace-spaces: [2] - unicorn/error-message: [0] - unicorn/escape-case: [0] - unicorn/expiring-todo-comments: [0] - unicorn/explicit-length-check: [0] - unicorn/filename-case: [0] - unicorn/import-index: [0] - unicorn/import-style: [0] - unicorn/new-for-builtins: [2] - unicorn/no-abusive-eslint-disable: [0] - unicorn/no-anonymous-default-export: [0] - unicorn/no-array-callback-reference: [0] - unicorn/no-array-for-each: [2] - unicorn/no-array-method-this-argument: [2] - unicorn/no-array-push-push: [2] - unicorn/no-array-reduce: [2] - unicorn/no-await-expression-member: [0] - unicorn/no-await-in-promise-methods: [2] - unicorn/no-console-spaces: [0] - unicorn/no-document-cookie: [2] - unicorn/no-empty-file: [2] - unicorn/no-for-loop: [0] - unicorn/no-hex-escape: [0] - unicorn/no-instanceof-array: [0] - unicorn/no-invalid-fetch-options: [2] - unicorn/no-invalid-remove-event-listener: [2] - unicorn/no-keyword-prefix: [0] - unicorn/no-length-as-slice-end: [2] - unicorn/no-lonely-if: [2] - unicorn/no-magic-array-flat-depth: [0] - unicorn/no-negated-condition: [0] - unicorn/no-negation-in-equality-check: [2] - unicorn/no-nested-ternary: [0] - unicorn/no-new-array: [0] - unicorn/no-new-buffer: [0] - unicorn/no-null: [0] - unicorn/no-object-as-default-parameter: [0] - unicorn/no-process-exit: [0] - unicorn/no-single-promise-in-promise-methods: [2] - unicorn/no-static-only-class: [2] - unicorn/no-thenable: [2] - unicorn/no-this-assignment: [2] - unicorn/no-typeof-undefined: [2] - unicorn/no-unnecessary-await: [2] - unicorn/no-unnecessary-polyfills: [2] - unicorn/no-unreadable-array-destructuring: [0] - unicorn/no-unreadable-iife: [2] - unicorn/no-unused-properties: [2] - unicorn/no-useless-fallback-in-spread: [2] - unicorn/no-useless-length-check: [2] - unicorn/no-useless-promise-resolve-reject: [2] - unicorn/no-useless-spread: [2] - unicorn/no-useless-switch-case: [2] - unicorn/no-useless-undefined: [0] - unicorn/no-zero-fractions: [2] - unicorn/number-literal-case: [0] - unicorn/numeric-separators-style: [0] - unicorn/prefer-add-event-listener: [2] - unicorn/prefer-array-find: [2] - unicorn/prefer-array-flat-map: [2] - unicorn/prefer-array-flat: [2] - unicorn/prefer-array-index-of: [2] - unicorn/prefer-array-some: [2] - unicorn/prefer-at: [0] - unicorn/prefer-blob-reading-methods: [2] - unicorn/prefer-code-point: [0] - unicorn/prefer-date-now: [2] - unicorn/prefer-default-parameters: [0] - unicorn/prefer-dom-node-append: [2] - unicorn/prefer-dom-node-dataset: [0] - unicorn/prefer-dom-node-remove: [2] - unicorn/prefer-dom-node-text-content: [2] - unicorn/prefer-event-target: [2] - unicorn/prefer-export-from: [0] - unicorn/prefer-global-this: [0] - unicorn/prefer-includes: [2] - unicorn/prefer-json-parse-buffer: [0] - unicorn/prefer-keyboard-event-key: [2] - unicorn/prefer-logical-operator-over-ternary: [2] - unicorn/prefer-math-min-max: [2] - unicorn/prefer-math-trunc: [2] - unicorn/prefer-modern-dom-apis: [0] - unicorn/prefer-modern-math-apis: [2] - unicorn/prefer-module: [2] - unicorn/prefer-native-coercion-functions: [2] - unicorn/prefer-negative-index: [2] - unicorn/prefer-node-protocol: [2] - unicorn/prefer-number-properties: [0] - unicorn/prefer-object-from-entries: [2] - unicorn/prefer-object-has-own: [0] - unicorn/prefer-optional-catch-binding: [2] - unicorn/prefer-prototype-methods: [0] - unicorn/prefer-query-selector: [2] - unicorn/prefer-reflect-apply: [0] - unicorn/prefer-regexp-test: [2] - unicorn/prefer-set-has: [0] - unicorn/prefer-set-size: [2] - unicorn/prefer-spread: [0] - unicorn/prefer-string-raw: [0] - unicorn/prefer-string-replace-all: [0] - unicorn/prefer-string-slice: [0] - unicorn/prefer-string-starts-ends-with: [2] - unicorn/prefer-string-trim-start-end: [2] - unicorn/prefer-structured-clone: [2] - unicorn/prefer-switch: [0] - unicorn/prefer-ternary: [0] - unicorn/prefer-text-content: [2] - unicorn/prefer-top-level-await: [0] - unicorn/prefer-type-error: [0] - unicorn/prevent-abbreviations: [0] - unicorn/relative-url-style: [2] - unicorn/require-array-join-separator: [2] - unicorn/require-number-to-fixed-digits-argument: [2] - unicorn/require-post-message-target-origin: [0] - unicorn/string-content: [0] - unicorn/switch-case-braces: [0] - unicorn/template-indent: [2] - unicorn/text-encoding-identifier-case: [0] - unicorn/throw-new-error: [2] - use-isnan: [2] - valid-typeof: [2, {requireStringLiterals: true}] - vars-on-top: [0] - wc/attach-shadow-constructor: [2] - wc/define-tag-after-class-definition: [0] - wc/expose-class-on-global: [0] - wc/file-name-matches-element: [2] - wc/guard-define-call: [0] - wc/guard-super-call: [2] - wc/max-elements-per-file: [0] - wc/no-child-traversal-in-attributechangedcallback: [2] - wc/no-child-traversal-in-connectedcallback: [2] - wc/no-closed-shadow-root: [2] - wc/no-constructor-attributes: [2] - wc/no-constructor-params: [2] - wc/no-constructor: [2] - wc/no-customized-built-in-elements: [2] - wc/no-exports-with-element: [0] - wc/no-invalid-element-name: [2] - wc/no-invalid-extends: [2] - wc/no-method-prefixed-with-on: [2] - wc/no-self-class: [2] - wc/no-typos: [2] - wc/require-listener-teardown: [2] - wc/tag-name-matches-class: [2] - yoda: [2, never] From b9457422934f33a37e0285d100007aae12e87ff7 Mon Sep 17 00:00:00 2001 From: TheFox0x7 Date: Wed, 18 Dec 2024 04:08:04 +0100 Subject: [PATCH 05/50] Change pprof labels to be prometheus compatible (#32865) Enables scrapping pprof endpoint for continuous profiling Closes: https://github.com/go-gitea/gitea/issues/32854 --- modules/graceful/manager.go | 6 +++--- modules/graceful/manager_common.go | 14 ++++++++++---- modules/process/context.go | 2 +- modules/process/manager.go | 4 ++-- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/modules/graceful/manager.go b/modules/graceful/manager.go index 991b2f2b7a..cac6ce62d6 100644 --- a/modules/graceful/manager.go +++ b/modules/graceful/manager.go @@ -136,7 +136,7 @@ func (g *Manager) doShutdown() { } g.lock.Lock() g.shutdownCtxCancel() - atShutdownCtx := pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "post-shutdown")) + atShutdownCtx := pprof.WithLabels(g.hammerCtx, pprof.Labels(LifecyclePProfLabel, "post-shutdown")) pprof.SetGoroutineLabels(atShutdownCtx) for _, fn := range g.toRunAtShutdown { go fn() @@ -167,7 +167,7 @@ func (g *Manager) doHammerTime(d time.Duration) { default: log.Warn("Setting Hammer condition") g.hammerCtxCancel() - atHammerCtx := pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "post-hammer")) + atHammerCtx := pprof.WithLabels(g.terminateCtx, pprof.Labels(LifecyclePProfLabel, "post-hammer")) pprof.SetGoroutineLabels(atHammerCtx) } g.lock.Unlock() @@ -183,7 +183,7 @@ func (g *Manager) doTerminate() { default: log.Warn("Terminating") g.terminateCtxCancel() - atTerminateCtx := pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "post-terminate")) + atTerminateCtx := pprof.WithLabels(g.managerCtx, pprof.Labels(LifecyclePProfLabel, "post-terminate")) pprof.SetGoroutineLabels(atTerminateCtx) for _, fn := range g.toRunAtTerminate { diff --git a/modules/graceful/manager_common.go b/modules/graceful/manager_common.go index f6dbcc748d..15dd4406d5 100644 --- a/modules/graceful/manager_common.go +++ b/modules/graceful/manager_common.go @@ -22,6 +22,12 @@ const ( watchdogMsg systemdNotifyMsg = "WATCHDOG=1" ) +// LifecyclePProfLabel is a label marking manager lifecycle phase +// Making it compliant with prometheus key regex https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels +// would enable someone interested to be able to to continuously gather profiles into pyroscope. +// Other labels for pprof (in "modules/process" package) should also follow this rule. +const LifecyclePProfLabel = "graceful_lifecycle" + func statusMsg(msg string) systemdNotifyMsg { return systemdNotifyMsg("STATUS=" + msg) } @@ -65,10 +71,10 @@ func (g *Manager) prepare(ctx context.Context) { g.hammerCtx, g.hammerCtxCancel = context.WithCancel(ctx) g.managerCtx, g.managerCtxCancel = context.WithCancel(ctx) - g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate")) - g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown")) - g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer")) - g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager")) + g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels(LifecyclePProfLabel, "with-terminate")) + g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels(LifecyclePProfLabel, "with-shutdown")) + g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels(LifecyclePProfLabel, "with-hammer")) + g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels(LifecyclePProfLabel, "with-manager")) if !g.setStateTransition(stateInit, stateRunning) { panic("invalid graceful manager state: transition from init to running failed") diff --git a/modules/process/context.go b/modules/process/context.go index 26a80ebd62..1854988bce 100644 --- a/modules/process/context.go +++ b/modules/process/context.go @@ -32,7 +32,7 @@ func (c *Context) Value(key any) any { } // ProcessContextKey is the key under which process contexts are stored -var ProcessContextKey any = "process-context" +var ProcessContextKey any = "process_context" // GetContext will return a process context if one exists func GetContext(ctx context.Context) *Context { diff --git a/modules/process/manager.go b/modules/process/manager.go index bdc4931810..7b8ada786e 100644 --- a/modules/process/manager.go +++ b/modules/process/manager.go @@ -26,7 +26,7 @@ var ( ) // DescriptionPProfLabel is a label set on goroutines that have a process attached -const DescriptionPProfLabel = "process-description" +const DescriptionPProfLabel = "process_description" // PIDPProfLabel is a label set on goroutines that have a process attached const PIDPProfLabel = "pid" @@ -35,7 +35,7 @@ const PIDPProfLabel = "pid" const PPIDPProfLabel = "ppid" // ProcessTypePProfLabel is a label set on goroutines that have a process attached -const ProcessTypePProfLabel = "process-type" +const ProcessTypePProfLabel = "process_type" // IDType is a pid type type IDType string From e4c4629465f4e45ffdb9ef5eff6ef17e15ee4c2d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 17 Dec 2024 19:44:16 -0800 Subject: [PATCH 06/50] Move RepoTransfer from models to models/repo sub package (#32506) `RepoTransfer` now is at models, but if we want to move it into `repo` model, it will depend on `Team`. So this PR also makes repo model depend on org model to make it possible. Just refactor, no code change. - [x] Move `DeleteOrganization` from `models/organization` to service layer - [x] Move `AccessibleTeamReposEnv` to `models/repo` - [x] Move `RepoTransfer` from `models` to `models/repo` - [x] Merge `getUserTeamIDs` and `GetUserTeamIDs`, Merge `GetUserTeams` and `getUserTeams`. - [x] Remove `Team`'s `Repos []*repo_model.Repository` to avoid dependency recycle. --- models/activities/action.go | 2 +- models/error.go | 42 ---- models/organization/org.go | 209 +----------------- models/organization/org_repo.go | 17 -- models/organization/org_test.go | 8 +- models/organization/team.go | 17 +- models/organization/team_list.go | 7 +- models/organization/team_repo.go | 26 --- models/organization/team_test.go | 10 +- models/repo/org_repo.go | 206 +++++++++++++++++ models/{repo_transfer.go => repo/transfer.go} | 66 +++++- routers/api/v1/org/team.go | 6 +- routers/api/v1/repo/teams.go | 2 +- routers/api/v1/repo/transfer.go | 7 +- routers/web/org/teams.go | 8 +- routers/web/repo/repo.go | 3 +- routers/web/repo/setting/collaboration.go | 2 +- routers/web/repo/setting/setting.go | 6 +- services/context/repo.go | 3 +- services/convert/repository.go | 7 +- services/org/org.go | 31 ++- services/org/team.go | 29 ++- services/org/team_test.go | 11 +- services/org/user.go | 2 +- services/repository/repo_team.go | 11 +- services/repository/transfer.go | 9 +- services/repository/transfer_test.go | 17 +- services/user/block.go | 3 +- templates/org/team/repositories.tmpl | 2 +- tests/integration/api_user_block_test.go | 3 +- 30 files changed, 383 insertions(+), 389 deletions(-) delete mode 100644 models/organization/org_repo.go create mode 100644 models/repo/org_repo.go rename models/{repo_transfer.go => repo/transfer.go} (73%) diff --git a/models/activities/action.go b/models/activities/action.go index 65d95fbe66..ff7fdb2f10 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -511,7 +511,7 @@ func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder. } if opts.RequestedTeam != nil { - env := organization.OrgFromUser(opts.RequestedUser).AccessibleTeamReposEnv(ctx, opts.RequestedTeam) + env := repo_model.AccessibleTeamReposEnv(ctx, organization.OrgFromUser(opts.RequestedUser), opts.RequestedTeam) teamRepoIDs, err := env.RepoIDs(1, opts.RequestedUser.NumRepos) if err != nil { return nil, fmt.Errorf("GetTeamRepositories: %w", err) diff --git a/models/error.go b/models/error.go index 75c53245de..865d5a442f 100644 --- a/models/error.go +++ b/models/error.go @@ -72,48 +72,6 @@ func (err ErrDeleteLastAdminUser) Error() string { return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID) } -// ErrNoPendingRepoTransfer is an error type for repositories without a pending -// transfer request -type ErrNoPendingRepoTransfer struct { - RepoID int64 -} - -func (err ErrNoPendingRepoTransfer) Error() string { - return fmt.Sprintf("repository doesn't have a pending transfer [repo_id: %d]", err.RepoID) -} - -// IsErrNoPendingTransfer is an error type when a repository has no pending -// transfers -func IsErrNoPendingTransfer(err error) bool { - _, ok := err.(ErrNoPendingRepoTransfer) - return ok -} - -func (err ErrNoPendingRepoTransfer) Unwrap() error { - return util.ErrNotExist -} - -// ErrRepoTransferInProgress represents the state of a repository that has an -// ongoing transfer -type ErrRepoTransferInProgress struct { - Uname string - Name string -} - -// IsErrRepoTransferInProgress checks if an error is a ErrRepoTransferInProgress. -func IsErrRepoTransferInProgress(err error) bool { - _, ok := err.(ErrRepoTransferInProgress) - return ok -} - -func (err ErrRepoTransferInProgress) Error() string { - return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name) -} - -func (err ErrRepoTransferInProgress) Unwrap() error { - return util.ErrAlreadyExist -} - // ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error. type ErrInvalidCloneAddr struct { Host string diff --git a/models/organization/org.go b/models/organization/org.go index 725a99356e..3e55a36758 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -9,11 +9,8 @@ import ( "fmt" "strings" - actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" - repo_model "code.gitea.io/gitea/models/repo" - secret_model "code.gitea.io/gitea/models/secret" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" @@ -407,33 +404,6 @@ func GetOrgByName(ctx context.Context, name string) (*Organization, error) { return u, nil } -// DeleteOrganization deletes models associated to an organization. -func DeleteOrganization(ctx context.Context, org *Organization) error { - if org.Type != user_model.UserTypeOrganization { - return fmt.Errorf("%s is a user not an organization", org.Name) - } - - if err := db.DeleteBeans(ctx, - &Team{OrgID: org.ID}, - &OrgUser{OrgID: org.ID}, - &TeamUser{OrgID: org.ID}, - &TeamUnit{OrgID: org.ID}, - &TeamInvite{OrgID: org.ID}, - &secret_model.Secret{OwnerID: org.ID}, - &user_model.Blocking{BlockerID: org.ID}, - &actions_model.ActionRunner{OwnerID: org.ID}, - &actions_model.ActionRunnerToken{OwnerID: org.ID}, - ); err != nil { - return fmt.Errorf("DeleteBeans: %w", err) - } - - if _, err := db.GetEngine(ctx).ID(org.ID).Delete(new(user_model.User)); err != nil { - return fmt.Errorf("Delete: %w", err) - } - - return nil -} - // GetOrgUserMaxAuthorizeLevel returns highest authorize level of user in an organization func (org *Organization) GetOrgUserMaxAuthorizeLevel(ctx context.Context, uid int64) (perm.AccessMode, error) { var authorize perm.AccessMode @@ -604,7 +574,9 @@ func RemoveOrgRepo(ctx context.Context, orgID, repoID int64) error { return err } -func (org *Organization) getUserTeams(ctx context.Context, userID int64, cols ...string) ([]*Team, error) { +// GetUserTeams returns all teams that belong to user, +// and that the user has joined. +func (org *Organization) GetUserTeams(ctx context.Context, userID int64, cols ...string) ([]*Team, error) { teams := make([]*Team, 0, org.NumTeams) return teams, db.GetEngine(ctx). Where("`team_user`.org_id = ?", org.ID). @@ -616,7 +588,8 @@ func (org *Organization) getUserTeams(ctx context.Context, userID int64, cols .. Find(&teams) } -func (org *Organization) getUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) { +// GetUserTeamIDs returns of all team IDs of the organization that user is member of. +func (org *Organization) GetUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) { teamIDs := make([]int64, 0, org.NumTeams) return teamIDs, db.GetEngine(ctx). Table("team"). @@ -640,175 +613,3 @@ func getUserTeamIDsQueryBuilder(orgID, userID int64) *builder.Builder { func (org *Organization) TeamsWithAccessToRepo(ctx context.Context, repoID int64, mode perm.AccessMode) ([]*Team, error) { return GetTeamsWithAccessToRepo(ctx, org.ID, repoID, mode) } - -// GetUserTeamIDs returns of all team IDs of the organization that user is member of. -func (org *Organization) GetUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) { - return org.getUserTeamIDs(ctx, userID) -} - -// GetUserTeams returns all teams that belong to user, -// and that the user has joined. -func (org *Organization) GetUserTeams(ctx context.Context, userID int64) ([]*Team, error) { - return org.getUserTeams(ctx, userID) -} - -// AccessibleReposEnvironment operations involving the repositories that are -// accessible to a particular user -type AccessibleReposEnvironment interface { - CountRepos() (int64, error) - RepoIDs(page, pageSize int) ([]int64, error) - Repos(page, pageSize int) (repo_model.RepositoryList, error) - MirrorRepos() (repo_model.RepositoryList, error) - AddKeyword(keyword string) - SetSort(db.SearchOrderBy) -} - -type accessibleReposEnv struct { - org *Organization - user *user_model.User - team *Team - teamIDs []int64 - ctx context.Context - keyword string - orderBy db.SearchOrderBy -} - -// AccessibleReposEnv builds an AccessibleReposEnvironment for the repositories in `org` -// that are accessible to the specified user. -func AccessibleReposEnv(ctx context.Context, org *Organization, userID int64) (AccessibleReposEnvironment, error) { - var user *user_model.User - - if userID > 0 { - u, err := user_model.GetUserByID(ctx, userID) - if err != nil { - return nil, err - } - user = u - } - - teamIDs, err := org.getUserTeamIDs(ctx, userID) - if err != nil { - return nil, err - } - return &accessibleReposEnv{ - org: org, - user: user, - teamIDs: teamIDs, - ctx: ctx, - orderBy: db.SearchOrderByRecentUpdated, - }, nil -} - -// AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org` -// that are accessible to the specified team. -func (org *Organization) AccessibleTeamReposEnv(ctx context.Context, team *Team) AccessibleReposEnvironment { - return &accessibleReposEnv{ - org: org, - team: team, - ctx: ctx, - orderBy: db.SearchOrderByRecentUpdated, - } -} - -func (env *accessibleReposEnv) cond() builder.Cond { - cond := builder.NewCond() - if env.team != nil { - cond = cond.And(builder.Eq{"team_repo.team_id": env.team.ID}) - } else { - if env.user == nil || !env.user.IsRestricted { - cond = cond.Or(builder.Eq{ - "`repository`.owner_id": env.org.ID, - "`repository`.is_private": false, - }) - } - if len(env.teamIDs) > 0 { - cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs)) - } - } - if env.keyword != "" { - cond = cond.And(builder.Like{"`repository`.lower_name", strings.ToLower(env.keyword)}) - } - return cond -} - -func (env *accessibleReposEnv) CountRepos() (int64, error) { - repoCount, err := db.GetEngine(env.ctx). - Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id"). - Where(env.cond()). - Distinct("`repository`.id"). - Count(&repo_model.Repository{}) - if err != nil { - return 0, fmt.Errorf("count user repositories in organization: %w", err) - } - return repoCount, nil -} - -func (env *accessibleReposEnv) RepoIDs(page, pageSize int) ([]int64, error) { - if page <= 0 { - page = 1 - } - - repoIDs := make([]int64, 0, pageSize) - return repoIDs, db.GetEngine(env.ctx). - Table("repository"). - Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id"). - Where(env.cond()). - GroupBy("`repository`.id,`repository`."+strings.Fields(string(env.orderBy))[0]). - OrderBy(string(env.orderBy)). - Limit(pageSize, (page-1)*pageSize). - Cols("`repository`.id"). - Find(&repoIDs) -} - -func (env *accessibleReposEnv) Repos(page, pageSize int) (repo_model.RepositoryList, error) { - repoIDs, err := env.RepoIDs(page, pageSize) - if err != nil { - return nil, fmt.Errorf("GetUserRepositoryIDs: %w", err) - } - - repos := make([]*repo_model.Repository, 0, len(repoIDs)) - if len(repoIDs) == 0 { - return repos, nil - } - - return repos, db.GetEngine(env.ctx). - In("`repository`.id", repoIDs). - OrderBy(string(env.orderBy)). - Find(&repos) -} - -func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) { - repoIDs := make([]int64, 0, 10) - return repoIDs, db.GetEngine(env.ctx). - Table("repository"). - Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true). - Where(env.cond()). - GroupBy("`repository`.id, `repository`.updated_unix"). - OrderBy(string(env.orderBy)). - Cols("`repository`.id"). - Find(&repoIDs) -} - -func (env *accessibleReposEnv) MirrorRepos() (repo_model.RepositoryList, error) { - repoIDs, err := env.MirrorRepoIDs() - if err != nil { - return nil, fmt.Errorf("MirrorRepoIDs: %w", err) - } - - repos := make([]*repo_model.Repository, 0, len(repoIDs)) - if len(repoIDs) == 0 { - return repos, nil - } - - return repos, db.GetEngine(env.ctx). - In("`repository`.id", repoIDs). - Find(&repos) -} - -func (env *accessibleReposEnv) AddKeyword(keyword string) { - env.keyword = keyword -} - -func (env *accessibleReposEnv) SetSort(orderBy db.SearchOrderBy) { - env.orderBy = orderBy -} diff --git a/models/organization/org_repo.go b/models/organization/org_repo.go deleted file mode 100644 index f7e59928f4..0000000000 --- a/models/organization/org_repo.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package organization - -import ( - "context" - - "code.gitea.io/gitea/models/db" - repo_model "code.gitea.io/gitea/models/repo" -) - -// GetOrgRepositories get repos belonging to the given organization -func GetOrgRepositories(ctx context.Context, orgID int64) (repo_model.RepositoryList, error) { - var orgRepos []*repo_model.Repository - return orgRepos, db.GetEngine(ctx).Where("owner_id = ?", orgID).Find(&orgRepos) -} diff --git a/models/organization/org_test.go b/models/organization/org_test.go index 5e99e88689..2c5b4090df 100644 --- a/models/organization/org_test.go +++ b/models/organization/org_test.go @@ -318,7 +318,7 @@ func TestAccessibleReposEnv_CountRepos(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}) testSuccess := func(userID, expectedCount int64) { - env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID) + env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID) assert.NoError(t, err) count, err := env.CountRepos() assert.NoError(t, err) @@ -332,7 +332,7 @@ func TestAccessibleReposEnv_RepoIDs(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}) testSuccess := func(userID int64, expectedRepoIDs []int64) { - env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID) + env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID) assert.NoError(t, err) repoIDs, err := env.RepoIDs(1, 100) assert.NoError(t, err) @@ -346,7 +346,7 @@ func TestAccessibleReposEnv_Repos(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}) testSuccess := func(userID int64, expectedRepoIDs []int64) { - env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID) + env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID) assert.NoError(t, err) repos, err := env.Repos(1, 100) assert.NoError(t, err) @@ -365,7 +365,7 @@ func TestAccessibleReposEnv_MirrorRepos(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}) testSuccess := func(userID int64, expectedRepoIDs []int64) { - env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID) + env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID) assert.NoError(t, err) repos, err := env.MirrorRepos() assert.NoError(t, err) diff --git a/models/organization/team.go b/models/organization/team.go index fb7f0c0493..96666da39a 100644 --- a/models/organization/team.go +++ b/models/organization/team.go @@ -11,7 +11,6 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" - repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" @@ -78,9 +77,8 @@ type Team struct { LowerName string Name string Description string - AccessMode perm.AccessMode `xorm:"'authorize'"` - Repos []*repo_model.Repository `xorm:"-"` - Members []*user_model.User `xorm:"-"` + AccessMode perm.AccessMode `xorm:"'authorize'"` + Members []*user_model.User `xorm:"-"` NumRepos int NumMembers int Units []*TeamUnit `xorm:"-"` @@ -155,17 +153,6 @@ func (t *Team) IsMember(ctx context.Context, userID int64) bool { return isMember } -// LoadRepositories returns paginated repositories in team of organization. -func (t *Team) LoadRepositories(ctx context.Context) (err error) { - if t.Repos != nil { - return nil - } - t.Repos, err = GetTeamRepositories(ctx, &SearchTeamRepoOptions{ - TeamID: t.ID, - }) - return err -} - // LoadMembers returns paginated members in team of organization. func (t *Team) LoadMembers(ctx context.Context) (err error) { t.Members, err = GetTeamMembers(ctx, &SearchMembersOptions{ diff --git a/models/organization/team_list.go b/models/organization/team_list.go index 4ceb405e31..6f2a922e95 100644 --- a/models/organization/team_list.go +++ b/models/organization/team_list.go @@ -9,7 +9,6 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" - repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "xorm.io/builder" @@ -98,11 +97,11 @@ func SearchTeam(ctx context.Context, opts *SearchTeamOptions) (TeamList, int64, } // GetRepoTeams gets the list of teams that has access to the repository -func GetRepoTeams(ctx context.Context, repo *repo_model.Repository) (teams TeamList, err error) { +func GetRepoTeams(ctx context.Context, orgID, repoID int64) (teams TeamList, err error) { return teams, db.GetEngine(ctx). Join("INNER", "team_repo", "team_repo.team_id = team.id"). - Where("team.org_id = ?", repo.OwnerID). - And("team_repo.repo_id=?", repo.ID). + Where("team.org_id = ?", orgID). + And("team_repo.repo_id=?", repoID). OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END"). Find(&teams) } diff --git a/models/organization/team_repo.go b/models/organization/team_repo.go index c90dfdeda0..53edd203a8 100644 --- a/models/organization/team_repo.go +++ b/models/organization/team_repo.go @@ -8,10 +8,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" - repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" - - "xorm.io/builder" ) // TeamRepo represents an team-repository relation. @@ -32,29 +29,6 @@ func HasTeamRepo(ctx context.Context, orgID, teamID, repoID int64) bool { return has } -type SearchTeamRepoOptions struct { - db.ListOptions - TeamID int64 -} - -// GetRepositories returns paginated repositories in team of organization. -func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (repo_model.RepositoryList, error) { - sess := db.GetEngine(ctx) - if opts.TeamID > 0 { - sess = sess.In("id", - builder.Select("repo_id"). - From("team_repo"). - Where(builder.Eq{"team_id": opts.TeamID}), - ) - } - if opts.PageSize > 0 { - sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } - var repos []*repo_model.Repository - return repos, sess.OrderBy("repository.name"). - Find(&repos) -} - // AddTeamRepo adds a repo for an organization's team func AddTeamRepo(ctx context.Context, orgID, teamID, repoID int64) error { _, err := db.GetEngine(ctx).Insert(&TeamRepo{ diff --git a/models/organization/team_test.go b/models/organization/team_test.go index 8c34e7a612..deaabbfa2c 100644 --- a/models/organization/team_test.go +++ b/models/organization/team_test.go @@ -8,6 +8,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "github.com/stretchr/testify/assert" @@ -42,9 +43,12 @@ func TestTeam_GetRepositories(t *testing.T) { test := func(teamID int64) { team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) - assert.NoError(t, team.LoadRepositories(db.DefaultContext)) - assert.Len(t, team.Repos, team.NumRepos) - for _, repo := range team.Repos { + repos, err := repo_model.GetTeamRepositories(db.DefaultContext, &repo_model.SearchTeamRepoOptions{ + TeamID: team.ID, + }) + assert.NoError(t, err) + assert.Len(t, repos, team.NumRepos) + for _, repo := range repos { unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repo.ID}) } } diff --git a/models/repo/org_repo.go b/models/repo/org_repo.go new file mode 100644 index 0000000000..5f0af2d475 --- /dev/null +++ b/models/repo/org_repo.go @@ -0,0 +1,206 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "context" + "fmt" + "strings" + + "code.gitea.io/gitea/models/db" + org_model "code.gitea.io/gitea/models/organization" + user_model "code.gitea.io/gitea/models/user" + + "xorm.io/builder" +) + +// GetOrgRepositories get repos belonging to the given organization +func GetOrgRepositories(ctx context.Context, orgID int64) (RepositoryList, error) { + var orgRepos []*Repository + return orgRepos, db.GetEngine(ctx).Where("owner_id = ?", orgID).Find(&orgRepos) +} + +type SearchTeamRepoOptions struct { + db.ListOptions + TeamID int64 +} + +// GetRepositories returns paginated repositories in team of organization. +func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (RepositoryList, error) { + sess := db.GetEngine(ctx) + if opts.TeamID > 0 { + sess = sess.In("id", + builder.Select("repo_id"). + From("team_repo"). + Where(builder.Eq{"team_id": opts.TeamID}), + ) + } + if opts.PageSize > 0 { + sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) + } + var repos []*Repository + return repos, sess.OrderBy("repository.name"). + Find(&repos) +} + +// AccessibleReposEnvironment operations involving the repositories that are +// accessible to a particular user +type AccessibleReposEnvironment interface { + CountRepos() (int64, error) + RepoIDs(page, pageSize int) ([]int64, error) + Repos(page, pageSize int) (RepositoryList, error) + MirrorRepos() (RepositoryList, error) + AddKeyword(keyword string) + SetSort(db.SearchOrderBy) +} + +type accessibleReposEnv struct { + org *org_model.Organization + user *user_model.User + team *org_model.Team + teamIDs []int64 + ctx context.Context + keyword string + orderBy db.SearchOrderBy +} + +// AccessibleReposEnv builds an AccessibleReposEnvironment for the repositories in `org` +// that are accessible to the specified user. +func AccessibleReposEnv(ctx context.Context, org *org_model.Organization, userID int64) (AccessibleReposEnvironment, error) { + var user *user_model.User + + if userID > 0 { + u, err := user_model.GetUserByID(ctx, userID) + if err != nil { + return nil, err + } + user = u + } + + teamIDs, err := org.GetUserTeamIDs(ctx, userID) + if err != nil { + return nil, err + } + return &accessibleReposEnv{ + org: org, + user: user, + teamIDs: teamIDs, + ctx: ctx, + orderBy: db.SearchOrderByRecentUpdated, + }, nil +} + +// AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org` +// that are accessible to the specified team. +func AccessibleTeamReposEnv(ctx context.Context, org *org_model.Organization, team *org_model.Team) AccessibleReposEnvironment { + return &accessibleReposEnv{ + org: org, + team: team, + ctx: ctx, + orderBy: db.SearchOrderByRecentUpdated, + } +} + +func (env *accessibleReposEnv) cond() builder.Cond { + cond := builder.NewCond() + if env.team != nil { + cond = cond.And(builder.Eq{"team_repo.team_id": env.team.ID}) + } else { + if env.user == nil || !env.user.IsRestricted { + cond = cond.Or(builder.Eq{ + "`repository`.owner_id": env.org.ID, + "`repository`.is_private": false, + }) + } + if len(env.teamIDs) > 0 { + cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs)) + } + } + if env.keyword != "" { + cond = cond.And(builder.Like{"`repository`.lower_name", strings.ToLower(env.keyword)}) + } + return cond +} + +func (env *accessibleReposEnv) CountRepos() (int64, error) { + repoCount, err := db.GetEngine(env.ctx). + Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id"). + Where(env.cond()). + Distinct("`repository`.id"). + Count(&Repository{}) + if err != nil { + return 0, fmt.Errorf("count user repositories in organization: %w", err) + } + return repoCount, nil +} + +func (env *accessibleReposEnv) RepoIDs(page, pageSize int) ([]int64, error) { + if page <= 0 { + page = 1 + } + + repoIDs := make([]int64, 0, pageSize) + return repoIDs, db.GetEngine(env.ctx). + Table("repository"). + Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id"). + Where(env.cond()). + GroupBy("`repository`.id,`repository`."+strings.Fields(string(env.orderBy))[0]). + OrderBy(string(env.orderBy)). + Limit(pageSize, (page-1)*pageSize). + Cols("`repository`.id"). + Find(&repoIDs) +} + +func (env *accessibleReposEnv) Repos(page, pageSize int) (RepositoryList, error) { + repoIDs, err := env.RepoIDs(page, pageSize) + if err != nil { + return nil, fmt.Errorf("GetUserRepositoryIDs: %w", err) + } + + repos := make([]*Repository, 0, len(repoIDs)) + if len(repoIDs) == 0 { + return repos, nil + } + + return repos, db.GetEngine(env.ctx). + In("`repository`.id", repoIDs). + OrderBy(string(env.orderBy)). + Find(&repos) +} + +func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) { + repoIDs := make([]int64, 0, 10) + return repoIDs, db.GetEngine(env.ctx). + Table("repository"). + Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true). + Where(env.cond()). + GroupBy("`repository`.id, `repository`.updated_unix"). + OrderBy(string(env.orderBy)). + Cols("`repository`.id"). + Find(&repoIDs) +} + +func (env *accessibleReposEnv) MirrorRepos() (RepositoryList, error) { + repoIDs, err := env.MirrorRepoIDs() + if err != nil { + return nil, fmt.Errorf("MirrorRepoIDs: %w", err) + } + + repos := make([]*Repository, 0, len(repoIDs)) + if len(repoIDs) == 0 { + return repos, nil + } + + return repos, db.GetEngine(env.ctx). + In("`repository`.id", repoIDs). + Find(&repos) +} + +func (env *accessibleReposEnv) AddKeyword(keyword string) { + env.keyword = keyword +} + +func (env *accessibleReposEnv) SetSort(orderBy db.SearchOrderBy) { + env.orderBy = orderBy +} diff --git a/models/repo_transfer.go b/models/repo/transfer.go similarity index 73% rename from models/repo_transfer.go rename to models/repo/transfer.go index 37f591f65d..43e15b33bc 100644 --- a/models/repo_transfer.go +++ b/models/repo/transfer.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package models +package repo import ( "context" @@ -10,16 +10,58 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" - repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" "xorm.io/builder" ) +// ErrNoPendingRepoTransfer is an error type for repositories without a pending +// transfer request +type ErrNoPendingRepoTransfer struct { + RepoID int64 +} + +func (err ErrNoPendingRepoTransfer) Error() string { + return fmt.Sprintf("repository doesn't have a pending transfer [repo_id: %d]", err.RepoID) +} + +// IsErrNoPendingTransfer is an error type when a repository has no pending +// transfers +func IsErrNoPendingTransfer(err error) bool { + _, ok := err.(ErrNoPendingRepoTransfer) + return ok +} + +func (err ErrNoPendingRepoTransfer) Unwrap() error { + return util.ErrNotExist +} + +// ErrRepoTransferInProgress represents the state of a repository that has an +// ongoing transfer +type ErrRepoTransferInProgress struct { + Uname string + Name string +} + +// IsErrRepoTransferInProgress checks if an error is a ErrRepoTransferInProgress. +func IsErrRepoTransferInProgress(err error) bool { + _, ok := err.(ErrRepoTransferInProgress) + return ok +} + +func (err ErrRepoTransferInProgress) Error() string { + return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name) +} + +func (err ErrRepoTransferInProgress) Unwrap() error { + return util.ErrAlreadyExist +} + // RepoTransfer is used to manage repository transfers -type RepoTransfer struct { +type RepoTransfer struct { //nolint ID int64 `xorm:"pk autoincr"` DoerID int64 Doer *user_model.User `xorm:"-"` @@ -126,7 +168,7 @@ func GetPendingRepositoryTransfers(ctx context.Context, opts *PendingRepositoryT // GetPendingRepositoryTransfer fetches the most recent and ongoing transfer // process for the repository -func GetPendingRepositoryTransfer(ctx context.Context, repo *repo_model.Repository) (*RepoTransfer, error) { +func GetPendingRepositoryTransfer(ctx context.Context, repo *Repository) (*RepoTransfer, error) { transfers, err := GetPendingRepositoryTransfers(ctx, &PendingRepositoryTransferOptions{RepoID: repo.ID}) if err != nil { return nil, err @@ -145,11 +187,11 @@ func DeleteRepositoryTransfer(ctx context.Context, repoID int64) error { } // TestRepositoryReadyForTransfer make sure repo is ready to transfer -func TestRepositoryReadyForTransfer(status repo_model.RepositoryStatus) error { +func TestRepositoryReadyForTransfer(status RepositoryStatus) error { switch status { - case repo_model.RepositoryBeingMigrated: + case RepositoryBeingMigrated: return errors.New("repo is not ready, currently migrating") - case repo_model.RepositoryPendingTransfer: + case RepositoryPendingTransfer: return ErrRepoTransferInProgress{} } return nil @@ -159,7 +201,7 @@ func TestRepositoryReadyForTransfer(status repo_model.RepositoryStatus) error { // it marks the repository transfer as "pending" func CreatePendingRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.User, repoID int64, teams []*organization.Team) error { return db.WithTx(ctx, func(ctx context.Context) error { - repo, err := repo_model.GetRepositoryByID(ctx, repoID) + repo, err := GetRepositoryByID(ctx, repoID) if err != nil { return err } @@ -169,16 +211,16 @@ func CreatePendingRepositoryTransfer(ctx context.Context, doer, newOwner *user_m return err } - repo.Status = repo_model.RepositoryPendingTransfer - if err := repo_model.UpdateRepositoryCols(ctx, repo, "status"); err != nil { + repo.Status = RepositoryPendingTransfer + if err := UpdateRepositoryCols(ctx, repo, "status"); err != nil { return err } // Check if new owner has repository with same name. - if has, err := repo_model.IsRepositoryModelExist(ctx, newOwner, repo.Name); err != nil { + if has, err := IsRepositoryModelExist(ctx, newOwner, repo.Name); err != nil { return fmt.Errorf("IsRepositoryExist: %w", err) } else if has { - return repo_model.ErrRepoAlreadyExist{ + return ErrRepoAlreadyExist{ Uname: newOwner.LowerName, Name: repo.Name, } diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index bc50960b61..8164d2cfe9 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -573,19 +573,19 @@ func GetTeamRepos(ctx *context.APIContext) { // "$ref": "#/responses/notFound" team := ctx.Org.Team - teamRepos, err := organization.GetTeamRepositories(ctx, &organization.SearchTeamRepoOptions{ + teamRepos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{ ListOptions: utils.GetListOptions(ctx), TeamID: team.ID, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetTeamRepos", err) + ctx.Error(http.StatusInternalServerError, "GetTeamRepositories", err) return } repos := make([]*api.Repository, len(teamRepos)) for i, repo := range teamRepos { permission, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetTeamRepos", err) + ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err) return } repos[i] = convert.ToRepo(ctx, repo, permission) diff --git a/routers/api/v1/repo/teams.go b/routers/api/v1/repo/teams.go index 82ecaf3020..42fb0a1d75 100644 --- a/routers/api/v1/repo/teams.go +++ b/routers/api/v1/repo/teams.go @@ -42,7 +42,7 @@ func ListTeams(ctx *context.APIContext) { return } - teams, err := organization.GetRepoTeams(ctx, ctx.Repo.Repository) + teams, err := organization.GetRepoTeams(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index 776b336761..787ec34404 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -8,7 +8,6 @@ import ( "fmt" "net/http" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" @@ -108,7 +107,7 @@ func Transfer(ctx *context.APIContext) { oldFullname := ctx.Repo.Repository.FullName() if err := repo_service.StartRepositoryTransfer(ctx, ctx.Doer, newOwner, ctx.Repo.Repository, teams); err != nil { - if models.IsErrRepoTransferInProgress(err) { + if repo_model.IsErrRepoTransferInProgress(err) { ctx.Error(http.StatusConflict, "StartRepositoryTransfer", err) return } @@ -213,9 +212,9 @@ func RejectTransfer(ctx *context.APIContext) { } func acceptOrRejectRepoTransfer(ctx *context.APIContext, accept bool) error { - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) + repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) if err != nil { - if models.IsErrNoPendingTransfer(err) { + if repo_model.IsErrNoPendingTransfer(err) { ctx.NotFound() return nil } diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go index bd78832103..b03b18bd9c 100644 --- a/routers/web/org/teams.go +++ b/routers/web/org/teams.go @@ -410,11 +410,15 @@ func TeamRepositories(ctx *context.Context) { return } - if err := ctx.Org.Team.LoadRepositories(ctx); err != nil { - ctx.ServerError("GetRepositories", err) + repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{ + TeamID: ctx.Org.Team.ID, + }) + if err != nil { + ctx.ServerError("GetTeamRepositories", err) return } ctx.Data["Units"] = unit_model.Units + ctx.Data["TeamRepos"] = repos ctx.HTML(http.StatusOK, tplTeamRepositories) } diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index f5e59b0357..4b017f61c4 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -11,7 +11,6 @@ import ( "slices" "strings" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" "code.gitea.io/gitea/models/organization" @@ -378,7 +377,7 @@ func Action(ctx *context.Context) { } func acceptOrRejectRepoTransfer(ctx *context.Context, accept bool) error { - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) + repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) if err != nil { return err } diff --git a/routers/web/repo/setting/collaboration.go b/routers/web/repo/setting/collaboration.go index cdf91edf4a..df7cc5e39b 100644 --- a/routers/web/repo/setting/collaboration.go +++ b/routers/web/repo/setting/collaboration.go @@ -32,7 +32,7 @@ func Collaboration(ctx *context.Context) { } ctx.Data["Collaborators"] = users - teams, err := organization.GetRepoTeams(ctx, ctx.Repo.Repository) + teams, err := organization.GetRepoTeams(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID) if err != nil { ctx.ServerError("GetRepoTeams", err) return diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 717d7cbce1..f2169d8a79 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -788,7 +788,7 @@ func SettingsPost(ctx *context.Context) { if err := repo_service.StartRepositoryTransfer(ctx, ctx.Doer, newOwner, repo, nil); err != nil { if repo_model.IsErrRepoAlreadyExist(err) { ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil) - } else if models.IsErrRepoTransferInProgress(err) { + } else if repo_model.IsErrRepoTransferInProgress(err) { ctx.RenderWithErr(ctx.Tr("repo.settings.transfer_in_progress"), tplSettingsOptions, nil) } else if errors.Is(err, user_model.ErrBlockedUser) { ctx.RenderWithErr(ctx.Tr("repo.settings.transfer.blocked_user"), tplSettingsOptions, nil) @@ -814,9 +814,9 @@ func SettingsPost(ctx *context.Context) { return } - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) + repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) if err != nil { - if models.IsErrNoPendingTransfer(err) { + if repo_model.IsErrNoPendingTransfer(err) { ctx.Flash.Error("repo.settings.transfer_abort_invalid") ctx.Redirect(repo.Link() + "/settings") } else { diff --git a/services/context/repo.go b/services/context/repo.go index 9b54439110..e96916ca42 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -14,7 +14,6 @@ import ( "path" "strings" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" @@ -709,7 +708,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc { ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer { - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) + repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) if err != nil { ctx.ServerError("GetPendingRepositoryTransfer", err) return cancel diff --git a/services/convert/repository.go b/services/convert/repository.go index e026d0f440..88ccd88fcf 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -7,7 +7,6 @@ import ( "context" "time" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" @@ -158,8 +157,8 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR var transfer *api.RepoTransfer if repo.Status == repo_model.RepositoryPendingTransfer { - t, err := models.GetPendingRepositoryTransfer(ctx, repo) - if err != nil && !models.IsErrNoPendingTransfer(err) { + t, err := repo_model.GetPendingRepositoryTransfer(ctx, repo) + if err != nil && !repo_model.IsErrNoPendingTransfer(err) { log.Warn("GetPendingRepositoryTransfer: %v", err) } else { if err := t.LoadAttributes(ctx); err != nil { @@ -248,7 +247,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR } // ToRepoTransfer convert a models.RepoTransfer to a structs.RepeTransfer -func ToRepoTransfer(ctx context.Context, t *models.RepoTransfer) *api.RepoTransfer { +func ToRepoTransfer(ctx context.Context, t *repo_model.RepoTransfer) *api.RepoTransfer { teams, _ := ToTeams(ctx, t.Teams, false) return &api.RepoTransfer{ diff --git a/services/org/org.go b/services/org/org.go index c19572a123..471e6fcaf1 100644 --- a/services/org/org.go +++ b/services/org/org.go @@ -8,16 +8,45 @@ import ( "fmt" "code.gitea.io/gitea/models" + actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" org_model "code.gitea.io/gitea/models/organization" packages_model "code.gitea.io/gitea/models/packages" repo_model "code.gitea.io/gitea/models/repo" + secret_model "code.gitea.io/gitea/models/secret" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/util" repo_service "code.gitea.io/gitea/services/repository" ) +// deleteOrganization deletes models associated to an organization. +func deleteOrganization(ctx context.Context, org *org_model.Organization) error { + if org.Type != user_model.UserTypeOrganization { + return fmt.Errorf("%s is a user not an organization", org.Name) + } + + if err := db.DeleteBeans(ctx, + &org_model.Team{OrgID: org.ID}, + &org_model.OrgUser{OrgID: org.ID}, + &org_model.TeamUser{OrgID: org.ID}, + &org_model.TeamUnit{OrgID: org.ID}, + &org_model.TeamInvite{OrgID: org.ID}, + &secret_model.Secret{OwnerID: org.ID}, + &user_model.Blocking{BlockerID: org.ID}, + &actions_model.ActionRunner{OwnerID: org.ID}, + &actions_model.ActionRunnerToken{OwnerID: org.ID}, + ); err != nil { + return fmt.Errorf("DeleteBeans: %w", err) + } + + if _, err := db.GetEngine(ctx).ID(org.ID).Delete(new(user_model.User)); err != nil { + return fmt.Errorf("Delete: %w", err) + } + + return nil +} + // DeleteOrganization completely and permanently deletes everything of organization. func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge bool) error { ctx, committer, err := db.TxContext(ctx) @@ -48,7 +77,7 @@ func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge return models.ErrUserOwnPackages{UID: org.ID} } - if err := org_model.DeleteOrganization(ctx, org); err != nil { + if err := deleteOrganization(ctx, org); err != nil { return fmt.Errorf("DeleteOrganization: %w", err) } diff --git a/services/org/team.go b/services/org/team.go index 3688e68433..ee3bd898ea 100644 --- a/services/org/team.go +++ b/services/org/team.go @@ -141,11 +141,14 @@ func UpdateTeam(ctx context.Context, t *organization.Team, authChanged, includeA // Update access for team members if needed. if authChanged { - if err = t.LoadRepositories(ctx); err != nil { - return fmt.Errorf("LoadRepositories: %w", err) + repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{ + TeamID: t.ID, + }) + if err != nil { + return fmt.Errorf("GetTeamRepositories: %w", err) } - for _, repo := range t.Repos { + for _, repo := range repos { if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil { return fmt.Errorf("recalculateTeamAccesses: %w", err) } @@ -172,10 +175,6 @@ func DeleteTeam(ctx context.Context, t *organization.Team) error { } defer committer.Close() - if err := t.LoadRepositories(ctx); err != nil { - return err - } - if err := t.LoadMembers(ctx); err != nil { return err } @@ -301,8 +300,11 @@ func AddTeamMember(ctx context.Context, team *organization.Team, user *user_mode // FIXME: Update watch repos batchly if setting.Service.AutoWatchNewRepos { // Get team and its repositories. - if err := team.LoadRepositories(ctx); err != nil { - log.Error("team.LoadRepositories failed: %v", err) + repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{ + TeamID: team.ID, + }) + if err != nil { + log.Error("GetTeamRepositories failed: %v", err) } // FIXME: in the goroutine, it can't access the "ctx", it could only use db.DefaultContext at the moment @@ -312,7 +314,7 @@ func AddTeamMember(ctx context.Context, team *organization.Team, user *user_mode log.Error("watch repo failed: %v", err) } } - }(team.Repos) + }(repos) } return nil @@ -332,7 +334,10 @@ func removeTeamMember(ctx context.Context, team *organization.Team, user *user_m team.NumMembers-- - if err := team.LoadRepositories(ctx); err != nil { + repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{ + TeamID: team.ID, + }) + if err != nil { return err } @@ -350,7 +355,7 @@ func removeTeamMember(ctx context.Context, team *organization.Team, user *user_m } // Delete access to team repositories. - for _, repo := range team.Repos { + for _, repo := range repos { if err := access_model.RecalculateUserAccess(ctx, repo, user.ID); err != nil { return err } diff --git a/services/org/team_test.go b/services/org/team_test.go index 98addac8f8..3791776e46 100644 --- a/services/org/team_test.go +++ b/services/org/team_test.go @@ -189,9 +189,12 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { testTeamRepositories := func(teamID int64, repoIDs []int64) { team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) - assert.NoError(t, team.LoadRepositories(db.DefaultContext), "%s: GetRepositories", team.Name) - assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name) - assert.Len(t, team.Repos, len(repoIDs), "%s: repo count", team.Name) + repos, err := repo_model.GetTeamRepositories(db.DefaultContext, &repo_model.SearchTeamRepoOptions{ + TeamID: team.ID, + }) + assert.NoError(t, err, "%s: GetTeamRepositories", team.Name) + assert.Len(t, repos, team.NumRepos, "%s: len repo", team.Name) + assert.Len(t, repos, len(repoIDs), "%s: repo count", team.Name) for i, rid := range repoIDs { if rid > 0 { assert.True(t, repo_service.HasRepository(db.DefaultContext, team, rid), "%s: HasRepository(%d) %d", rid, i) @@ -310,5 +313,5 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { assert.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, rid), "DeleteRepository %d", i) } } - assert.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization") + assert.NoError(t, DeleteOrganization(db.DefaultContext, org, false), "DeleteOrganization") } diff --git a/services/org/user.go b/services/org/user.go index 0627860fe7..0e74d006bb 100644 --- a/services/org/user.go +++ b/services/org/user.go @@ -60,7 +60,7 @@ func RemoveOrgUser(ctx context.Context, org *organization.Organization, user *us } // Delete all repository accesses and unwatch them. - env, err := organization.AccessibleReposEnv(ctx, org, user.ID) + env, err := repo_model.AccessibleReposEnv(ctx, org, user.ID) if err != nil { return fmt.Errorf("AccessibleReposEnv: %w", err) } diff --git a/services/repository/repo_team.go b/services/repository/repo_team.go index 29c67893b2..672ee49fea 100644 --- a/services/repository/repo_team.go +++ b/services/repository/repo_team.go @@ -63,7 +63,7 @@ func addRepositoryToTeam(ctx context.Context, t *organization.Team, repo *repo_m // If the team already has some repositories they will be left unchanged. func AddAllRepositoriesToTeam(ctx context.Context, t *organization.Team) error { return db.WithTx(ctx, func(ctx context.Context) error { - orgRepos, err := organization.GetOrgRepositories(ctx, t.OrgID) + orgRepos, err := repo_model.GetOrgRepositories(ctx, t.OrgID) if err != nil { return fmt.Errorf("get org repos: %w", err) } @@ -103,8 +103,15 @@ func RemoveAllRepositoriesFromTeam(ctx context.Context, t *organization.Team) (e // Note: Shall not be called if team includes all repositories func removeAllRepositoriesFromTeam(ctx context.Context, t *organization.Team) (err error) { e := db.GetEngine(ctx) + repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{ + TeamID: t.ID, + }) + if err != nil { + return fmt.Errorf("GetTeamRepositories: %w", err) + } + // Delete all accesses. - for _, repo := range t.Repos { + for _, repo := range repos { if err := access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil { return err } diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 9a643469d9..9ef28ddeb9 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -9,7 +9,6 @@ import ( "os" "strings" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" @@ -285,7 +284,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName wikiRenamed = true } - if err := models.DeleteRepositoryTransfer(ctx, repo.ID); err != nil { + if err := repo_model.DeleteRepositoryTransfer(ctx, repo.ID); err != nil { return fmt.Errorf("deleteRepositoryTransfer: %w", err) } repo.Status = repo_model.RepositoryReady @@ -388,7 +387,7 @@ func ChangeRepositoryName(ctx context.Context, doer *user_model.User, repo *repo // StartRepositoryTransfer transfer a repo from one owner to a new one. // it make repository into pending transfer state, if doer can not create repo for new owner. func StartRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error { - if err := models.TestRepositoryReadyForTransfer(repo.Status); err != nil { + if err := repo_model.TestRepositoryReadyForTransfer(repo.Status); err != nil { return err } @@ -425,7 +424,7 @@ func StartRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.Use // Make repo as pending for transfer repo.Status = repo_model.RepositoryPendingTransfer - if err := models.CreatePendingRepositoryTransfer(ctx, doer, newOwner, repo.ID, teams); err != nil { + if err := repo_model.CreatePendingRepositoryTransfer(ctx, doer, newOwner, repo.ID, teams); err != nil { return err } @@ -449,7 +448,7 @@ func CancelRepositoryTransfer(ctx context.Context, repo *repo_model.Repository) return err } - if err := models.DeleteRepositoryTransfer(ctx, repo.ID); err != nil { + if err := repo_model.DeleteRepositoryTransfer(ctx, repo.ID); err != nil { return err } diff --git a/services/repository/transfer_test.go b/services/repository/transfer_test.go index 0401701ba5..91722fb8ae 100644 --- a/services/repository/transfer_test.go +++ b/services/repository/transfer_test.go @@ -7,7 +7,6 @@ import ( "sync" "testing" - "code.gitea.io/gitea/models" activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" @@ -86,23 +85,23 @@ func TestRepositoryTransfer(t *testing.T) { doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) - transfer, err := models.GetPendingRepositoryTransfer(db.DefaultContext, repo) + transfer, err := repo_model.GetPendingRepositoryTransfer(db.DefaultContext, repo) assert.NoError(t, err) assert.NotNil(t, transfer) // Cancel transfer assert.NoError(t, CancelRepositoryTransfer(db.DefaultContext, repo)) - transfer, err = models.GetPendingRepositoryTransfer(db.DefaultContext, repo) + transfer, err = repo_model.GetPendingRepositoryTransfer(db.DefaultContext, repo) assert.Error(t, err) assert.Nil(t, transfer) - assert.True(t, models.IsErrNoPendingTransfer(err)) + assert.True(t, repo_model.IsErrNoPendingTransfer(err)) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - assert.NoError(t, models.CreatePendingRepositoryTransfer(db.DefaultContext, doer, user2, repo.ID, nil)) + assert.NoError(t, repo_model.CreatePendingRepositoryTransfer(db.DefaultContext, doer, user2, repo.ID, nil)) - transfer, err = models.GetPendingRepositoryTransfer(db.DefaultContext, repo) + transfer, err = repo_model.GetPendingRepositoryTransfer(db.DefaultContext, repo) assert.NoError(t, err) assert.NoError(t, transfer.LoadAttributes(db.DefaultContext)) assert.Equal(t, "user2", transfer.Recipient.Name) @@ -110,12 +109,12 @@ func TestRepositoryTransfer(t *testing.T) { org6 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // Only transfer can be started at any given time - err = models.CreatePendingRepositoryTransfer(db.DefaultContext, doer, org6, repo.ID, nil) + err = repo_model.CreatePendingRepositoryTransfer(db.DefaultContext, doer, org6, repo.ID, nil) assert.Error(t, err) - assert.True(t, models.IsErrRepoTransferInProgress(err)) + assert.True(t, repo_model.IsErrRepoTransferInProgress(err)) // Unknown user - err = models.CreatePendingRepositoryTransfer(db.DefaultContext, doer, &user_model.User{ID: 1000, LowerName: "user1000"}, repo.ID, nil) + err = repo_model.CreatePendingRepositoryTransfer(db.DefaultContext, doer, &user_model.User{ID: 1000, LowerName: "user1000"}, repo.ID, nil) assert.Error(t, err) // Cancel transfer diff --git a/services/user/block.go b/services/user/block.go index 0b3b618aae..c24ce5273c 100644 --- a/services/user/block.go +++ b/services/user/block.go @@ -6,7 +6,6 @@ package user import ( "context" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" org_model "code.gitea.io/gitea/models/organization" @@ -194,7 +193,7 @@ func unwatchRepos(ctx context.Context, watcher, repoOwner *user_model.User) erro } func cancelRepositoryTransfers(ctx context.Context, sender, recipient *user_model.User) error { - transfers, err := models.GetPendingRepositoryTransfers(ctx, &models.PendingRepositoryTransferOptions{ + transfers, err := repo_model.GetPendingRepositoryTransfers(ctx, &repo_model.PendingRepositoryTransferOptions{ SenderID: sender.ID, RecipientID: recipient.ID, }) diff --git a/templates/org/team/repositories.tmpl b/templates/org/team/repositories.tmpl index 502cf97992..92c3d724ba 100644 --- a/templates/org/team/repositories.tmpl +++ b/templates/org/team/repositories.tmpl @@ -27,7 +27,7 @@ {{end}}
- {{range .Team.Repos}} + {{range $.TeamRepos}}
{{template "repo/icon" .}} diff --git a/tests/integration/api_user_block_test.go b/tests/integration/api_user_block_test.go index 2cc3895a71..ae6b9eb849 100644 --- a/tests/integration/api_user_block_test.go +++ b/tests/integration/api_user_block_test.go @@ -8,7 +8,6 @@ import ( "net/http" "testing" - "code.gitea.io/gitea/models" auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" @@ -42,7 +41,7 @@ func TestBlockUser(t *testing.T) { } countRepositoryTransfers := func(t *testing.T, senderID, recipientID int64) int64 { - transfers, err := models.GetPendingRepositoryTransfers(db.DefaultContext, &models.PendingRepositoryTransferOptions{ + transfers, err := repo_model.GetPendingRepositoryTransfers(db.DefaultContext, &repo_model.PendingRepositoryTransferOptions{ SenderID: senderID, RecipientID: recipientID, }) From f9f62b4c4cb356c7a06b4ce6b0be2e672aa47725 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 17 Dec 2024 20:10:38 -0800 Subject: [PATCH 07/50] Move delete deploy keys into service layer (#32201) --- models/asymkey/error.go | 5 ++- models/repo.go | 48 ---------------------- routers/api/v1/repo/key.go | 2 +- routers/web/repo/setting/deploy_key.go | 2 +- services/asymkey/deploy_key.go | 56 ++++++++++++++++++++++++-- services/asymkey/main_test.go | 1 + services/repository/delete.go | 13 ++---- 7 files changed, 61 insertions(+), 66 deletions(-) diff --git a/models/asymkey/error.go b/models/asymkey/error.go index 03bc82302f..2e65d76612 100644 --- a/models/asymkey/error.go +++ b/models/asymkey/error.go @@ -217,6 +217,7 @@ func (err ErrGPGKeyAccessDenied) Unwrap() error { // ErrKeyAccessDenied represents a "KeyAccessDenied" kind of error. type ErrKeyAccessDenied struct { UserID int64 + RepoID int64 KeyID int64 Note string } @@ -228,8 +229,8 @@ func IsErrKeyAccessDenied(err error) bool { } func (err ErrKeyAccessDenied) Error() string { - return fmt.Sprintf("user does not have access to the key [user_id: %d, key_id: %d, note: %s]", - err.UserID, err.KeyID, err.Note) + return fmt.Sprintf("user does not have access to the key [user_id: %d, repo_id: %d, key_id: %d, note: %s]", + err.UserID, err.RepoID, err.KeyID, err.Note) } func (err ErrKeyAccessDenied) Unwrap() error { diff --git a/models/repo.go b/models/repo.go index 0dc8ee5df3..3e9c52fdd9 100644 --- a/models/repo.go +++ b/models/repo.go @@ -6,15 +6,12 @@ package models import ( "context" - "fmt" "strconv" _ "image/jpeg" // Needed for jpeg support - asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" - access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" @@ -315,48 +312,3 @@ func DoctorUserStarNum(ctx context.Context) (err error) { return err } - -// DeleteDeployKey delete deploy keys -func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error { - key, err := asymkey_model.GetDeployKeyByID(ctx, id) - if err != nil { - if asymkey_model.IsErrDeployKeyNotExist(err) { - return nil - } - return fmt.Errorf("GetDeployKeyByID: %w", err) - } - - // Check if user has access to delete this key. - if !doer.IsAdmin { - repo, err := repo_model.GetRepositoryByID(ctx, key.RepoID) - if err != nil { - return fmt.Errorf("GetRepositoryByID: %w", err) - } - has, err := access_model.IsUserRepoAdmin(ctx, repo, doer) - if err != nil { - return fmt.Errorf("GetUserRepoPermission: %w", err) - } else if !has { - return asymkey_model.ErrKeyAccessDenied{ - UserID: doer.ID, - KeyID: key.ID, - Note: "deploy", - } - } - } - - if _, err := db.DeleteByID[asymkey_model.DeployKey](ctx, key.ID); err != nil { - return fmt.Errorf("delete deploy key [%d]: %w", key.ID, err) - } - - // Check if this is the last reference to same key content. - has, err := asymkey_model.IsDeployKeyExistByKeyID(ctx, key.KeyID) - if err != nil { - return err - } else if !has { - if _, err = db.DeleteByID[asymkey_model.PublicKey](ctx, key.KeyID); err != nil { - return err - } - } - - return nil -} diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index e5115980eb..060694d085 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -279,7 +279,7 @@ func DeleteDeploykey(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - if err := asymkey_service.DeleteDeployKey(ctx, ctx.Doer, ctx.PathParamInt64(":id")); err != nil { + if err := asymkey_service.DeleteDeployKey(ctx, ctx.Repo.Repository, ctx.PathParamInt64(":id")); err != nil { if asymkey_model.IsErrKeyAccessDenied(err) { ctx.Error(http.StatusForbidden, "", "You do not have access to this key") } else { diff --git a/routers/web/repo/setting/deploy_key.go b/routers/web/repo/setting/deploy_key.go index abc3eb4af1..193562528b 100644 --- a/routers/web/repo/setting/deploy_key.go +++ b/routers/web/repo/setting/deploy_key.go @@ -99,7 +99,7 @@ func DeployKeysPost(ctx *context.Context) { // DeleteDeployKey response for deleting a deploy key func DeleteDeployKey(ctx *context.Context) { - if err := asymkey_service.DeleteDeployKey(ctx, ctx.Doer, ctx.FormInt64("id")); err != nil { + if err := asymkey_service.DeleteDeployKey(ctx, ctx.Repo.Repository, ctx.FormInt64("id")); err != nil { ctx.Flash.Error("DeleteDeployKey: " + err.Error()) } else { ctx.Flash.Success(ctx.Tr("repo.settings.deploy_key_deletion_success")) diff --git a/services/asymkey/deploy_key.go b/services/asymkey/deploy_key.go index 324688c534..9e5a6d6292 100644 --- a/services/asymkey/deploy_key.go +++ b/services/asymkey/deploy_key.go @@ -5,21 +5,69 @@ package asymkey import ( "context" + "fmt" - "code.gitea.io/gitea/models" + asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" - user_model "code.gitea.io/gitea/models/user" + repo_model "code.gitea.io/gitea/models/repo" ) +// DeleteRepoDeployKeys deletes all deploy keys of a repository. permissions check should be done outside +func DeleteRepoDeployKeys(ctx context.Context, repoID int64) (int, error) { + deployKeys, err := db.Find[asymkey_model.DeployKey](ctx, asymkey_model.ListDeployKeysOptions{RepoID: repoID}) + if err != nil { + return 0, fmt.Errorf("listDeployKeys: %w", err) + } + + for _, dKey := range deployKeys { + if err := deleteDeployKeyFromDB(ctx, dKey); err != nil { + return 0, fmt.Errorf("deleteDeployKeys: %w", err) + } + } + return len(deployKeys), nil +} + +// deleteDeployKeyFromDB delete deploy keys from database +func deleteDeployKeyFromDB(ctx context.Context, key *asymkey_model.DeployKey) error { + if _, err := db.DeleteByID[asymkey_model.DeployKey](ctx, key.ID); err != nil { + return fmt.Errorf("delete deploy key [%d]: %w", key.ID, err) + } + + // Check if this is the last reference to same key content. + has, err := asymkey_model.IsDeployKeyExistByKeyID(ctx, key.KeyID) + if err != nil { + return err + } else if !has { + if _, err = db.DeleteByID[asymkey_model.PublicKey](ctx, key.KeyID); err != nil { + return err + } + } + + return nil +} + // DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed. -func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error { +// Permissions check should be done outside. +func DeleteDeployKey(ctx context.Context, repo *repo_model.Repository, id int64) error { dbCtx, committer, err := db.TxContext(ctx) if err != nil { return err } defer committer.Close() - if err := models.DeleteDeployKey(dbCtx, doer, id); err != nil { + key, err := asymkey_model.GetDeployKeyByID(ctx, id) + if err != nil { + if asymkey_model.IsErrDeployKeyNotExist(err) { + return nil + } + return fmt.Errorf("GetDeployKeyByID: %w", err) + } + + if key.RepoID != repo.ID { + return fmt.Errorf("deploy key %d does not belong to repository %d", id, repo.ID) + } + + if err := deleteDeployKeyFromDB(dbCtx, key); err != nil { return err } if err := committer.Commit(); err != nil { diff --git a/services/asymkey/main_test.go b/services/asymkey/main_test.go index 3505b26f69..1cdc39933d 100644 --- a/services/asymkey/main_test.go +++ b/services/asymkey/main_test.go @@ -8,6 +8,7 @@ import ( "code.gitea.io/gitea/models/unittest" + _ "code.gitea.io/gitea/models" _ "code.gitea.io/gitea/models/actions" _ "code.gitea.io/gitea/models/activities" ) diff --git a/services/repository/delete.go b/services/repository/delete.go index f33bae7790..61e39fe105 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -7,11 +7,9 @@ import ( "context" "fmt" - "code.gitea.io/gitea/models" actions_model "code.gitea.io/gitea/models/actions" activities_model "code.gitea.io/gitea/models/activities" admin_model "code.gitea.io/gitea/models/admin" - asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" @@ -76,16 +74,11 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID } // Delete Deploy Keys - deployKeys, err := db.Find[asymkey_model.DeployKey](ctx, asymkey_model.ListDeployKeysOptions{RepoID: repoID}) + deleted, err := asymkey_service.DeleteRepoDeployKeys(ctx, repoID) if err != nil { - return fmt.Errorf("listDeployKeys: %w", err) - } - needRewriteKeysFile := len(deployKeys) > 0 - for _, dKey := range deployKeys { - if err := models.DeleteDeployKey(ctx, doer, dKey.ID); err != nil { - return fmt.Errorf("deleteDeployKeys: %w", err) - } + return err } + needRewriteKeysFile := deleted > 0 if cnt, err := sess.ID(repoID).Delete(&repo_model.Repository{}); err != nil { return err From 2beaedc4179ba6a3917ca99e2fd95fd2f476925e Mon Sep 17 00:00:00 2001 From: Exploding Dragon Date: Wed, 18 Dec 2024 15:25:05 +0800 Subject: [PATCH 08/50] Fix Arch package metadata introduced incorrect field (#32881) Incorrect content was introduced while generating the index, which has now been removed, and the missing fields have been added. ![](https://github.com/user-attachments/assets/4fbb8884-337e-43b1-939f-a5ba687f7ffd) --- modules/packages/arch/metadata.go | 6 ++++++ modules/packages/arch/metadata_test.go | 4 ++++ services/packages/arch/repository.go | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/packages/arch/metadata.go b/modules/packages/arch/metadata.go index e1e79c60e0..06a2206a36 100644 --- a/modules/packages/arch/metadata.go +++ b/modules/packages/arch/metadata.go @@ -69,10 +69,12 @@ type FileMetadata struct { Packager string `json:"packager,omitempty"` Groups []string `json:"groups,omitempty"` Provides []string `json:"provides,omitempty"` + Replaces []string `json:"replaces,omitempty"` Depends []string `json:"depends,omitempty"` OptDepends []string `json:"opt_depends,omitempty"` MakeDepends []string `json:"make_depends,omitempty"` CheckDepends []string `json:"check_depends,omitempty"` + Conflicts []string `json:"conflicts,omitempty"` XData []string `json:"xdata,omitempty"` Backup []string `json:"backup,omitempty"` Files []string `json:"files,omitempty"` @@ -201,12 +203,16 @@ func ParsePackageInfo(r io.Reader) (*Package, error) { p.FileMetadata.Provides = append(p.FileMetadata.Provides, value) case "depend": p.FileMetadata.Depends = append(p.FileMetadata.Depends, value) + case "replaces": + p.FileMetadata.Replaces = append(p.FileMetadata.Replaces, value) case "optdepend": p.FileMetadata.OptDepends = append(p.FileMetadata.OptDepends, value) case "makedepend": p.FileMetadata.MakeDepends = append(p.FileMetadata.MakeDepends, value) case "checkdepend": p.FileMetadata.CheckDepends = append(p.FileMetadata.CheckDepends, value) + case "conflict": + p.FileMetadata.Conflicts = append(p.FileMetadata.Conflicts, value) case "backup": p.FileMetadata.Backup = append(p.FileMetadata.Backup, value) case "group": diff --git a/modules/packages/arch/metadata_test.go b/modules/packages/arch/metadata_test.go index f611ef5e84..37c0a553b8 100644 --- a/modules/packages/arch/metadata_test.go +++ b/modules/packages/arch/metadata_test.go @@ -42,8 +42,10 @@ depend = gitea provides = common provides = gitea optdepend = hex +replaces = gogs checkdepend = common makedepend = cmake +conflict = ninja backup = usr/bin/paket1`) } @@ -149,8 +151,10 @@ func TestParsePackageInfo(t *testing.T) { assert.ElementsMatch(t, []string{"group"}, p.FileMetadata.Groups) assert.ElementsMatch(t, []string{"common", "gitea"}, p.FileMetadata.Provides) assert.ElementsMatch(t, []string{"common", "gitea"}, p.FileMetadata.Depends) + assert.ElementsMatch(t, []string{"gogs"}, p.FileMetadata.Replaces) assert.ElementsMatch(t, []string{"hex"}, p.FileMetadata.OptDepends) assert.ElementsMatch(t, []string{"common"}, p.FileMetadata.CheckDepends) + assert.ElementsMatch(t, []string{"ninja"}, p.FileMetadata.Conflicts) assert.ElementsMatch(t, []string{"cmake"}, p.FileMetadata.MakeDepends) assert.ElementsMatch(t, []string{"usr/bin/paket1"}, p.FileMetadata.Backup) }) diff --git a/services/packages/arch/repository.go b/services/packages/arch/repository.go index ab1b85ae95..6731d9a1ac 100644 --- a/services/packages/arch/repository.go +++ b/services/packages/arch/repository.go @@ -371,11 +371,12 @@ func writeDescription(tw *tar.Writer, opts *entryOptions) error { {"BUILDDATE", fmt.Sprintf("%d", opts.FileMetadata.BuildDate)}, {"PACKAGER", opts.FileMetadata.Packager}, {"PROVIDES", strings.Join(opts.FileMetadata.Provides, "\n")}, + {"REPLACES", strings.Join(opts.FileMetadata.Replaces, "\n")}, + {"CONFLICTS", strings.Join(opts.FileMetadata.Conflicts, "\n")}, {"DEPENDS", strings.Join(opts.FileMetadata.Depends, "\n")}, {"OPTDEPENDS", strings.Join(opts.FileMetadata.OptDepends, "\n")}, {"MAKEDEPENDS", strings.Join(opts.FileMetadata.MakeDepends, "\n")}, {"CHECKDEPENDS", strings.Join(opts.FileMetadata.CheckDepends, "\n")}, - {"XDATA", strings.Join(opts.FileMetadata.XData, "\n")}, }) } From 857abed3a92eb6b02a9fd14d2eff20b7a94ed8b4 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 18 Dec 2024 21:26:17 +0100 Subject: [PATCH 09/50] Move RepoBranchTagSelector init outside the SFC (#32890) SFCs shouldn't export anything besides their component, and this eliminates one issue with tsc, while apparently also solving a hack. It seems to work as before, also when multiples are on the same page. --- .../js/components/RepoBranchTagSelector.vue | 75 ++++++++----------- web_src/js/features/repo-legacy.ts | 9 ++- web_src/js/globals.d.ts | 1 - 3 files changed, 38 insertions(+), 47 deletions(-) diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue index 4b7ca1429d..a5ed8b6dad 100644 --- a/web_src/js/components/RepoBranchTagSelector.vue +++ b/web_src/js/components/RepoBranchTagSelector.vue @@ -1,5 +1,5 @@