archlinuxcn / lilac

Lilac is the build bot for archlinuxcn

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Github actions 打包支持

petronny opened this issue · comments

archlinuxcn/repo#1575

FAQ:

  1. 必要性何在
    我觉得带有run_cmd的lilac.*其实都是不安全的,万一来一个rm -rf ~虽说不会搞炸系统但是也很恶心的,所以扔个docker里跑可能也挺好的
    剩下的就是透明度啥的

  2. 和现有的lilac是什么关系
    我打算把这个作为一个特殊的build_prefix交给lilac,即整体还是由lilac决定打哪些包和时序上的依赖关系
    然后每个pkgbase都做一个专门的workflow,lilac通过发送一个内容为对应pkgbase的POST请求触发workflow
    结束时一个artifact到这个workflow run,然后lilac下载这个artifact就可以和原来的lilac兼容

  3. 可否使用devtools
    可以

  4. 依赖关系如何解决
    首先前置依赖的包的artifact是60天有效。
    然后对于一个有依赖的包,先查找依赖包的artifact还在不在,在的话直接下载这个;
    如果artifact不在了,说明肯定超过60天了,mirror里肯定有,从合适的mirror下载即可。

  5. 缓存如何解决
    github actions有缓存,不过只有5G大小。
    建议是做一个叫pacman-cache-x86_64的缓存只用来缓存extra-x86_64-build下载的包。
    repo_depends、source等的只能下载了

  6. 性能问题如何解决
    虽然单个包的确慢,但结合 #145 的话,20个包并行打还是很好的。
    对于某些特别吃资源的包,可以不用这个,还在本地打包。
    后期的话希望lilac也由一个专门的workflow来运行,高性能服务器做一个self-hosted action runner

  7. 有无预览
    我给无依赖的yay做了一个,可以参考 https://github.com/petronny/arch4edu/blob/master/.github/workflows/yay.yml
    一个对应的运行实例在
    https://github.com/petronny/arch4edu/actions/runs/113176984
    打包结果和log在
    https://github.com/petronny/arch4edu/suites/710761537/artifacts/6924885
    https://github.com/petronny/arch4edu/suites/710761537/artifacts/6924884

  8. 目前还有何问题

    1. 自动下载依赖包没做了
      我可以做一个python版本的,就是递归读一下yml,然后用github token/mirror下载
      但最好是通过js实现并封装
    2. workflow配置文件巨长
      调查了一下可以通过自定义action的做法封装一些固定操作
      我写了一个预览版(就是假设actions都封装好了),见最后
      应该和原来的lilac.*差不多了
    3. actions如何封装?
      查了一下custom actions分为docker和js版两种。
      我们应该是要用js版的,就是在js里调用各种shell命令,下载文件什么的
      可以参考 https://github.com/actions/checkout
      这个就是通过git命令clone一个repo到当前目录,后面的steps可以读。
      所以文件IO,命令执行什么的应该都是可行的。
      不过有个问题是我js基本不会。。。所以需要帮助了。。。

name: yay

on:
  repository_dispatch:
    types: [yay]

jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: archlinux
      options: --privileged
      volumes:
        - /sys/fs/cgroup:/sys/fs/cgroup

    steps:
      - name: Initialize build environment
        uses: archlinuxcn/lilac/actions/initialize-extra-x86_64@master

      - uses: actions/checkout@master

      - uses: archlinuxcn/lilac/actions/aur-pre-build@master

      - name: Build package
        id: build
        uses: archlinuxcn/lilac/actions/extra-x86_64-build@master
        with:
          exclude_package: yay-test yay-test2
          repo_depends: a b/b
          archbuild_args:
          makechrootpkg_args:
          makepkg_args:

      - uses: archlinuxcn/lilac/actions/aur-post-build@master

      - uses: archlinuxcn/lilac/actions/post-process@master

