Open25

Github Actions手習い

ktz_aliasktz_alias
ERROR: invalid tag "/ritalin/duckdb-extract-placeholder:main": invalid reference format
Error: buildx failed with: ERROR: invalid tag "/ritalin/duckdb-extract-placeholder:main": invalid reference format

ってなった。前途多難。

ktz_aliasktz_alias

dockerくん使わなくなっててサービス止めてたわ。うっかりうっかり

Error: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

docker起動し直して、ローカルレポジトリのルートでact叩いたら少し進展した。
なんかエラーが出た。

##[error]Parameter token or opts.auth is required

Github Actionが自動で引き渡すトークンを指定しなきゃらしい

引数で渡せるとおあるのでこれを試してみる

https://earthly.dev/blog/using-github-actions-locally/

ktz_aliasktz_alias

そのまえに、gh持ってなかったから、こいつの初期設定から。

$ gh issue list
To get started with GitHub CLI, please run:  gh auth login

まずログインね

gh auth login

対話モードになるので指示に従って進める。

Fine-grained personal access tokens使うようにしたらgit pushできなくなったお

ChatGPT先生曰く、今までは自動切り替えでうまくやってくれてたのかもって

トークンの自動切り替え(Token auto-rotation):
GitHubはセキュリティを強化するため、古い認証トークン(classic personal access token)を自動的に新しいタイプのトークン(Fine-grained personal access token)に切り替える仕組みを導入している場合があります。これにより、以前は classic personal access token を使っていた場合でも、GitHubが自動的に新しいタイプのトークンを使うように設定されている可能性があります。

APIキー取得のためだけに一切の権限なしでFine-grained personal access tokens作ったもんだから、自動切り替えが働かなくなったかもって。

とりあえず、Fine-grained personal access tokensRepository permissionsの設定が、

Read access to metadata
Read and Write access to code, commit statuses, pull requests, and workflows

となるようスコープを付与したら、pushはできた。
有効期限がどう影響するかは様子見で。

ktz_aliasktz_alias

再開

act -s GITHUB_TOKEN="$(gh auth token)"

予定通り(?)のエラーが表示された。

しょうもないミスだった。

images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}と指定しているが、REGISTRYを設定してなかった。
そりゃだめだ。

ktz_aliasktz_alias

alpineのapkが失敗する・・・

RUN echo "@edge-community https://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories
RUN apk add --no-cache zig@edge-community~=0.13.0
ktz_aliasktz_alias

結局、Dockerfileに書くのやめて、Actionsymlにベタ書きしたら通った。
しょうもない記述ミスで難儀はしたが・・・

  • sudo apt updateしてなかったとか
ktz_aliasktz_alias

落ち穂拾い

異なるプラットホーム向けのビルド

jobs:
  build:
    name: Build on ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        node-version: [14.x, 16.x]
    # (snip)

のように、matrixとして記述する。(CharGPT先生より)

タスクのサブコマンドもマトリックスにしたい

追記すれば良い

# (snip)
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        node-version: [14.x, 16.x]
+        command: [test, build]
# (snip)
  step:
     - name: Run npm command
        run: npm run ${{ matrix.command }}

異なるプラットホームで異なるパッケージマネージャーを使いたい

プラットホームをマトリックスとして記述した上で、各stepディレクティブ、ifでレクティブを使って書き分ける。

# (snip)
    step:
      - name: Install dependencies on Ubuntu
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y some-linux-package

      - name: Install dependencies on macOS
        if: runner.os == 'macOS'
        run: |
          brew update
          brew install some-macos-package

      - name: Install dependencies on Windows
        if: runner.os == 'Windows'
        run: |
          choco install some-windows-package
# (snip)

ビルドジョブの最後でアップロード

      # OSごとにアーティファクトをアップロード
      - name: Upload artifacts
        uses: actions/upload-artifact@v2
        with:
          name: ${{ matrix.os }}-build-artifacts-${{ matrix.node-version }}
          path: ./dist/${{ runner.os }}/ # OSごとに適切なパスを指定

リリースジョブで各アーティファクトをダウンロード

release:
    name: Create Release
    runs-on: ubuntu-latest
    needs: build

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      # OSごとにアーティファクトをダウンロード
      - name: Download artifacts
        uses: actions/download-artifact@v2
        with:
          name: ubuntu-latest-build-artifacts-14.x # ビルドしたプラットホームとバージョン
          path: ./dist/ubuntu-latest/ # ダウンロード先のパスを指定
      # (snip)

