当前位置: 首页 > 知识库问答 >
问题:

在GitHub Actions工作流中缓存APT包

顾单弓
2023-03-14

我对我的C项目使用以下Github操作工作流。工作流大约在40秒内完成,但其中一半以上的时间用于安装valgrind软件包及其依赖项。

我相信缓存可以帮助我加快工作流程。我不介意多等几秒钟,但这似乎是对GitHub资源的毫无意义的浪费。

name: C Workflow

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1

    - name: make
      run: make

    - name: valgrind
      run: |
        sudo apt-get install -y valgrind
        valgrind -v --leak-check=full --show-leak-kinds=all ./bin

运行sudo apt get install-y valgrind安装以下软件包:

  • gdb
  • gdbserver
  • libbabeltrace1
  • libc6-dbg
  • libipt1
  • valgrind

我知道Actions支持缓存特定的目录(已经有一些回答了SO问题和文章),但是我不确定apt安装的所有不同的包最终会在哪里。我假设/bin//usr/bin/不是受安装包影响的唯一目录。

是否有一种优雅的方法来缓存已安装的系统包,以便将来运行工作流?

共有3个答案

宇文航
2023-03-14

更新:我创建了一个GitHub操作,作为这个解决方案,代码更少,优化更好。缓存新内容

这个解决方案类似于大多数投票。我尝试了建议的解决方案,但它不适合我,因为我正在安装tex live-latexpandoc,它有许多依赖关系和子依赖关系。

我创建了一个可以帮助很多人的解决方案。一种情况是当你安装了几个包(apt安装),另一种解决方案是当你制作了一个程序,并且需要一段时间。

