Alpine Linux 软件包仓库一致性检查
2025 年 6 月 6 日
问题描述
镜像中共有两种文件,APKINDEX
索引文件和具体的 pkgname-pkgver-rpkgrel.apk
安装包。当用户执行安装软件操作时,会进行以下过程:
- 下载最新的 APKINDEX 到本地
- 解析索引,拿到对应的具体包名
- 下载对应的软件包文件
- 执行安装、脚本等
如果索引文件的版本与具体软件包不匹配,就会造成安装时找不到软件包,报错信息等同于软件名输入错误,示例如下:
$ doas apk add cage
ERROR: unable to select packages:
cage (no such package):
required by: world[cage]
当前进展
- 联系上游制作基于本地的一致性检查工具
- 本地测试
- 联系镜像站测试 TODO
- 发现问题后如何解决?
使用方法
Go 项目很好编译,调用参数为 APKINDEX.tar.gz
所在的目录路径:
$ ./repo-tools/repo-tools local check test-repo/
# 如果缺失
Package zfs-scripts-2.3.2-r0.apk mentioned in the index not found
# 如果成功无显示 TODO 可能要进程返回值设置为 1
个人猜想
可以做成两步同步:先单独拉索引文件,生成 rsync 同步 apk 文件列表,再用这个列表做同步,最后检查没问题时,使用新索引文件覆盖掉旧的。
引用网址
- https://gitlab.alpinelinux.org/alpine/infra/repo-tools/-/merge_requests/1
- https://salsa.debian.org/mirror-team/archvsync/
TinyWL 源码学习记录
2025 年 5 月 22 日
数据结构 wl_list
wl_list
是标准的双向链表,其中第一个元素与最后一个元素首尾相连。使用中仅关心 wl_list
成员,不关注实际结构体类型:
struct wl_list list_head;
struct element {
int data;
struct wl_list link;
}
对头节点来讲,需要显式手动初始化,双向指针指向自身:
void wl_list_init(struct wl_list *list) {
list->prev = list;
list->next = list;
}
// wayland/src/wayland-util.c
因此对于头节点位置的插入、链表判空等操作均为 O(1) 复杂度。
对于元素,将结构体子成员 struct wl_list link
插入链并保存(代码略)。读取时使用库中提供的宏:
#define wl_container_of(ptr, sample, member) \
(__typeof__(sample))((char *)(ptr) - \
offsetof(__typeof__(*sample), member))
sample
是此处表示元素的指针类型,仅用做类型计算,因此传入 NULL 等皆可ptr
表示链表中保存的wl_list
成员的指针member
表示元素的wl_list
子成员的字段名
结构体成员的偏移量一般为非负的,所以拿 ptr
减去偏移量得到 sample
的地址;不同类型的指针做减法运算时,是以该类型大小为单位进行计算的,因此这里强转为 char *
占用 1 字节调整地址。
对链表的遍历库中有若干 helper:
wl_list_for_each(pos, head, member)
wl_list_for_each_safe(pos, tmp, head, member)
wl_list_for_each_reverse(pos, head, member)
wl_list_for_each_reverse_safe(pos, tmp, head, member)
pos
和 tmp
表示元素的指针容器,member
与前文宏相同,是成员字段名,通常为 link
。
Why are there "key" and "modifiers" two events at the same time?
根据协议,key 表示物理按键序列,而 modifiers 用来同步当前修饰键的逻辑状态
- 客户端 enter 之后会收到一次 modifiers 同步
- 修饰键 key 触发后也会收到 modifiers 状态激活
在 wlroots/types/seat/wlr_seat_keyboard.c
中可以看到调用顺序:
wlr_seat_keyboard_notify_enter
wlr_seat_keyboard_enter
wlr_seat_keyboard_send_modifiers
第二种情况暂未发现 // TODO
对窗口的移动
客户端主动发出请求 xdg_toplevel::move
,服务端在 xdg_toplevel_request_move
中处理
server->grabbed_toplevel = toplevel;
server->cursor_mode = TINYWL_CURSOR_MOVE;
server->grab_x = server->cursor->x - toplevel->scene_tree->node.x;
server->grab_y = server->cursor->y - toplevel->scene_tree->node.y;
处理该请求主要为记录状态,包括希望移动的窗口、触发时的鼠标位置等。后续具体移动逻辑由服务端在鼠标事件中负责:
*toplevel = server->grabbed_toplevel;
wlr_scene_node_set_position(
&toplevel->scene_tree->node,
server->cursor->x - server->grab_x,
server->cursor->y - server->grab_y
);
使用“鼠标的实时位置”与请求触发时“窗口顶点与鼠标的相对距离”设置移动过程与最终的窗口位置。
对窗口的尺寸调整
与移动相似,但窗口需要更多调整,涉及到窗口的大小、位置改变。
- 记录光标位置与窗口边缘坐标
- 注意窗口最小尺寸(当调整左上边框时)
- 计算得到新窗口大小并设置
- 计算得到新窗口坐标并设置
对于坐标来说,wlr_xdg_toplevel
的坐标不等于 wlr_surface
的坐标,因此需要考虑两者之间的相对值。
键盘状态-焦点 surface
struct wlr_surface* previous_surface = seat->keyboard_state.focused_surface;
// tinywl.c @focuse_toplevel
在 wlroots
的 seat 中这个状态鼠标键盘各有一个:
struct wlr_surface *pointer_focus = wlr_seat->pointer_state.focused_surface;
if (pointer_focus != NULL &&
wl_resource_get_client(pointer_focus->resource) == client) {
wlr_seat->pointer_state.focused_client = seat_client;
}
struct wlr_surface *keyboard_focus = wlr_seat->keyboard_state.focused_surface;
if (keyboard_focus != NULL &&
wl_resource_get_client(keyboard_focus->resource) == client) {
wlr_seat->keyboard_state.focused_client = seat_client;
}
// types/seat/wlr_seat.c @static struct wlr_seat_client *seat_client_create()
当 wlr_seat
没有资源时,持有的焦点也要销毁(不过一般一个 WM 只有一个实例)
if ((capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) {
if (focused_client != NULL && focused_surface != NULL) {
seat_client_send_pointer_leave_raw(focused_client, focused_surface);
}
}
// types/seat/wlr_seat.c @void wlr_seat_set_capabilities()
所以这个变量可以为空,仅表示当前 seat 相关的焦点状态。常规修改发生在一些 enter 事件上:
wlr_seat_keyboard_enter
wlr_seat_pointer_enter
不过注释说更应该使用 wlr_seat_*_notify_enter
,内部会自行调整。
focus_toplevel
主要在以下三个位置调用:
handle_keybinding
做窗口轮换时重新获得焦点server_cursor_button
鼠标点击事件xdg_toplevel_map
新窗口生成时拿到焦点
对于这三种情况基本可以满足 focuse_output
的等效替换,不过要考虑不可最大化的窗口处理
// TODO
进程与环境变量
2025年5月18日 在想办法用环境变量做程序的配置系统
数据结构
理论上讲应该采用 HashMap 的数据格式保存,以实现 O(1) 的访问速度。
#include <unistd.h>
char **__environ = 0;
weak_alias(__environ, ___environ);
weak_alias(__environ, _environ);
weak_alias(__environ, environ);
// https://git.musl-libc.org/cgit/musl/tree/src/env/__environ.c
但实际使用形如 KEY=VALUE
的字符串数组。
系统调用
对环境变量的读和写均发生在用户态:
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char *getenv(const char *name)
{
size_t l = __strchrnul(name, '=') - name;
if (l && !name[l] && __environ)
for (char **e = __environ; *e; e++)
if (!strncmp(name, *e, l) && l[*e] == '=')
return *e + l+1;
return 0;
}
// https://git.musl-libc.org/cgit/musl/tree/src/env/getenv.c
仅初始化与系统调用有关:
#include <unistd.h>
#include "syscall.h"
int execve(const char *path, char *const argv[], char *const envp[])
{
/* do we need to use environ if envp is null? */
return syscall(SYS_execve, path, argv, envp);
}
// https://git.musl-libc.org/cgit/musl/tree/src/process/execve.c
Bash
在 Bash 中很坏,“环境变量”只是普通变量的一个属性,一般用 export
关键字标识
#define att_exported 0x0000001 /* export to environment */
...
#define att_local 0x0000020 /* variable is local to a function */
// bash/variables.h
/* An array which is passed to commands as their environment. It is
manufactured from the union of the initial environment and the
shell variables that are marked for export. */
char **export_env = (char **)NULL;
// bash/variables.c
所以会出现以下情况,环境变量随变量修改而改变:
export foo=bar
foo=barbar
sh -c 'echo $foo'
# output: barbar
其它对环境变量的构建和修改与普通函数无异
static inline char *
mk_env_string (name, value, attributes)
const char *name, *value;
int attributes;
{
...
{
p = (char *)xmalloc (2 + name_len + value_len);
memcpy (p, name, name_len);
q = p + name_len;
}
q[0] = '=';
memcpy (q + 1, value, value_len + 1);
}
// bash/variables.c
int
shell_execve (command, args, env)
char *command;
char **args, **env;
{
...
execve (command, args, env);
}
// bash/execute_cmd.c
JuDou 句读
2025 年 5 月 14 日 记录一下项目想法
概述
JuDou 是一个基于 LSP 的代码审阅辅助工具,通过在本地建立预缓存,实现在网页端的语义高亮和引用跳转。
- 服务端:资源消耗极低
- 客户端:无需环境配置
- 流水线:自动集成构建
名称
“常买旧书的人,有时会遇到一部书,开首加过句读,夹些破句,中途却停了笔:他点不下去了。”
技术栈
- Golang
- Vue
- Json-RPC 2.0
- Json
盈利
前期贴皮广告,中期 SAAS,后期私有化部署。
直接竞争对手为 GitHub 和 Sourcegraph,潜在包括各类 LLM 分析工具。
参考
- https://github.com/netmute/ctags-lsp
- https://github.com/sourcegraph/jsonrpc2
- https://microsoft.github.io/language-server-protocol/
Git 祖先跳表
2025年5月4日 介绍一下 Bushi 所使用的祖先跳表
目标
快速判断 Commit A 是否是 Commit B 的祖先
- git-ref-contains-commit
- git-log-path-filter
限制
本项目仅考虑提交的第一个 Parent Commit
原理
判断
验证
选择 Aports 仓库中 Commit-ID 分别为 100 和 20000 的两个提交,对应的 Commit-OID 如下
sqlite> SELECT * FROM commits WHERE commit_id = 100 OR commit_id = 20000;
commit_id commit_hash commit_mark parent_id depth repo_id
--------- ---------------------------------------- ----------- --------- ----- -------
100 44a369d15ac69464584099d339a0e1ec1ec7fa66 100 99 99 1
20000 89d3e577e9a10d8f75b87d5ec44d801efbb7f3c7 20000 19999 13920 1
查看网页确实在同一分支中,两者 depth 之差为 13821,转为二进制数得到 2 的 N 次幂
>>> bin(13821)
'0b11010111111101'
即为 13、12、10、8、7、6、5、4、3、2、0 这些序列,下面开始在祖先表中查找
sqlite> SELECT * FROM ancestors WHERE commit_id = 20000 AND level = 13;
commit_id level ancestor_id
--------- ----- -----------
20000 13 7618
sqlite> SELECT * FROM ancestors WHERE commit_id = 7618 AND level = 12;
commit_id level ancestor_id
--------- ----- -----------
7618 12 2133
sqlite> SELECT * FROM ancestors WHERE commit_id = 2133 AND level = 10;
commit_id level ancestor_id
--------- ----- -----------
2133 10 777
sqlite> SELECT * FROM ancestors WHERE commit_id = 777 AND level = 8;
commit_id level ancestor_id
--------- ----- -----------
777 8 372
sqlite> SELECT * FROM ancestors WHERE commit_id = 372 AND level = 7;
commit_id level ancestor_id
--------- ----- -----------
372 7 244
sqlite> SELECT * FROM ancestors WHERE commit_id = 244 AND level = 6;
commit_id level ancestor_id
--------- ----- -----------
244 6 161
sqlite> SELECT * FROM ancestors WHERE commit_id = 161 AND level = 5;
commit_id level ancestor_id
--------- ----- -----------
161 5 129
sqlite> SELECT * FROM ancestors WHERE commit_id = 129 AND level = 4;
commit_id level ancestor_id
--------- ----- -----------
129 4 113
sqlite> SELECT * FROM ancestors WHERE commit_id = 113 AND level = 3;
commit_id level ancestor_id
--------- ----- -----------
113 3 105
sqlite> SELECT * FROM ancestors WHERE commit_id = 105 AND level = 2;
commit_id level ancestor_id
--------- ----- -----------
105 2 101
sqlite> SELECT * FROM ancestors WHERE commit_id = 101 AND level = 0;
commit_id level ancestor_id
--------- ----- -----------
101 0 100
最终得到 Commit-ID 与目标 ID 相同,两者位于同一分支中。
GitWeb Collect
2025 年 4 月 12 日 cosplay 一下产品经理,对潜在精品进行汇总
汇总有遗漏,请额外查看:
- https://forge.runxiyu.org/stuff/-/repos/code-forge-list/
- https://en.wikipedia.org/wiki/Comparison_of_source-code-hosting_facilities
- https://en.wikipedia.org/wiki/Forge_(software)#Examples
Library
- libgit2 (c)
- git2-rs (c-rust)
- pygit2 (c-python)
- rugged (c-ruby)
- nodegit (c-javascript)
- go-git (golang)
- gitoxide (rust)
- ungit (javascript)
- Gitaly (git-golang)
严格来说 Gitaly 不算库,它是 GitLab 在替换掉 Rugged 之后的 RPC 远程调用,内部是对 Git 原版命令的封装
Simple
Perl
- GitWeb(Perl, Git builtin)
- https://github.com/yuki-kimoto/gitweblite
- https://github.com/cantonios/gitwebhub
- https://github.com/broquaint/Gitalist
Rust
- https://github.com/w4/rgit
- https://github.com/alexwennerberg/mygit
- https://github.com/qaqland/bushi Here is mine
Golang
- https://github.com/icyphox/legit
- https://github.com/emersion/matcha
- https://github.com/esote/gitweb
- SoftServe
C
- https://git.zx2c4.com/cgit/about
- https://github.com/oxalorg/stagit
- https://github.com/Ernest1338/GitWebSee
Java
Lua
Full
VSCode Remote SSH 连接 Alpine Linux
2025 年 4 月 11 日
远程连接时的步骤大致如下:
- 安装 remote-cli 到服务端
- remote-cli 下载并启动 vscode-server
- 本地客户端与 vscode-server 连接
基础
$ apk add bash curl git libstdc++ procps-ng
如果还是在启动时报错,可以查看 vscode 仓库对应脚本 resources/server/bin/helpers/check-requirements-linux.sh
插件
// TODO
References
Git 很慢
//
2025 年 1 月 23 日 抱怨一下 Git 的数据结构
2025 年 4 月 13 日 后续:ROGit 改名为 Bushi 卜筮,基本解决了这个问题,其余部分还在做
//
一年半之前,我有一个 ROGit 项目计划,旨在构建一个 cgit 同生态位的现代替代品
- ReadOnly:网页所有操作对 Git 仓库来说都是只读的,区别于 GitHub 或者 Gitea
- Rust:没有学习过 Rust 所以借此机会尝试一点新东西
- Online:以后可能除了 http 还会做 gemini 协议网站,所以很在线
得益于技术栈发展,cgit 所使用的 CGI 协议已相当少见。ROGit 相比 cgit 有以下几个目标:
- 现代 CSS 界面及交互设计
- 避免 Git 源码依赖
- 灵活的 API 接口及扩展
当前正在做后端的数据初始化部分,希望在启动时对所有的提交及其变动修改预缓存到 SQLite,但是遇到了严重的性能问题:解析 aports 仓库需要 3 个小时(对于初始化而言 3 分钟是可以接受的)。
注:使用 libgit2 作为解析库,火焰图时关闭缓存、关闭 Hash 验证,在开启自带缓存后性能略有提高
Git Object
理论上讲 Git 中有三个大类别的对象:Commit、Tree、Blob,具体到解析时还有 Commit 的同类 Tag 及 Note。这些对象以各自 Object 的 hash 作为索引,经 zlib 压缩后保存在 .git/objects
下的文件中,纯正文件系统驱动,并在需要时被 Git 解析。
Git 的数据结构为写多读少设计,因此其他程序应避免将 Git 作为数据库使用。具体保存格式有 Loose Objects 和 Packfiles 两类,参考阅读:
- https://git-scm.com/book/en/v2/Git-Internals-Packfiles
- https://github.blog/open-source/git/gits-database-internals-i-packed-object-store/
Git Commit
提交 Commit 指向自己的历史来源,有 0 个、1 个、2 个或者更多个 Parent Commit。不同的数量表示了这次提交的不同种类,是 root 节点还是 fast-forward 这种单线链表,或者是合并分支的 Merge 节点,超过 2 的情况不多见,是特殊的合并节点,比如内核的提交中出现的:
https://github.com/torvalds/linux/commit/9b25d604182169a08b206306b312d2df26b5f502
当然像提交信息、提交人、提交时间、以及 Committer 与 Author 之间的区别这里不再赘述。Commit 还保存了提交时的文件(树)快照,指向当前 Commit 相随的 Tree。
- Tree Hash
- Parent Commit Hash
- Author
- Committer
- Commit Message
这就是一个 Commit 对象信息的全部,当一个普通线性提交发生时,Git 会扫描当前 WorkTree 生成 TreeHash,底层保存的数据中并不关心此次提交的修改。
Git Log
Git 并没有在 Commit 中描述这次提交修改了哪些文件,所以若要知晓给定文件的最后修改日期,过程较为艰难:
- 获取 Commit 的 TreeHash 与 Parent Commit 的 TreeHash
- 在两个 Tree Object 中遍历,查找修改的文件
- 检查给定文件是否在本次 Diff 中修改
- 重复上述操作,直到两次提交的树之间存在期望差异
Git 内部在第二步和第三步之间应该有查询优化,仅对比给定的路径,但整体在没有对应数据结构的情况下进行类似 SQL 的结构化查询还是相当消耗性能,面对稍微大一点的仓库,时间来到秒级
$ /usr/bin/time git -c core.commitGraph=false log --oneline -n 10 community/xmake/
81380060446 community/xmake: upgrade to 2.9.9
7c21bea5624 */*: replace non-POSIX find -not option
a92fe0ba060 community/xmake: upgrade to 2.9.7
12374870a8f community/xmake: upgrade to 2.9.6
12c188ec967 community/xmake: upgrade to 2.9.5
0096aeef07c community/xmake: upgrade to 2.9.4
4b130eb7f5a community/xmake: upgrade to 2.9.3
d516ffbb476 community/xmake: upgrade to 2.9.2
d74b311e776 community/xmake: move from testing
real 0m 51.11s
user 0m 41.44s
sys 0m 9.56s
Git CommitGraph
Git 在 2.18 版本后引入了提交图的概念,使用单独的提交图文件对提交关系做缓存,所以在理想情况下,最快的查询条件为:
$ git gc --aggressive
$ git commit-graph write --changed-paths
- 全量 pack 保存,避免 Git Object 保存在 loose 时反复文件 IO
- 开启提交图缓存,包括提交修改记录
$ /usr/bin/time git -c core.commitGraph=true log --oneline -n 10 community/xmake/
81380060446 community/xmake: upgrade to 2.9.9
7c21bea5624 */*: replace non-POSIX find -not option
a92fe0ba060 community/xmake: upgrade to 2.9.7
12374870a8f community/xmake: upgrade to 2.9.6
12c188ec967 community/xmake: upgrade to 2.9.5
0096aeef07c community/xmake: upgrade to 2.9.4
4b130eb7f5a community/xmake: upgrade to 2.9.3
d516ffbb476 community/xmake: upgrade to 2.9.2
d74b311e776 community/xmake: move from testing
real 0m 1.99s
user 0m 1.17s
sys 0m 0.81s
提升很明显(但是其它第三方 Git 实现都没类似功能)
Git Blame
Blame 是 Log 的细节版本,不仅找到相关的提交,而且按照行定位每行的最后修改提交,类似糖豆人的蜂巢关卡,直到完成对目标文件当前所有行的修改提交定位,终止遍历。
所以这里有个 ddos 的小技巧,请求一个不存在的文件,Git 不得不遍历所有提交定位最终修改(但是似乎大家普遍缓存了每个文件的最终修改提交,所以可能没用)
References
- https://github.com/libgit2/libgit2-backends
- https://github.com/go-git/go-git/issues/811
- https://github.com/rust-lang/git2-rs/issues/222
- https://github.com/libgit2/libgit2/issues/3027
- https://github.com/eafcc/eafcc-core/blob/master/src/storage_backends/git/p2p/custom_backend_demo.rs
- https://git-scm.com/docs/git-commit-graph
- https://devblogs.microsoft.com/devops/supercharging-the-git-commit-graph/
- https://devblogs.microsoft.com/devops/updates-to-the-git-commit-graph-feature/
Alpine Linux 软件打包
2024 年 1 月 14 日
软件仓库的总部叫 aports,目前是官方 GitLab 实例上的一个超大 repository,所有与官方有关的软件包提交修改都基于仓库的流水线,有什么问题可以查阅 Wiki,也可以前往 oftc.net 上的 #alpine-devel
频道(IRC)寻求帮助。
$ git clone --depth=1 ...
据观察基于邮箱的协作好像不太能用,只能注册 GitLab 使用。找到官方的仓库 fork 到自己名下(就像 GitHub 那样),接下来的大致流程:
- 绑定密钥、设置用户名与用户邮箱
- 克隆自己的 fork 到本地,耐心等待
- 为自己需要修改的地方创建分支
- 修改(测试)并提交,推送新分支到仓库
- 在网页里自己的仓库页面提交合并请求
Alpine Linux 软件包的配置文件 APKBUILD 与 Arch Linux 的 PKGBUILD 非常相似,可以偷偷去他们的网站学习打包经验,但是不要抄袭——发行版不同、工具链不通用、分包策略也不同。同时也非常推荐 Fedora 家的包,他们相比 Arch 更加严格规范,易于参考。
网址
- 包管理器使用 https://wiki.alpinelinux.org/wiki/Alpine_Package_Keeper
- 新建软件包 https://wiki.alpinelinux.org/wiki/Creating_an_Alpine_package
- 配置文件说明 https://wiki.alpinelinux.org/wiki/APKBUILD_Reference
- 打包相关工具 https://wiki.alpinelinux.org/wiki/Abuild_and_Helpers
- 与 aports 仓库相关的 aports/README.md
- 代码与提交格式要求 CODINGSTYLE.md COMMITSTYLE.md
- Arch Linux 的包 https://archlinux.org/packages/
- Fedora 的包 http://src.fedoraproject.org/
FAQ
由于 musl 引起的编译错误如何解决?
应该都有人遇到过,aports 搜一搜自己也做个补丁
如何知道软件需要的依赖?
构建依赖:abuild rootbld
一个一个添加 dev 包测试
运行依赖:打包时会自动识别大部分动态库,手动加上额外的运行时依赖
编译需要的 gcc 是依赖吗?
build-base
已有,不需要写,apk info -r build-base
查看类似基础组件
网络问题拉不下来构建需要的源码
使用 http 代理,例如:https_proxy=http://localhost:7890/ abuild checksum
软件包依赖另外一个自己打的本地包
abuild rootbld
时添加 ~/packages/testing/
到 ~/aports/testing/.rootbld-repositories
怎么知道安装包里包含什么文件
新版 tar 会自动识别压缩格式,执行tar -vtf file.apk
获得文件列表
每次下载软件包耗时很久
① 开启 setup-apkcache
② $ mirror=http://mirrors.tuna.tsinghua.edu.cn/alpine abuild rootbld
最后如果有其他问题可以联系我
Apktools
2025 年 4 月 13 日 Alpine Linux 相关的基础使用,主要是系统包管理器
mirror
所有软件包的镜像信息保存在 /etc/apk/repositories
,系统默认自带有 vi 编辑器,可以尝试修改。
有三种选择推荐:指定版本号、最新稳定版、滚动版,不推荐混合使用可能造成依赖冲突。
# 国内推荐使用:南阳理工学院开源软件镜像站
# https://mirror.nyist.edu.cn/
# 指定版本号
http://mirror.nyist.edu.cn/alpine/v3.21/main
http://mirror.nyist.edu.cn/alpine/v3.21/community
# 最新稳定版
http://mirror.nyist.edu.cn/alpine/latest-stable/main
http://mirror.nyist.edu.cn/alpine/latest-stable/community
# 滚动版
http://mirror.nyist.edu.cn/alpine/edge/main
http://mirror.nyist.edu.cn/alpine/edge/community
http://mirror.nyist.edu.cn/alpine/edge/testing
修改后刷新生效(不刷新也没事,默认间隔后使用会自动刷新)
$ apk update
$ apk upgrade
cli
已安装软件包信息保存在 /etc/apk/world
中,查看此文件了解系统中显式安装的软件包。
搜索比较弱鸡,推荐用谷歌、官方搜包站、和本人自建服务辅助进行
# 默认
$ apk search helix
# 搜索终端命令
$ apk search cmd:hx
# 搜动态链接库
$ apk search so:libgit2.so
安装和卸载软件包
$ apk add xxx
$ apk del xxx
查看此系统中哪些包依赖 xxx
$ apk info -r xxx
查看已安装的给定包 xxx 内部包含哪些文件
$ apk info -L xxx
example
docker
world
/etc/apk/world
$ apk fix
Setup Aports
2025年4月30日 设置 Aports 本地环境
Abuild
配置本地 Abuild,遵照官方 Wiki
apk add abuild abuild-rootbld alpine-sdk
addgroup xxx abuild
abuild-keygen -a -i
在 ~/.abuild/abuild.conf
可以配置部分设置选项,如颜色、镜像等
Ssh
官方的 GitLab 注册并配置 Ssh 免密,与常见操作相同
cd .ssh
ssh-keygen # 生成 key
cat xxx.pub # 复制到网页里
vim config
ssh -T [email protected]
Git
推荐 pull 时从镜像拉,因为官方的仓库又大又慢,以下是本人初始化过程
Clone
从镜像进行首次 Clone,默认只包括 master 分支
git config --global user.name qaqland
git config --global user.email "[email protected]"
git clone https://mirror.nyist.edu.cn/git/aports
cd aports/
git remote -v
Remote
将镜像上游改名到 mirror,设置官方上游到 origin
git remote add mirror https://mirror.nyist.edu.cn/git/aports
git remote set-url origin [email protected]:qaqland/aports.git
最终效果如下
$ git remote -v
mirror https://mirror.nyist.edu.cn/git/aports (fetch)
mirror https://mirror.nyist.edu.cn/git/aports (push)
origin [email protected]:qaqland/aports.git (fetch)
origin [email protected]:qaqland/aports.git (push)
Branch
更新 remote 并把 master 绑定到 mirror,因为 master 只用来同步 Pull
git fetch origin
git fetch mirror
git branch --set-upstream-to=mirror/master
git branch -vv
每当自己有新修改时,推送自己的新分支到 origin
git checkout -b xxx
git commit
git push -u origin xxx
Abuild & Rust(Cargo)
rustup
setup mirror
keep offline
options="net" # cargo fetch
prepare() {
default_prepare
cargo fetch --target="$CTARGET" --locked
}
build() {
cargo auditable build --frozen --release
:
}
clap(completions)
subpackages="
$pkgname-bash-completion
$pkgname-fish-completion
$pkgname-zsh-completion
"
build() {
:
./target/release/mdbook completions bash > $pkgname.bash
./target/release/mdbook completions fish > $pkgname.fish
./target/release/mdbook completions zsh > $pkgname.zsh
}
package() {
:
install -Dm644 $pkgname.bash "$pkgdir"/usr/share/bash-completion/completions/$pkgname
install -Dm644 $pkgname.fish "$pkgdir"/usr/share/fish/vendor_completions.d/$pkgname.fish
install -Dm644 $pkgname.zsh "$pkgdir"/usr/share/zsh/site-functions/_$pkgname
}
make patches
$ git init .
$ git add Cargo.toml Cargo.lock
$ git diff > ../../01-update-lock.patch
download patch from GitHub and GitLab
tests
options="!check" # upstream has no test
check() {
cargo test --frozen
}
$ cargo test -- --skip xxx
patches
use system library
- env
- config.toml
- patch