1. 安全性的话,其实也有办法,比如 userns + bind mount 或者 syscall filter 限制一下就好了。编译机上了 cgroups v2,所以 docker 已经跑不动了。

2. 不要搞特殊哇。新建个 runner 字段多好。

6. 那就先把 #145 给解决了。

8. 为什么要用 js 啊,那些 pre/post_build,不能调 Python 的吗?

2. 都可以😂
临时的话写一个build_prefix = actions-extra-x86_64就无痛和现在兼容了
比较方便验证

8. 如果前置环境配好了可以调用python的lilac

- run: |
          cd yay && ./lilac --pre_build_only
- uses: archlinuxcn/lilac/actions/extra-x86_64-build@master

或者就是直接用shell重写pre_build中自定义的部分,其他封装

- uses: archlinuxcn/lilac/actions/aur-pre-build@master
- run: |
          cd yay && sed 's/aaa/bbb/g' -i PKGBUILD
- uses: archlinuxcn/lilac/actions/extra-x86_64-build@master

我比较倾向后者,配置环境每个run都会做,少重复一点是一点
另外反正workflow都是新文件,不太麻烦的就重写
麻烦的就先不迁移了。。。

我决定优先推进一下这个了。。。
最近被国内的网络环境搞得有点烦。。。转移到github解决网络问题

我想了一下重新造一遍lilac的轮子还是不太好。。。
比如比较新老pkgver什么的。。。

总之我就做了一个完全兼容现有lilac.py的workflow
https://github.com/petronny/arch4edu/blob/master/.github/workflows/build.yml
通过控制POST请求的参数可以指定运行某个包的lilac.py里的single_main
目前应该可以打出任何无依赖的包,比如:

下一步只要在single_main那里

  1. 通过lilac.yaml找到所有依赖
  2. 把所有依赖都下载好
  3. 把依赖都加到makechrootpkg_args里面

我觉得就目标达成了

问题与讨论

  1. 这个workflow可以在任何repo里运行,理论上可以通过开马甲来突破cache 5GB限制
  2. lilac 使用的 https://github.com/petronny/lilac 干掉了kill_children和build_cleaner
    docker环境中应该不需要了吧?
  3. 部分脚本封装在了 https://github.com/petronny/action-tools
    不写 js 那套了。。。直接执行得了。。。
  4. 这个workflow还有其他问题么?
  5. 其实每个包一个独立workflow的我也写了,参考
    https://github.com/petronny/arch4edu/blob/master/.github/workflows/yay.yml
    做好了以后可以把lilac.yaml和lilac.py都吃了
    但缺点就是需要做lilac API 向 action-tools 中的 API 的迁移
    所以 这个lilac兼容的共享workflow 和 独立workflow 如何取舍为好。。?

这个 container 选项指定的 container 是哪里来的?

GitHub Actions 的免费配额和限制文档在哪里来着?

kill_children 除了超时处理外,也用于大日志的处理,所以还是需要的(除非你想等着有问题的构建把所有可用空间写完然后等超时)。

你是想让每个包独立跑,还是你在服务器上跑一个 lilac 实例来调度?

复用 lilac 的代码吧,那么多 api 函数你重新实现起来也挺费事的。

我想比较好的方案是,只把更新检查和构建过程交给 github actions,剩下的事务还是由 lilac 处理。single_main 是给本地跑来测试用的,我们可以再为 github actions 搞一个 github_main,在其中做依赖包下载之类的事情。

这个 container 选项指定的 container 是哪里来的?

应该是docker hub,这里有个测试第三方container的例子
https://github.com/petronny/docker-image/actions/runs/111211548

GitHub Actions 的免费配额和限制文档在哪里来着?

对公有repo没有限额,只对私有有限额。
主要限制是运行实例的限制和cache的限制

你是想让每个包独立跑,还是你在服务器上跑一个 lilac 实例来调度?