解决方案:

  • 步骤中包含所有逻辑,它将缓存。
    • 使用find创建容器中所有文件的列表
    • 安装所有软件包或makeprograms,无论您想缓存什么
    • 使用find创建容器中所有文件的列表
    • 使用diff获取新创建的文件
    • 将这些新文件添加到缓存目录。此目录将自动存储操作/cache@v2
    • 将所有文件从缓存目录复制到主路径/

    什么时候用这个?

    • 我没有使用缓存,软件包的安装大约需要2分钟才能完成整个过程
    • 有了缓存,第一次创建它需要7~10分钟。
      • 使用缓存大约需要1分钟才能完成所有过程

      实施:

      >

      我的操作的登录页:工作流。

      释放yml

      name: CI - Release books
      
      on:
        release:
          types: [ released ]
        workflow_dispatch:
      
      jobs:
        build:
          runs-on: ubuntu-18.04
          steps:
            - uses: actions/checkout@v2
      
            - uses: actions/cache@v2
              id: cache-packages
              with:
                path: ${{ runner.temp }}/cache-linux
                key: ${{ runner.os }}-cache-packages-v2.1
      
            - name: Install packages
              if: steps.cache-packages.outputs.cache-hit != 'true'
              env:
                SOURCE: ${{ runner.temp }}/cache-linux
              run: |
                set +xv
                echo "# --------------------------------------------------------"
                echo "# Action environment variables"
                echo "github.workspace: ${{ github.workspace }}"
                echo "runner.workspace: ${{ runner.workspace }}"
                echo "runner.os: ${{ runner.os }}"
                echo "runner.temp: ${{ runner.temp }}"
                echo "# --------------------------------------------------------"
                echo "# Where am I?"
                pwd
                echo "SOURCE: ${SOURCE}"
                ls -lha /
                sudo du -h -d 1 / 2> /dev/null || true
                echo "# --------------------------------------------------------"
                echo "# APT update"
                sudo apt update
                echo "# --------------------------------------------------------"
                echo "# Set up snapshot"
                mkdir -p "${{ runner.temp }}"/snapshots/
                echo "# --------------------------------------------------------"
                echo "# Install tools"
                sudo rm -f /var/lib/apt/lists/lock
                #sudo apt install -y vim bash-completion
                echo "# --------------------------------------------------------"
                echo "# Take first snapshot"
                sudo find / \
                      -type f,l \
                      -not \( -path "/sys*" -prune \) \
                      -not \( -path "/proc*" -prune \) \
                      -not \( -path "/mnt*" -prune \) \
                      -not \( -path "/dev*" -prune \) \
                      -not \( -path "/run*" -prune \) \
                      -not \( -path "/etc/mtab*" -prune \) \
                      -not \( -path "/var/cache/apt/archives*" -prune \) \
                      -not \( -path "/tmp*" -prune \) \
                      -not \( -path "/var/tmp*" -prune \) \
                      -not \( -path "/var/backups*" \) \
                      -not \( -path "/boot*" -prune \) \
                      -not \( -path "/vmlinuz*" -prune \) \
                      > "${{ runner.temp }}"/snapshots/snapshot_01.txt 2> /dev/null \
                      || true
                echo "# --------------------------------------------------------"
                echo "# Install pandoc and dependencies"
                sudo apt install -y texlive-latex-extra wget
                wget -q https://github.com/jgm/pandoc/releases/download/2.11.2/pandoc-2.11.2-1-amd64.deb
                sudo dpkg -i pandoc-2.11.2-1-amd64.deb
                rm -f pandoc-2.11.2-1-amd64.deb
                echo "# --------------------------------------------------------"
                echo "# Take second snapshot"
                sudo find / \
                      -type f,l \
                      -not \( -path "/sys*" -prune \) \
                      -not \( -path "/proc*" -prune \) \
                      -not \( -path "/mnt*" -prune \) \
                      -not \( -path "/dev*" -prune \) \
                      -not \( -path "/run*" -prune \) \
                      -not \( -path "/etc/mtab*" -prune \) \
                      -not \( -path "/var/cache/apt/archives*" -prune \) \
                      -not \( -path "/tmp*" -prune \) \
                      -not \( -path "/var/tmp*" -prune \) \
                      -not \( -path "/var/backups*" \) \
                      -not \( -path "/boot*" -prune \) \
                      -not \( -path "/vmlinuz*" -prune \) \
                      > "${{ runner.temp }}"/snapshots/snapshot_02.txt 2> /dev/null \
                      || true
                echo "# --------------------------------------------------------"
                echo "# Filter new files"
                diff -C 1 \
                    --color=always \
                    "${{ runner.temp }}"/snapshots/snapshot_01.txt \
                    "${{ runner.temp }}"/snapshots/snapshot_02.txt \
                    | grep -E "^\+" \
                    | sed -E s/..// \
                    > "${{ runner.temp }}"/snapshots/snapshot_new_files.txt
                < "${{ runner.temp }}"/snapshots/snapshot_new_files.txt wc -l
                ls -lha "${{ runner.temp }}"/snapshots/
                echo "# --------------------------------------------------------"
                echo "# Make cache directory"
                rm -fR "${SOURCE}"
                mkdir -p "${SOURCE}"
                while IFS= read -r LINE
                do
                  sudo cp -a --parent "${LINE}" "${SOURCE}"
                done < "${{ runner.temp }}"/snapshots/snapshot_new_files.txt
                ls -lha "${SOURCE}"
                echo ""
                sudo du -sh "${SOURCE}" || true
                echo "# --------------------------------------------------------"
      
            - name: Copy cached packages
              if: steps.cache-packages.outputs.cache-hit == 'true'
              env:
                SOURCE: ${{ runner.temp }}/cache-linux
              run: |
                echo "# --------------------------------------------------------"
                echo "# Using Cached packages"
                ls -lha "${SOURCE}"
                sudo cp --force --recursive "${SOURCE}"/. /
                echo "# --------------------------------------------------------"
      
            - name: Generate release files and commit in GitHub
              run: |
                echo "# --------------------------------------------------------"
                echo "# Generating release files"
                git fetch --all
                git pull --rebase origin main
                git checkout main
                cd ./src/programming-from-the-ground-up
                ./make.sh
                cd ../../
                ls -lha release/
                git config --global user.name 'Israel Roldan'
                git config --global user.email 'israel.alberto.rv@gmail.com'
                git add .
                git status
                git commit -m "Automated Release."
                git push
                git status
                echo "# --------------------------------------------------------"
      

      解释部分代码:

      这里是操作缓存,指示一个,它将生成一次,并在以后的执行中进行比较。路径是生成缓存压缩文件的文件所在的目录。

            - uses: actions/cache@v2
              id: cache-packages
              with:
                path: ${{ runner.temp }}/cache-linux
                key: ${{ runner.os }}-cache-packages-v2.1
      

      如果退出缓存命中则对缓存的有条件搜索为“true”。

      if: steps.cache-packages.outputs.cache-hit != 'true'
      if: steps.cache-packages.outputs.cache-hit == 'true'
      

      这并不重要,但是当第一次执行du命令时,Linux索引所有的文件(5~8分钟),然后当我们使用fine时,只需要大约50秒就可以得到所有的文件文件。如果你愿意,你可以删除这一行。

      带后缀的命令| | true防止出现2

      sudo du -h -d 1 / 2> /dev/null || true
      

      这是神奇的部分,使用查找生成实际文件的列表,排除一些目录来优化缓存文件夹。它也将在安装和make程序后执行。在下一个快照中,文件名应该是不同的snapshot_02.txt

      sudo find / \
            -type f,l \
            -not \( -path "/sys*" -prune \) \
            -not \( -path "/proc*" -prune \) \
            -not \( -path "/mnt*" -prune \) \
            -not \( -path "/dev*" -prune \) \
            -not \( -path "/run*" -prune \) \
            -not \( -path "/etc/mtab*" -prune \) \
            -not \( -path "/var/cache/apt/archives*" -prune \) \
            -not \( -path "/tmp*" -prune \) \
            -not \( -path "/var/tmp*" -prune \) \
            -not \( -path "/var/backups*" \) \
            -not \( -path "/boot*" -prune \) \
            -not \( -path "/vmlinuz*" -prune \) \
            > "${{ runner.temp }}"/snapshots/snapshot_01.txt 2> /dev/null \
            || true
      

      安装一些软件包并pandoc

      sudo apt install -y texlive-latex-extra wget
      wget -q https://github.com/jgm/pandoc/releases/download/2.11.2/pandoc-2.11.2-1-amd64.deb
      sudo dpkg -i pandoc-2.11.2-1-amd64.deb
      rm -f pandoc-2.11.2-1-amd64.deb
      

      使用添加的新文件生成文本文件,这些文件也可以是符号文件。

      diff -C 1 \
            "${{ runner.temp }}"/snapshots/snapshot_01.txt \
            "${{ runner.temp }}"/snapshots/snapshot_02.txt \
            | grep -E "^\+" \
            | sed -E s/..// \
            > "${{ runner.temp }}"/snapshots/snapshot_new_files.txt
      

      最后,将所有文件复制到缓存目录中,作为存档保存原始信息。

      while IFS= read -r LINE
      do
        sudo cp -a --parent "${LINE}" "${SOURCE}"
      done < "${{ runner.temp }}"/snapshots/snapshot_new_files.txt
      

      步骤将所有缓存文件复制到主路径/

            - name: Copy cached packages
              if: steps.cache-packages.outputs.cache-hit == 'true'
              env:
                SOURCE: ${{ runner.temp }}/cache-linux
              run: |
                echo "# --------------------------------------------------------"
                echo "# Using Cached packages"
                ls -lha "${SOURCE}"
                sudo cp --force --recursive "${SOURCE}"/. /
                echo "# --------------------------------------------------------"
      

      这一步就是我使用缓存生成的安装包的地方,/制作sh脚本使用pandoc进行一些转换。正如我提到的,您可以创建使用缓存好处的其他步骤,也可以创建不使用缓存的其他步骤。

            - name: Generate release files and commit in GitHub
              run: |
                echo "# --------------------------------------------------------"
                echo "# Generating release files"
                cd ./src/programming-from-the-ground-up
                ./make.sh
      