ダウンロードしたアーティファクト使ってリリース

      # ダウンロードしたアーティファクトを使用してリリースを作成する手順を追加
      - name: Create GitHub Release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          draft: false
          prerelease: false
ktz_aliasktz_alias

aptでインストールしたパッケージの確認

  • パス - dpkg -L <PACKAGE>
  • 利用可能バージョン - apt-cache showpkg <PACKAGE>
ktz_aliasktz_alias

wgetで持ってきて、unzipして配置してた時の名残

 - name: Install libduckdb
    run: |
        mkdir /tmp/duckdb
        sudo mkdir -p /usr/local/duckdb/lib
        sudo mkdir -p /usr/local/duckdb/include
        wget -nv -O /tmp/duckdk.zip https://github.com/duckdb/duckdb/releases/download/v1.0.0/libduckdb-linux-amd64.zip
        unzip /tmp/duckdk.zip -d /tmp/duckdb
        echo "check duckdb include (from)"
        ls -ld /tmp/duckdb/*
        sudo mv /tmp/duckdb/lib* /usr/local/duckdb/lib/
        sudo mv /tmp/duckdb/*.h /tmp/duckdb/*/*.hpp /usr/local/duckdb/include/
ktz_aliasktz_alias

nektos /actでローカルでビルドしたdockerイメージをruns-onとして使用する

actのCLI引数として、-P <NAME>=<IMAGE>を指定する
ここで

  • NAME - run-onに指定する名称。例えばubuntu-latest
  • IMAGE - docker buildで指定したイメージ名

ただし-Pオプションはrun-onにしか作用しない。
Actionsubuntu固定のため、現実問題を解決しない。

ビルドしたイメージの使用を強制する(dockerhubからpullしない)のならpull=falseも併せて指定も有効。

  • pull=falseオプションはcontainerセクションにも反映される。
ktz_aliasktz_alias

dockerのマルチステージビルドの勘所

  • 1st stage: パッケージマネージャによる依存パッケージの構成
  • 2st stage: build構成(confihure)とbuildの実行(cmake --build)
  • 3rd stage: build成果物のインストール(cmake --install)
  • last stage: 3rd stageの成果物から新しいイメージを作る
ktz_aliasktz_alias

dockerの特定のステージのキャッシュを無効化

任意のステージで

  • ARG CACHE_BUST=1のようなダミーの引数を割り当てる
  • docker --build-arg CACHE_BUST=$(date +%s) ...のように実行するたびに値を指定する

毎度引数の値が変更されることでキャッシュを無効化したのと同様の効果を得られる。

ktz_aliasktz_alias

docker buildで実行中のログを出す

docker build --progress=plain ...

ktz_aliasktz_alias

nektos/act 下でcontainerセクションのシミュレーションでエラー

mkdir /host_mnt/Users/xxxx/.docker/run/docker.sock: operation not supportedのようなエラーが出た場合の対処

  1. docker context lsでソケットを確認する
  2. desktop-linux *endpointからunix://を除いたパスで、/var/run/docker.sockのシンボリックリンクを作る
sudo ln -s /var/run/docker.sock <desktop-linux *のendpoint>

see aldo: https://outatime.dev/notes/using-act-to-run-github-actions-locally/

ktz_aliasktz_alias

クロスプラットホームのビルド

単純なケースではmatrixを使うのが楽

プラットホーム間で依存パッケージのABIが異なる場合、matrixでは面倒。
素直にプラットホームごとにジョブを分けたほうがいい。

分け方は

  • 1つの構成ファイルに両方のジョブを定義する
  • プラットホームごとに構成ファイルを用意する

いずれの場合も独立に別プロセスで並行してビルドが実行される。

ktz_aliasktz_alias

dockerのマルチステージビルドのキャッシュされたステージに入りたい

そのままでは無理

docker build --target <ステージ名> -t <イメージ名> .

とすると、キャッシュしたステージをエクスポートでくる。
あとはコンテナ起動して入るだけ。

docker run -it <イメージ名> /bin/bash
ktz_aliasktz_alias

nektos/actでcontainerセクションを使用する

環境変数はcontainer.envにまとめておくとスッキリする。

起動は以下

 act -P ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-latest -s GITHUB_TOKEN="$(gh auth token)"
ktz_aliasktz_alias

マルチステージビルドで前ステージでインストールしたパッケージを次ステージにコピーする