本地服务器上跑一个 lilac 实例来调度,每个包由build.yml派生一个独立的runner打包。runner之间可以并行。
lilac先运行pre_build()函数用于比较新老pkgver,如果新的比老的老就报错。
然后调用github actions。
actions内部重复执行一遍pre_build(),然后打包。
执行一个独立的action_post_build(),(比如用来把不想要的包删了,就不用上传了)
打包完成后上传包、PKGBUILD等、log到artifact。
lilac的post_build()的内容为下载后,包存本地,PKGBUILD等更新,log保存到对应变量中。
之后应该就能跟后面的创建commit、签名包等对上了。

kill_children 除了超时处理外,也用于大日志的处理,所以还是需要的(除非你想等着有问题的构建把所有可用空间写完然后等超时)。

那也不应该是kill_children,直接通过API把这个workflow取消就行

只把更新检查和构建过程交给 github actions

检查更新也可以么?其实nvchecker的话,加个proxy的选项应该就能解决网络问题。。。

github actions 搞一个 github_main

那就action_main吧,可以通过sys.argv[1]区分。
不过目前不冲突,我可以先实现着。。。

首先前置依赖的包的artifact是60天有效。
然后对于一个有依赖的包,先查找依赖包的artifact还在不在,在的话直接下载这个;
如果artifact不在了,说明肯定超过60天了,mirror里肯定有,从合适的mirror下载即可。

我在想要不artifact不在了,说明肯定超过60天了,那也就rebuild一遍得了?
rebuild一些很久之前包可能能帮助解决一些问题?

应该是docker hub

这个么?

另外我看到你的 initialize.sh 脚本里安装了一些依赖包。这个可能会导致打出来的包隐式地依赖上这些包。是否可能再调用 archbuild 脚本来打包?

对公有repo没有限额

所以可以跑需要运行很长时间的任务?

那也不应该是kill_children,直接通过API把这个workflow取消就行

嗯,有 API 那就用 API。

检查更新也可以么?其实nvchecker的话,加个proxy的选项应该就能解决网络问题。。。

实际上 nvchecker 是最容易搬上 github actions 上的呀。当然你不想搬也可以不搬。

我在想要不artifact不在了,说明肯定超过60天了,那也就rebuild一遍得了?

可以是可以。但是这就会导致不必要的依赖包更新呢。虽然可能解决一些问题,但也会引出另一些问题。

  1. pkgrel 一直涨一直涨,用户也要每两个月更新一个包
  2. 要是这次打包失败了怎么办?比如源码所在的网站宕机了啥的

这个么?

我觉得是

是否可能再调用 archbuild 脚本来打包

现在就是调用的archbuild,log里可以看到

微信截图_20200611163627

所以可以跑需要运行很长时间的任务?

时间是6小时。先把简单的包搬上去,之后可能通过自建runner把其他搬上去吧。。。

pkgrel 一直涨一直涨,用户也要每两个月更新一个包

那可能就不做这个了吧。。。

我做了一个带依赖打包的预览了
https://github.com/petronny/arch4edu/actions/runs/132493800

目前还是只从repo下载,从artifact下载WIP

主要功能:

(终于打出了因为网络问题失败好几周的包,老母亲留下了欣慰的泪水)

试试使用这个处理包下载?

https://github.com/archlinuxcn/lilac/blob/gh-actions/lilac2/remote/gh_actions.py

我不知道当前用户有没有 /var/cache/pacman/pkg 的写权限。如果没有,需要换一个目录来放下回来的缓存文件。

另外这个缓存需要手动管理么?还是 github 会自动删掉过多的文件呀?

试试使用这个处理包下载?

可能得等等了,今天打算做一个简单的 action-extra-x86_64-build ,然后把后面跟lilac对接的先都走通。然后就得下周末才有时间了。。。

不过初步看了一下,从repo下载那,我觉得目前的alpm下载相对于download-package-from-artifact.sh的pacman自带下载还有这些问题:

  1. 前一个mirror失败了自动尝试下一个 repo
  2. 下载完自动校验 gpg