何建中
2023-03-14

您可以使用预先安装的valgrind创建docker映像,并在该映像上运行您的工作流。

创建一个Dockerfile,内容如下:

FROM ubuntu

RUN apt-get install -y valgrind

构建它并将其推送到dockerhub:

docker build -t natiiix/valgrind .
docker push natiiix/valgrind

然后将以下内容用作您的工作流程:

name: C Workflow

on: [push, pull_request]

jobs:
  build:
    container: natiiix/valgrind

    steps:
    - uses: actions/checkout@v1

    - name: make
      run: make

    - name: valgrind
      run: valgrind -v --leak-check=full --show-leak-kinds=all ./bin

完全未经测试,但你明白了。

慕阳
2023-03-14

这个答案的目的是展示如何使用github操作进行缓存。不一定要展示如何缓存valgrind,它确实展示了这一点,但更多的是展示并非所有东西都可以/应该缓存,缓存和恢复缓存与重新安装依赖关系的权衡需要考虑在内。

您将使用操作/cache操作来执行此操作。

添加它作为一个步骤(在您需要使用valgrind之前):

- name: Cache valgrind
  uses: actions/cache@v2
  id: cache-valgrind
  with:
      path: "~/valgrind"
      key: ${{secrets.VALGRIND_VERSION}}

