5.1さらうどん

@giginetの技術ブログ。ゲーム開発、iOS開発、その他いろいろ

Swift PackageのArtifact Bundleを簡単に生成するGitHub ActionをAIで作った

github-action-artifactbundle

というGitHub Actionを作った。これでAppleの規定するバイナリ配布形式、Artifact Bundleに添ったアーカイブを簡単に作成できる。

Artifact Bundleは、本来、Package Pluginなどの仕組みでのexecutableの配布を想定したフォーマットだが、@mtj0928の開発したexecutable管理ツールnestもArtifact Bundleを想定しているため、この形式で配布しておくとユーザーの利便性が高い。

Artifact Bundleはアーキテクチャやプラットフォームごとのバイナリをzipに詰め、メタデータとしてJSONを添付するだけの簡易なフォーマットだ。一方で、簡単に作る方法がなく配布がとても面倒。このActionを使えば、Swift Packageからビルドしたバイナリを簡単に配布できる。

on:
  release:
    types: [published, edited]
name: Upload Artifact Bundle to Release
env:
  DEVELOPER_DIR: '/Applications/Xcode_16.2.app/Contents/Developer'
jobs:
  release:
    runs-on: macos-15
    steps:
      - uses: actions/checkout@v4
      - name: Build Universal Binary
        run: swift build --disable-sandbox -c release --arch arm64 --arch x86_64
      - uses: giginet/github-action-artifactbundle@v2
        id: artifactbundle
        with:
          artifact_name: myexecutable
      - name: Upload Artifact Bundle to Release
        run: |
          BODY="${{ github.event.release.body }}"
          BUNDLE_PATH="${{ steps.artifactbundle.outputs.bundle_path }}"
          SHA256="${{ steps.artifactbundle.outputs.bundle_sha256 }}"
          TAG_NAME="${{ github.event.release.tag_name }}"
          gh release upload "${TAG_NAME}" "${BUNDLE_PATH}"
          NEW_BODY="$(printf "%s\n%s" "$BODY" "$SHA256")"
          gh release edit "${TAG_NAME}" --notes "${NEW_BODY}"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

ビルドや配布の責務は持たないこととして、予め用意したバイナリを指定のフォーマットに圧縮するだけの設計にした。

Artifact Bundleの仕様上、バイナリであれば特にどの言語で書かれたものかは問わないが、このアクションでは割り切ってSwift Packageからのビルドにしか対応していない*1。Swift Packageのビルドディレクトリ構造から経験的にバイナリを探したり、アーキテクチャを特定している。

SHA256ハッシュの計算もできる。サンプルのようにReleaseトリガーや、ghと併用すれば、リリース作成時に簡単にアーカイブとハッシュ値を公開できる。

Static Linux SDKサポート

Swift 6.0から、macOSのみでLinux向けのSwiftバイナリをビルドできるようになった。このActionはこの仕組みによる成果物にも対応している。

Swift.org - Getting Started with the Static Linux SDK

macOS Runnerさえあれば、macOSとLinux両方に対応したバイナリ配布が容易にできる。これによって、macOSのいらないSwiftで書いたツールをLinuxで動かしたりもできる。

TypeScriptで書いた

GitHub Actionには、Dockerアクション、複合アクション、JavaScriptアクションの3つの形式があり、その気になればどの言語でもActionを実装できる。

とはいえ、今回のケースでは、まずmacOSでの実行を最優先に考えなくてはならない。残念ながらmacOS RunnerではDockerアクションは実行できないので、これで多くのインタプリタ言語が候補から消える。*2

もちろん、Swiftの採用は有力候補だったが、ビルドが必要な言語はビルド時間のオーバーヘッドが気になる。バイナリを配布すれば良いが、バイナリ配布のツールでバイナリ配布を考えなければいけないのは世話がない。

結果的にほぼJavaScriptアクションしか選択肢がなかったので、TypeScriptを選択した。

TypeScriptが書けないのでAIで書いた

TypeScriptはたまに必要があれば書いていて、過去にdenopsを使ったNeovimプラグインを書いたりしていたけど、あまり日常的に使っていないので不慣れ。node全くわからん。

というわけで、今回はほとんどをAIコーディング拡張のClineに任せて書いてみた。というか、どちらかというと因果関係が逆で、AIによるコーディングをちゃんとしてみたかったので、題材としてGitHub Actionを作ってみたというのが正しい。

結果的に、AIの頭の良さに感激した*3。今回のようなかなり尖ったドメインでも十二分にコーディングができることが実証できた。

今回の話はここからが見どころだけど、長くなるので次の記事で。

Artifact Bundleを配布しよう!!!

このツールで世の中のSwift製ツールのArtifact Bundle対応が進めば、全部nestでビルド済みバイナリとして入れられるようになって幸せ。どうぞご利用ください。

*1:action.ymlの仕様が貧弱で複雑な設定項目が難しかった

*2:下流ジョブにしたり、システムインタプリタで実行するなどやりようはあるけど・・・・・・

*3:頭の悪い感想しか出ない