另外这个缓存需要手动管理么?还是 github 会自动删掉过多的文件呀?

当前的cache使用方式是 每次把cache文件夹由 https://github.com/actions/cache 快照成一个cache-时间.zst交给github,github 下次就会找一个最新的cache-时间.zst恢复出来
(我觉得这样最适合pacman的cache)

限制为所有快照文件的容量为5G,超了就会从老的开始删,直到小于 5G
我猜如果最后一个cache-时间.zst> 5G 了,那么所有的cache就都被删了,就没有cache了。

所以最好就是在不超过5G的情况下尽可能多塞东西。。。

据我之前的观察,如果不存repo_depends,剩下的差不多就是5G
所以我建议repo_depends不要放 /var/cache/pacman/pkg,就每次下载吧。。。
我目前存的 ~/repo_depends

如果某个特定包经常要下大repo_depends,可能得开个马甲repo只打这个包,这样就独享一个5G cache

我觉得目前的alpm下载相对于download-package-from-artifact.sh的pacman自带下载还有这些问题

这些应该都有。不过 gpg 需要设置好。记得把 keyring 包装上应该就好了。我测试的时候,截断文件它会报告校验和错误,所以我不确定它有没有用 gpg……

所以我建议repo_depends不要放 /var/cache/pacman/pkg,就每次下载吧。。。

可以。去把 utils.py 里那个路径改了就行了。

记得把 keyring 包装上应该就好了。

我觉得这样不好。。。最好是专门做一个 gpgdir
pacman-key --init --gpgdir gnupg
pacman-key --populate third_party
然后pacman 只从这个gpgdir校验

啊,pyalpm 没实现 questioncb……指定 gpgdir 倒是没有问题。

看了一下,实现 questioncb 并不困难。需要的话我有空 pr 一个。

算了,既然不要缓存的话,暂时用不着回答问题。

从artifact下载包还有个冒号的问题
就是文件名的:我都替换成了COLON
否则不让上传。。。
下载后得自己换回来。。。

action-extra-x86_64-build第一版出现了:
已更新第二版

#!/bin/sh
set -e

export TOKEN=

repo=petronny/arch4edu

package=$(realpath .)
package=$(basename $package)
uuid=$(uuidgen)

curl -sS -X POST https://api.github.com/repos/${repo}/dispatches \
        -H "Accept: application/vnd.github.everest-preview+json" \
        -H "Authorization: token $TOKEN" \
        --data "{\"event_type\": \"$package $uuid\"}"

while true
do
        sleep 30
        download-file-from-artifact.zsh \
                --repo ${repo} \
                --file $package.$uuid \
                --type file \
                --save-path . 2>/dev/null || continue
        break
done

mv $package.$uuid workflow_id

workflow_id=$(cat workflow_id)

download-file-from-artifact.zsh \
        --repo ${repo} \
        --workflow $workflow_id \
        --file $package.log \
        --type file \
        --save-path /tmp \
        --save-json artifact.json || (echo "Error:\tDownload $package.log failed" && exit 1)

cat /tmp/$package.log

download-file-from-artifact.zsh \
        --repo ${repo} \
        --workflow $workflow_id \
        --file $package.patch \
        --type file \
        --save-path /tmp || (echo "Error:\tDownload $package.patch failed" && exit 1)

for i in $(git status -s | grep " M" | cut -c 4-)
do
        git checkout -- $i
done

for i in $(git status -s | grep "^A" | cut -c 4-)
do
        [ $(basename $i) = artifact.json ] && continue
        [ $(basename $i) = workflow_id ] && continue
        rm $i
done

git add workflow_id artifact.json

git apply /tmp/$package.patch

download-file-from-artifact.zsh \
        --repo ${repo} \
        --workflow $workflow_id \
        --file $package \
        --type package \
        --save-path . || (echo "Error:\tDownload $package failed" && exit 1)