下一步应尝试安装缓存版本(如果有),或从存储库安装:

- name: Install valgrind
  env:
    CACHE_HIT: ${{steps.cache-valgrind.outputs.cache-hit}}
    VALGRIND_VERSION: ${{secrets.VALGRIND_VERSION}}
  run: |
      if [[ "$CACHE_HIT" == 'true' ]]; then
        sudo cp --verbose --force --recursive ~/valgrind/* /
      else
        sudo apt-get install --yes valgrind="$VALGRIND_VERSION"
        mkdir -p ~/valgrind
        sudo dpkg -L valgrind | while IFS= read -r f; do if test -f $f; then echo $f; fi; done | xargs cp --parents --target-directory ~/valgrind/
      fi

设置VALGRIND_VERSION秘密为输出:

apt-cache policy valgrind | grep -oP '(?<=Candidate:\s)(.+)'

这将允许您在发布新版本时仅通过更改secret的值使缓存无效。

dpkg-L valgrind用于列出使用sudo apt get install valgrind时安装的所有文件。

我们现在可以使用这个命令将所有依赖项复制到缓存文件夹中:

dpkg -L valgrind | while IFS= read -r f; do if test -f $f; then echo $f; fi; done | xargs cp --parents --target-directory ~/valgrind/

除了复制valgrind的所有组件之外,可能还需要复制依赖项(例如在本例中的libc),但是我不建议继续沿着这条路走,因为依赖链只是从那里生长出来的。准确地说,复制最终有一个适合valgrind运行的环境所需的依赖关系如下:

  • libc6
  • libgcc1
  • gcc-8-base

要复制所有这些依赖项,可以使用与上面相同的语法:

for dep in libc6 libgcc1 gcc-8-base; do
    dpkg -L $dep | while IFS= read -r f; do if test -f $f; then echo $f; fi; done | xargs cp --parents --target-directory ~/valgrind/
done

当安装valgrind首先需要简单地运行sudo apt-get安装valgrind时,所有这些工作真的值得麻烦吗?如果您的目标是加快构建过程,那么您还必须考虑恢复(下载和提取)缓存所需的时间,而不是简单地再次运行命令来安装valgrind

最后要恢复缓存,假设它存储在/tmp/valgrind,您可以使用以下命令:

cp --force --recursive /tmp/valgrind/* /

它基本上将所有文件从缓存复制到根分区。

除了上面的过程之外,我还有一个“缓存valgrind”的示例,通过从源代码安装和编译它。现在缓存的大小约为63MB(压缩),仍然需要单独安装libc,这样做有违目的。

注意:这个问题的另一个答案是,通过使用一个依赖于预先安装的依赖项的容器,我认为可以更安全地缓存依赖关系。最好的部分是,您可以使用操作使这些容器保持最新。

参考文献:

  • https://askubuntu.com/a/408785
  • https://unix.stackexchange.com/questions/83593/copy-specific-file-type-keeping-the-folder-structure
 类似资料:
  • 我正在建立一个“类缓存”,我想稍后调用类。 主要的目标是,我不希望每次需要类实例时都扫描上下文。 首先要评估缓存,我将缓存方法@AutoWired放在@RestController中,这会很好地工作。在调用rest方法时填充缓存。 完成之后,我将@AutoWired对象移动到@Service中,创建一个方法来自填充缓存。但这不起作用。调用@PostConstructor方法时不填充缓存。 如何使用

  • 我对docker相当陌生,正试图通过写自己的图像来学习,目前,正在阅读docker in action(ISBN:1633430235) 我的代码: 图书代码: 然而,在这两个代码中,我都得到了一个无法定位的包eror和一个none-zeero代码: apt-get update'返回一个非零代码:100 这将成功地下载,但一旦它开始安装git,我得到同样的错误。这个 3)在https://doc

  • 令人惊讶的是,很难在网上找到关于这个的具体信息。 是否可以将结果缓存在Spring webflow中? 例如:我可以使用rest服务,然后使用mongodb作为主数据库,redis作为缓存吗。所以,当请求通过时,它会检查缓存,如果请求的结果不在缓存中,它会查询mongodb? 如果你有链接等只是把它们作为一个评论,和Il阅读他们并回答自己的问题。 谢谢

  • 我正在用 我正在使用 我已经查看了Picturefill的演示,甚至还有一个与我使用的方式非常接近的示例 有没有其他人遇到过这种情况,或者对如何修复它有什么建议?如果我使用src而不是srcset,这似乎是可行的,但我知道不建议将src与Picturefill一起使用,因为它会在不支持srcset的浏览器中导致双重下载。

  • 我建立了一个进步的网络应用,https://www.tavest.com.我不明白为什么我的服务人员也被缓存在Chrome中?https://www.tavest.com/service-worker-tavest.js因此,当我更改服务人员时,chrome没有检测到更改,因此服务人员没有更新。 尽管我刷新了很多次页面,它仍然是一样的。然而,在Mozilla中,它工作得很好。这是我安装服务人员的代

  • 在我的项目中,我使用了一个@Cacheable注释ia一个服务方法,它返回涉及书籍和一些标记的计算结果,我想在一个@Controller类方法中退出缓存,该方法将一本新书添加到数据库中,因为这本新书将是新计算所必需的。 服务类:@Cacheable("metas") 控制器类:@RequestMapping@CacheEvict(value=“metas”,allEntries=true)

  • 我有一个使用服务人员和缓存API的应用程序。Service Worker中使用的缓存名称包含GIT修订版(哈希),因此每次发布时,都会使用新缓存,删除旧缓存。这是为了百分之百地确保用户总是能够获得最新的源代码(这种情况对于预期的发布计划来说很好)。 我使用的是每天运行几次的Selenium Java(WebDriver)测试(比如GIT repo发生变化时),我希望确保每个新的GIT版本都能正确地

  • 我为我的网站创建了一个AMP页面,在我的桌面浏览器上运行正常,在我的手机上测试正常,如果某些字段为空或无效,提交错误将正确显示错误消息,并且在成功提交时,它将正确显示提交成功消息。 当我将页面提交给Google缓存amp页面时,我再次测试了表单,这次它没有显示错误或成功消息。但如果表单提交有效,它将向我发送电子邮件,但不会显示成功消息。 表单html代码: PHP页面: