diff --git a/src/recipe/os/pacman/Omarchy.c b/src/recipe/os/pacman/Omarchy.c index 8e6f5d8c1..ded8fbf66 100644 --- a/src/recipe/os/pacman/Omarchy.c +++ b/src/recipe/os/pacman/Omarchy.c @@ -2,16 +2,29 @@ * SPDX-License-Identifier: GPL-3.0-or-later * ------------------------------------------------------------*/ +#include "rawstr4c.h" + #define OS_Pacman_Omarchy_Conf "/etc/pacman.conf" +#define OS_Omarchy_Upstream "https://pkgs.omarchy.org" +#define OS_Omarchy_CN "https://pkgs.omarchy.cn" /** ------------------------------------------------------------ * omarchy dish (换 OPR 专属仓库) * - * Omarchy 的 Arch Linux 主仓库源与 arch 完全一致,请额外使用 - * `chsrc set arch` 来更换;本 dish 只负责 [omarchy] 段 (OPR)。 + * Omarchy 的 Arch 主仓库由 Omarchy 按通道管理:stable/rc 使用快照镜像 + * (stable-mirror/rc-mirror.omarchy.org),edge 使用滚动镜像(mirror.omarchy.org)。 + * 本 dish 只负责 [omarchy] 段 (OPR);OPR 的官方中国镜像由阿里云 CDN 提供。 * ------------------------------------------------------------*/ +/* 测速链接固定用 stable 的 omarchy.db:同通道 cn/org 内容一致,三通道体积相当(约 55-61 KB),不代表当前通道 */ +static MirrorSite_t OmarchyCN = +{ + IS_DedicatedMirrorSite, + "omarchycn", "Omarchy Ali", "Omarchy 阿里云镜像源", "https://pkgs.omarchy.cn/", + {NotSkip, NA, NA, "https://pkgs.omarchy.cn/stable/x86_64/omarchy.db", ACCURATE} +}; + def_dish(os_omarchy, "omarchy"); void @@ -20,7 +33,7 @@ os_omarchy_prepare () chef_prep_this_dish (os_omarchy, gsr); chef_set_recipe_created_on (this, "2026-09-04"); - chef_set_recipe_last_updated (this, "2026-09-06"); + chef_set_recipe_last_updated (this, "2026-11-06"); chef_set_chefs (this, 1, "@yayoinoyume"); chef_set_sauciers (this, 0); @@ -30,15 +43,14 @@ os_omarchy_prepare () chef_allow_user_define(this); chef_set_note (this, - "Omarchy 是基于 Arch Linux 开发的发行版,除官方专属软件包仓库 (OPR) 外,其余换源与 Arch 保持一致。" - "本菜品仅更换 OPR 源;Arch Linux 主仓库请另行 chsrc set arch,恢复官方 OPR 源请 chsrc reset omarchy。" - "注意: omarchy-refresh-pacman 更新脚本会覆盖此配置,换源后若被还原,请重新执行", - "Omarchy is an Arch Linux-based distro; apart from its official package repository (OPR), source switching is the same as Arch. " - "This dish only switches the OPR source; use chsrc set arch for the Arch Linux main source, and chsrc reset omarchy to restore the official OPR source. " - "Note: omarchy-refresh-pacman may overwrite this configuration; rerun if it reverts"); + "仅更换 OPR 源;Arch 主仓库请勿使用 chsrc set arch。" + "Omarchy 4.5+ 中国区 Arch 镜像:将 /etc/omarchy/region 写为 cn,再运行 omarchy-refresh-pacman;它会覆盖本菜品设置,之后重跑 chsrc set omarchy omarchycn 即可。", + "Only switches the OPR source; do not use chsrc set arch for the Arch main repositories. " + "For China on Omarchy 4.5+, write cn to /etc/omarchy/region and run omarchy-refresh-pacman; it overwrites this dish's setting, so rerun chsrc set omarchy omarchycn afterward."); def_sources_begin() - {&UpstreamProvider, "https://pkgs.omarchy.org/stable/$arch", DelegateToUpstream} + {&UpstreamProvider, OS_Omarchy_Upstream, DelegateToUpstream}, + {&OmarchyCN, OS_Omarchy_CN, DelegateToMirror} def_sources_end() } @@ -52,7 +64,8 @@ os_omarchy_getsrc (char *option) /** * @consult - * 1. https://github.com/basecamp/omarchy 官方 pacman.conf 模板 (仅 x86_64) + * 1. https://github.com/omacom/omarchy 官方 pacman.conf 模板 (仅 x86_64) + * 2. https://pkgs.omarchy.cn/ OPR 官方中国镜像 (stable/rc/edge) */ void os_omarchy_setsrc (char *option) @@ -69,24 +82,66 @@ os_omarchy_setsrc (char *option) chsrc_use_this_source (os_omarchy); + /* 一次解析 [omarchy] 段: 缺失与通道均从同一份配置读出,缺省 stable */ + char *channel_cmd = xy_str_gsub (RAWSTR_os_pacman_get_channel_cmd, "@f@", OS_Pacman_Omarchy_Conf); + char *channel = xy_run (channel_cmd, 1); + + if (!channel || xy_streql (channel, "missing")) + { + chsrc_error ("未在 " OS_Pacman_Omarchy_Conf " 中找到 [omarchy] 段"); + exit (Exit_Unsupported); + } + + if (xy_streql (channel, "edge")) + channel = "edge"; + else if (xy_streql (channel, "rc")) + channel = "rc"; + else + channel = "stable"; + chsrc_backup (OS_Pacman_Omarchy_Conf); - /* 上游默认源基于 stable 通道,edge 通道用户换源后将切换到 stable */ - bool is_edge = xy_run_get_status ("grep -q '^Server = https://pkgs.omarchy.org/edge/' " OS_Pacman_Omarchy_Conf) == 0; - if (is_edge && !source_is_userdefine (&source)) - chsrc_warn2 ("检测到当前使用 edge 通道,上游默认源基于 stable 通道,继续执行将切换到 stable"); + /* 内置源跟随当前通道;用户自定义源原样使用,不做通道替换 */ + bool is_cn = xy_streql (source.mirror->code, OmarchyCN.code); + bool is_user = source_is_userdefine (&source); - /* 幂等: 替换 [omarchy] 段的 Server 行为所选源;已是所选源则跳过 */ - char *exists_cmd = xy_strcat (3, "grep -qxF 'Server = ", source.url, "' " OS_Pacman_Omarchy_Conf); - if (xy_run_get_status (exists_cmd) == 0) + /* 用户自定义 URL 会被嵌入 shell 单引号参数,awk -v 还会转义反斜杠 */ + if (is_user && (strchr (source.url, '\'') || strchr (source.url, '\\') + || strchr (source.url, '\n') || strchr (source.url, '\r'))) { - chsrc_debug ("recipe", "所选 Server 行已存在,跳过 pacman.conf 修改"); + chsrc_error ("用户自定义 URL 不能包含单引号、反斜杠或换行"); + exit (Exit_UserCause); } + + char *target = NULL; + if (is_user) + target = source.url; else + target = xy_strcat (4, source.url, "/", channel, "/$arch"); + + /* 中国镜像后追加同通道上游兜底行,上游同步延迟时仍可安装 */ + char *fallback = is_cn ? xy_strcat (4, OS_Omarchy_Upstream, "/", channel, "/$arch") : ""; + + /* 只重写 [omarchy] 段,不触碰 [core]、[archlinuxcn] 等其他段 */ + char *tmp_conf = xy_2strcat (OS_Pacman_Omarchy_Conf, ".chsrc-tmp"); + char *write_cmd = xy_str_gsub (RAWSTR_os_pacman_rewrite_cmd, "@f@", OS_Pacman_Omarchy_Conf); + write_cmd = xy_str_gsub (write_cmd, "@tmp@", tmp_conf); + write_cmd = xy_str_gsub (write_cmd, "@2@", fallback); + write_cmd = xy_str_gsub (write_cmd, "@1@", target); + chsrc_run (write_cmd, RunOpt_Default); + + /* 写后校验: 首行必须是目标 URL;中国镜像还需要同通道兜底行 */ + if (!in_dry_run_mode()) { - char *sed_cmd = xy_strcat (4, "sed -i 's|^Server = https://pkgs.omarchy.org/.*|Server = ", - source.url, "|' ", OS_Pacman_Omarchy_Conf); - chsrc_run (sed_cmd, RunOpt_Default); + char *validate_cmd = xy_str_gsub (RAWSTR_os_pacman_validate_cmd, "@f@", OS_Pacman_Omarchy_Conf); + validate_cmd = xy_str_gsub (validate_cmd, "@2@", fallback); + validate_cmd = xy_str_gsub (validate_cmd, "@1@", target); + + if (xy_run_get_status (validate_cmd) != 0) + { + chsrc_error ("[omarchy] 段写入校验失败,请从 " OS_Pacman_Omarchy_Conf ".bak.* 恢复"); + exit (Exit_ExternalError); + } } /* 清理本地陈旧 omarchy.db 缓存: 残留旧 sig 会导致 -Syy 验签失败 */ @@ -98,14 +153,14 @@ os_omarchy_setsrc (char *option) chsrc_conclude (&source); chsrc_note2 (ENGLISH - ? "This dish only switches the OPR source; use chsrc set arch for the Arch Linux main source." - : "本菜品仅更换 OPR 源;Arch Linux 主仓库请另行 chsrc set arch。"); + ? xy_strcat (3, "Kept the current ", channel, " channel; the Arch main repositories are untouched.") + : xy_strcat (3, "已保持当前 ", channel, " 通道,Arch 主仓库未改动。")); } void os_omarchy_resetsrc (char *option) { - /* reset 模式下 chsrc_use_this_source 返回官方源,即恢复官方 stable 通道 */ + /* reset 模式下 chsrc_use_this_source 返回官方源,即恢复官方源的当前通道 */ os_omarchy_setsrc (option); } diff --git a/src/recipe/os/pacman/rawstr4c.h b/src/recipe/os/pacman/rawstr4c.h new file mode 100644 index 000000000..58b79b7c0 --- /dev/null +++ b/src/recipe/os/pacman/rawstr4c.h @@ -0,0 +1,11 @@ +#pragma once + +/** + * Generated from rawstr4c.md + */ + +char RAWSTR_os_pacman_get_channel_cmd[] = "\x61\x77\x6b\x20\x27\x0a\x20\x20\x2f\x5e\x5c\x5b\x6f\x6d\x61\x72\x63\x68\x79\x5c\x5d\x2f\x20\x7b\x20\x69\x6e\x73\x65\x63\x20\x3d\x20\x31\x3b\x20\x66\x6f\x75\x6e\x64\x20\x3d\x20\x31\x3b\x20\x6e\x65\x78\x74\x20\x7d\x0a\x20\x20\x2f\x5e\x5c\x5b\x2f\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x20\x69\x6e\x73\x65\x63\x20\x3d\x20\x30\x20\x7d\x0a\x20\x20\x69\x6e\x73\x65\x63\x20\x26\x26\x20\x2f\x5e\x5b\x5b\x3a\x73\x70\x61\x63\x65\x3a\x5d\x5d\x2a\x53\x65\x72\x76\x65\x72\x5b\x5b\x3a\x73\x70\x61\x63\x65\x3a\x5d\x5d\x2a\x3d\x2f\x20\x7b\x0a\x20\x20\x20\x20\x69\x66\x20\x28\x24\x30\x20\x7e\x20\x2f\x5c\x2f\x65\x64\x67\x65\x5c\x2f\x2f\x29\x20\x20\x20\x20\x20\x63\x68\x61\x6e\x6e\x65\x6c\x20\x3d\x20\x22\x65\x64\x67\x65\x22\x3b\x0a\x20\x20\x20\x20\x65\x6c\x73\x65\x20\x69\x66\x20\x28\x24\x30\x20\x7e\x20\x2f\x5c\x2f\x72\x63\x5c\x2f\x2f\x29\x20\x20\x63\x68\x61\x6e\x6e\x65\x6c\x20\x3d\x20\x22\x72\x63\x22\x3b\x0a\x20\x20\x20\x20\x65\x6c\x73\x65\x20\x69\x66\x20\x28\x63\x68\x61\x6e\x6e\x65\x6c\x20\x3d\x3d\x20\x22\x22\x29\x20\x20\x20\x63\x68\x61\x6e\x6e\x65\x6c\x20\x3d\x20\x22\x73\x74\x61\x62\x6c\x65\x22\x3b\x0a\x20\x20\x7d\x0a\x20\x20\x45\x4e\x44\x20\x7b\x0a\x20\x20\x20\x20\x69\x66\x20\x28\x21\x66\x6f\x75\x6e\x64\x29\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x70\x72\x69\x6e\x74\x20\x22\x6d\x69\x73\x73\x69\x6e\x67\x22\x3b\x0a\x20\x20\x20\x20\x65\x6c\x73\x65\x20\x69\x66\x20\x28\x63\x68\x61\x6e\x6e\x65\x6c\x20\x3d\x3d\x20\x22\x22\x29\x20\x70\x72\x69\x6e\x74\x20\x22\x73\x74\x61\x62\x6c\x65\x22\x3b\x0a\x20\x20\x20\x20\x65\x6c\x73\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x70\x72\x69\x6e\x74\x20\x63\x68\x61\x6e\x6e\x65\x6c\x3b\x0a\x20\x20\x7d\x0a\x27\x20\x40\x66\x40"; + +char RAWSTR_os_pacman_rewrite_cmd[] = "\x61\x77\x6b\x20\x2d\x76\x20\x74\x61\x72\x67\x65\x74\x3d\x27\x40\x31\x40\x27\x20\x2d\x76\x20\x66\x61\x6c\x6c\x62\x61\x63\x6b\x3d\x27\x40\x32\x40\x27\x20\x27\x0a\x20\x20\x2f\x5e\x5c\x5b\x6f\x6d\x61\x72\x63\x68\x79\x5c\x5d\x2f\x20\x7b\x0a\x20\x20\x20\x20\x70\x72\x69\x6e\x74\x3b\x0a\x20\x20\x20\x20\x70\x72\x69\x6e\x74\x20\x22\x53\x65\x72\x76\x65\x72\x20\x3d\x20\x22\x20\x74\x61\x72\x67\x65\x74\x3b\x0a\x20\x20\x20\x20\x69\x66\x20\x28\x66\x61\x6c\x6c\x62\x61\x63\x6b\x20\x21\x3d\x20\x22\x22\x29\x20\x70\x72\x69\x6e\x74\x20\x22\x53\x65\x72\x76\x65\x72\x20\x3d\x20\x22\x20\x66\x61\x6c\x6c\x62\x61\x63\x6b\x3b\x0a\x20\x20\x20\x20\x69\x6e\x73\x65\x63\x20\x3d\x20\x31\x3b\x0a\x20\x20\x20\x20\x6e\x65\x78\x74\x0a\x20\x20\x7d\x0a\x20\x20\x2f\x5e\x5c\x5b\x2f\x20\x7b\x20\x69\x6e\x73\x65\x63\x20\x3d\x20\x30\x20\x7d\x0a\x20\x20\x69\x6e\x73\x65\x63\x20\x26\x26\x20\x2f\x5e\x5b\x5b\x3a\x73\x70\x61\x63\x65\x3a\x5d\x5d\x2a\x53\x65\x72\x76\x65\x72\x5b\x5b\x3a\x73\x70\x61\x63\x65\x3a\x5d\x5d\x2a\x3d\x2f\x20\x7b\x20\x6e\x65\x78\x74\x20\x7d\x0a\x20\x20\x7b\x20\x70\x72\x69\x6e\x74\x20\x7d\x0a\x27\x20\x40\x66\x40\x20\x3e\x20\x40\x74\x6d\x70\x40\x20\x26\x26\x20\x6d\x76\x20\x40\x74\x6d\x70\x40\x20\x40\x66\x40"; + +char RAWSTR_os_pacman_validate_cmd[] = "\x61\x77\x6b\x20\x2d\x76\x20\x74\x61\x72\x67\x65\x74\x3d\x27\x40\x31\x40\x27\x20\x2d\x76\x20\x66\x61\x6c\x6c\x62\x61\x63\x6b\x3d\x27\x40\x32\x40\x27\x20\x27\x0a\x20\x20\x2f\x5e\x5c\x5b\x6f\x6d\x61\x72\x63\x68\x79\x5c\x5d\x2f\x20\x7b\x20\x69\x6e\x73\x65\x63\x20\x3d\x20\x31\x3b\x20\x6e\x65\x78\x74\x20\x7d\x0a\x20\x20\x2f\x5e\x5c\x5b\x2f\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7b\x20\x69\x6e\x73\x65\x63\x20\x3d\x20\x30\x20\x7d\x0a\x20\x20\x69\x6e\x73\x65\x63\x20\x26\x26\x20\x2f\x5e\x53\x65\x72\x76\x65\x72\x20\x3d\x20\x2f\x20\x7b\x0a\x20\x20\x20\x20\x69\x66\x20\x28\x21\x73\x65\x65\x6e\x29\x20\x7b\x20\x66\x69\x72\x73\x74\x20\x3d\x20\x24\x30\x3b\x20\x73\x65\x65\x6e\x20\x3d\x20\x31\x20\x7d\x0a\x20\x20\x20\x20\x69\x66\x20\x28\x66\x61\x6c\x6c\x62\x61\x63\x6b\x20\x21\x3d\x20\x22\x22\x20\x26\x26\x20\x24\x30\x20\x3d\x3d\x20\x22\x53\x65\x72\x76\x65\x72\x20\x3d\x20\x22\x20\x66\x61\x6c\x6c\x62\x61\x63\x6b\x29\x20\x68\x61\x73\x5f\x66\x61\x6c\x6c\x62\x61\x63\x6b\x20\x3d\x20\x31\x0a\x20\x20\x7d\x0a\x20\x20\x45\x4e\x44\x20\x7b\x0a\x20\x20\x20\x20\x69\x66\x20\x28\x21\x73\x65\x65\x6e\x20\x7c\x7c\x20\x66\x69\x72\x73\x74\x20\x21\x3d\x20\x22\x53\x65\x72\x76\x65\x72\x20\x3d\x20\x22\x20\x74\x61\x72\x67\x65\x74\x29\x20\x65\x78\x69\x74\x20\x31\x3b\x0a\x20\x20\x20\x20\x69\x66\x20\x28\x66\x61\x6c\x6c\x62\x61\x63\x6b\x20\x21\x3d\x20\x22\x22\x20\x26\x26\x20\x21\x68\x61\x73\x5f\x66\x61\x6c\x6c\x62\x61\x63\x6b\x29\x20\x65\x78\x69\x74\x20\x31\x3b\x0a\x20\x20\x20\x20\x65\x78\x69\x74\x20\x30\x3b\x0a\x20\x20\x7d\x0a\x27\x20\x40\x66\x40"; diff --git a/src/recipe/os/pacman/rawstr4c.md b/src/recipe/os/pacman/rawstr4c.md new file mode 100644 index 000000000..97e1f6918 --- /dev/null +++ b/src/recipe/os/pacman/rawstr4c.md @@ -0,0 +1,83 @@ + + +# rawstr4c input for pacman + +- prefix = `RAWSTR_os_pacman` +- output = `:global-variable-only-header` +- translate = `:hex` +- no-postfix = `true` + +## Omarchy + +### Get channel + +只读取 `[omarchy]` 段内未注释的 Server 行,输出 `stable`、`rc`、`edge` 或 `missing`。 + +- name = `get_channel_cmd` + +```sh +awk ' + /^\[omarchy\]/ { insec = 1; found = 1; next } + /^\[/ { insec = 0 } + insec && /^[[:space:]]*Server[[:space:]]*=/ { + if ($0 ~ /\/edge\//) channel = "edge"; + else if ($0 ~ /\/rc\//) channel = "rc"; + else if (channel == "") channel = "stable"; + } + END { + if (!found) print "missing"; + else if (channel == "") print "stable"; + else print channel; + } +' @f@ +``` + +### Rewrite Omarchy section + +只重写 `[omarchy]` 段:删除段内全部 Server 行,再写入目标行和可选的中国镜像兜底行。 + +- name = `rewrite_cmd` + +```sh +awk -v target='@1@' -v fallback='@2@' ' + /^\[omarchy\]/ { + print; + print "Server = " target; + if (fallback != "") print "Server = " fallback; + insec = 1; + next + } + /^\[/ { insec = 0 } + insec && /^[[:space:]]*Server[[:space:]]*=/ { next } + { print } +' @f@ > @tmp@ && mv @tmp@ @f@ +``` + +### Validate Omarchy section + +确认 `[omarchy]` 段首行是目标行;中国镜像还要求存在同通道兜底行。 + +- name = `validate_cmd` + +```sh +awk -v target='@1@' -v fallback='@2@' ' + /^\[omarchy\]/ { insec = 1; next } + /^\[/ { insec = 0 } + insec && /^Server = / { + if (!seen) { first = $0; seen = 1 } + if (fallback != "" && $0 == "Server = " fallback) has_fallback = 1 + } + END { + if (!seen || first != "Server = " target) exit 1; + if (fallback != "" && !has_fallback) exit 1; + exit 0; + } +' @f@ +```