action_main也好了。整体配套是这样

#!/usr/bin/env python3
from lilaclib import *

maintainers = [{'github': 'petronny', 'email': 'Jingbei Li <i@jingbei.li>'}]
update_on = [{'aur': None}]
build_prefix = '/path/to/action-extra-x86_64'
pre_build = aur_pre_build
post_build = aur_post_build

if __name__ == '__main__':
    from action_tools import action_main
    action_main(build_prefix)

sys.argv[1]是'action'的时候(action中运行的时候会加这个参数)
action_main会下载repo_depends,并去掉build_prefix中的.*action-
然后启动single_main

没有action参数就会退化成 single_main

问题与讨论

1. 现在有4个从artifact下载东西的sh...

  • download-file-from-artifact.sh
    从所有artifact中搜索,下载后自动解压zip
  • download-package-from-artifact.sh
    从所有artifact中搜索,下载后只从zip中解压指定的pkg.tar.*
  • download-file-from-workflow.sh
    从指定的workflow中的artifact中搜索,下载后自动解压zip
  • download-packages-from-workflow.sh
    从指定的workflow中的artifact中下载包,下载后自动解压所有pkg.tar.*

这4个应该是要合并成一个函数的,但我shell不太会写argparse...先分成4个了。。。

2. 有关uuid
首先github没有通过event_type检索workflow runs的API...
我想了一个通过uuid匹配的办法,应该可以应对所有情况(并行、并发等)

3. 执行 action-extra-x86_64-build 前,lilac会运行一次pre_build
action-extra-x86_64-build 执行中,workflow中也会运行一次pre_build,并把最终的改动传一个commit patch到artifact
所以 workflow 结束后,action-extra-x86_64-build 要把本地的所有改动都扔了
然后直接应用从artifact下载的patch就行了

我有个问题是把本地的所有改动都扔了这个怎么做。。。
改删好办,新文件的情况咋处理。

或者有更好的解决办法么。。。

这4个应该是要合并成一个函数的,但我shell不太会写argparse...先分成4个了。。。

这就是为什么我要用 Python。

我有个问题是把本地的所有改动都扔了这个怎么做。。。

你是要 git clean -xfd

看了一下,实现 questioncb 并不困难。需要的话我有空 pr 一个。

能顺便帮忙pr一个pacman -Sww package就可以无视依赖下载包么?
当时找了半天发现竟然没有这个功能。。。

你是要 git clean -xfd?

下周再看[捂脸]

能顺便帮忙pr一个pacman -Sww package就可以无视依赖下载包么?

pacman -Swd 不行么?

pacman -Swd 不行么?

貌似是 pacman -Swdd

但我shell不太会写argparse

我打算zsh试试。。。

发现了一个小问题

list all artifacts 这个API得通过翻页才能列出所有的 artifacts

目前这个API在以下几处用到了:

  1. 监控某任务是否已经完成(即pkgname.uuid文件有没有出现)
    这个只需要看最新的artifacts应该就行了,比如前100个吧
    这个只在本地运行

  2. 从所有artifact里找一个最新的某包
    这个只在workflow run里运行
    这个很要命,所有包都查一遍肯定是不好的

我想了以下解决办法:

  1. 首先不需要从所有artifact里找,从2天内的artifact里找肯定没问题了
  2. 在所有workflow runs之间传递一个artifacts list文件
    每个workflow run启动开始,
    先从所有artifact里找一个最新的artifacts list,除了首次运行之外应该不会翻很多页了
    维护artifacts list到最新,且把2天外的老artifacts从list中去掉
    最后上传

但感觉传文件这里文件重复度有点高,
但不这么干的话,如果都传到同一个地方(比如repo)就有一些并发问题要解决
有啥好的解决办法么。。。

list all artifacts 这个API得通过翻页才能列出所有的 artifacts

你才发现啊……翻页我那个分支里已经做了处理了。