homebrew(linuxbrew)はインストールしたパッケージを/<PREFIX>/Celler/<PACKAGE>/<VERSION>にインストールし、/<PREFIX>/opt/<PACKAGE>にシンボリックリンクを作成する。

次ステージで

COPY from=prev_stage /<PREFIX>/opt/<PACKAGE>/. /path/to/<PACKAGE>

とできれば、バージョンを意識する必要がなくなるため楽できるのだが、
シンボリックリンクはシンボリックリンクのままコピーしてしまうため、このままでは想定通りに動かない。

回避策として、前ステージでrsyncを使用して/<PREFIX>/opt/<PACKAGE>のシンボリックリンクを解決してしまう方法が挙げられる。
どうせ前ステージで捨ててしまうので上書きてしまっても、さして影響はないという考え。

# 一旦シンボリックリンクを解決しつつtmpにコピー
RUN rsync -aL /<PREFIX>/opt/<PACKAGE> /tmp/
# 書き戻す
RUN rsync -a --delete /tmp<PACKAGE> /<PREFIX>/opt

コピーする際は、依存パッケージも同様の作業を行うこと(ビルドの際にリンクエラーになる)

ktz_aliasktz_alias

タグからバージョンを抽出

例えばv1.2.3というタグを付与したとする。
ここから1.2.3を抽出したいというケース。

name: Extract version
id: extract_version
run: |
    VERSION = ${GITHUB_REF#refs/tags/v}
    echo "version=$VERSION" >> $GITHUB_OUTPUT

ここで、${GITHUB_REF#refs/tags/v}はシェルスクリプトの機能で、#以降の文字列を除去して返す物。
GITHUT_OUTPUT環境変数に割り当てられたファイルパスにリダイレクトすると、後続のステップで

${{ steps.extract_version.outputs.version }}

で取得できる。

ジョブ間で環境変数を引き継ぐ

ただし$GITHUB_OUTPUTはジョブごとにリセットされるため、この方法でだけでは別ジョブに引き継げない。
回避策として、

  1. 環境変数を別ファイルにリダイレクト
  2. actions/upload-artifactでアップロード
  3. 別ジョブで、actions/download-artifactで展開
  4. sourceコマンドでファイルを読み、環境変数としてロード
  5. $GITHUB_OUTPUTにリダイレクト

これで、${{ steps.extract_version.outputs.version }}を介して値が読める

#1
echo "version=$VERSION" | tee other-info.txt $GITHUB_OUTPUT > /dev/null
#2
uses: actions/upload-artifact
name:  other-info
path: other-info.txt
#3
uses: actions/download-artifact
name:  other-info
path: other-info.txt
#4
source other-info/other-info.txt
#5
echo "version=$version" >> $GITHUB_OUTPUT
ktz_aliasktz_alias

nektos/actで特定ワークフローの特定ジョブを実行する

  • ワークフローの指定
    • -W <PATH>で指定する
    • ヘルプにはpath to workflow file(s) (default "./.github/workflows/")とあり、一見ルートフォルダを指定するオプションのようにみえるが、ワークフローファイルのパスも指定できる模様
  • ジョブの指定
    • -j <JOB>で指定する

例:

act -W foo.yaml -j build ...
ktz_aliasktz_alias

nektos/actでリリース成果物生成のシミュレーション

リリース成果物を作成する際、actions/upload-artifactactions/download-artifactを使うことになるが、保存先がクラウドのストレージに向かっているためそのままではシミュレーションできない。

その際、--artifact-server-path <PATH>オプションを指定することで、保存先を変更できる

参照:
https://qiita.com/nagayaoh/items/d5cff83929452b790164

ktz_aliasktz_alias

nektos/actでリリースタグを指定したシミュレーション

nektos/actはカレンとブランチでシミュレーションを走らせるため、$GITHUB_REFにはrefs/heads/mainのようになる。

これではタグからバージョンを抽出するようなことができなくなる。
回避策として、nektos/actenvオプションで強制的に差し替える

act --env GITHUB_REF=refs/tags/v1.2.3 ...
ktz_aliasktz_alias

nektos/actでのactions/upload-artifact@v4に関するバグ

ctions/upload-artifact@v4でアップロードするファイルサイズが8MBを超えるとチェックサムを正しく計算できなくなるバグが存在する。
v0.2.64で存在を確認。

https://github.com/nektos/act/issues/2351

現在は修正されており、v0.2.69では問題なくアップロードできることを確認している。