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 文件列表,再用这个列表做同步,最后检查没问题时,使用新索引文件覆盖掉旧的。

引用网址

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)

postmp 表示元素的指针容器,member 与前文宏相同,是成员字段名,通常为 link

Why are there "key" and "modifiers" two events at the same time?

根据协议,key 表示物理按键序列,而 modifiers 用来同步当前修饰键的逻辑状态

  1. 客户端 enter 之后会收到一次 modifiers 同步
  2. 修饰键 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 分析工具。

参考

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 一下产品经理,对潜在精品进行汇总

汇总有遗漏,请额外查看:

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

Rust

Golang

C

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 分钟是可以接受的)。

rogit meet aports - flamegraph

注:使用 libgit2 作为解析库,火焰图时关闭缓存、关闭 Hash 验证,在开启自带缓存后性能略有提高

Git Object

理论上讲 Git 中有三个大类别的对象:Commit、Tree、Blob,具体到解析时还有 Commit 的同类 Tag 及 Note。这些对象以各自 Object 的 hash 作为索引,经 zlib 压缩后保存在 .git/objects 下的文件中,纯正文件系统驱动,并在需要时被 Git 解析。

Git 的数据结构为写多读少设计,因此其他程序应避免将 Git 作为数据库使用。具体保存格式有 Loose Objects 和 Packfiles 两类,参考阅读:

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
  1. 全量 pack 保存,避免 Git Object 保存在 loose 时反复文件 IO
  2. 开启提交图缓存,包括提交修改记录
$ /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

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 更加严格规范,易于参考。

网址

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