你会有很多页么?不多的话应该没太大关系,如果太多就把太旧的删掉?

应该会有很多吧

一个包传4个文件,假设每天100个包,60天,100个每页,总计240页。

呃,那看来得用 v4 了……

不过可以限制到只找2天内的。8页吧。

虽然我觉得还是有点多

100个包都找8页也800次请求了,并且可能会比100个包多。。。
因为绝大多数都能搬上去

我想了一下其实貌似可以不冲突?
如果每个workflow run最后都只提交自己这个run里的artifacts的话
肯定不会冲突

感觉可以

while true
do
  git push && break || git pull --rebase
done

有个问题,这个repo可不可以和存lilac.py的repo是一个?就是和lilac.py存一块
感觉上是同一个的话比较符合认识?
打开目录的同时就能查到文件下载链接,上次运行的workflow_id什么的

就是多存一个artifacts.json和(可选)workflow_id

action-extra-x86_64-build第二版 能正常工作了

现在 arch4edu 的 ncurses5-compat-libs 和 vim-youcompleteme-git 就是action-extra-x86_64-build 打包的了,调用的是petronny/arch4edu这个马甲的action,产生的commit见下

https://github.com/petronny/arch4edu/actions/runs/134418854
arch4edu/arch4edu@91475c3

https://github.com/petronny/arch4edu/actions/runs/134422382
arch4edu/arch4edu@b2c8fce

后续对接也正常。

就是多存一个artifacts.json和(可选)workflow_id

我目前就是把这俩都存了。我想了一下还是存repo里最好。
因为会有多个马甲repo来打包,要跨repo维护这个。
多个马甲比如:

  • action-runner-x86_64
    这个就是调用github的runner
  • action-runner-x86_64-powerful
    这个是调用self-hosted runner,比如archlinuxcn-build-e5
    有几个self-hosted runner就是几并发
    可以不用docker,直接在原生环境运行lilac.py,cache什么的能省很多事。
    缺点就是lilac.py的内容需要人工确认安全
  • action-runner-{armv6h,armv7h,aarch64}
    这几个可以先找一个树莓派4装aarch64的系统,接通到一个国际网络超级好的地方
    然后通过docker实现armv6h和armv7h
  • action-runner-{armv6h,armv7h,aarch64}-qemu
    通过github的runner再qemu模拟的。。。估计慢的要死,但能增加并发

(我们只能通过不同repo来调用不同的runner)

目前我是打算接着改造 action_tools,现在存了artifacts.json了,download_repo_depends那要改改了

就是多存一个artifacts.json和(可选)workflow_id

这里有个需求。。。就是能不能把lilac的最后一push改成每个都push
或者至少通过action打包的成功后push一下,否则这俩文件没法同步。。。

那。。。实在不行就另开个仓库存吧。。。

也好,这样lilac改动更少了

有一个需求。。。

我打算在lilac本地调用时,把pre_build post_build都跳过。
(这个可以通过build_prefix是不是action开头,或者一个叫什么use_action的变量控制)
action中这两个会执行,最后都会反馈到最终的patch里,所以本地没有运行的必要

然而,在 https://github.com/archlinuxcn/lilac/blob/master/lilac#L128
这里要求_G.pkgver_G.pkgrel必须有值。
能不能改成直接从PKGBUILD中就可以读pkgver和pkgrel

另外这里我觉得有点奇怪,直接从PKGBUILD中就可以读了啊,为什么要用_G中的呢

都跳过了,那你还用 lilac 干嘛呢。

用 _G 是因为,已经读到了,干嘛再读一次呢……

得用啊,不是计划的lilac来定哪些要打包及打包顺序调度,签名等等。。。

那。。。我还是判断一下吧。。。
就是post build遇action不跳过,override成一个固定的post_build,只负责读PKGBUILD写_G

哦那个。计划是打包部分在 Actions 上运行,lilac 这边自然就不执行相应的代码了呀。