tech.guitarrapc.cóm

Technical updates

scoop の bucket を自動更新する

Scoop Bucketを作るのは避けたいものです。 しかし、MainやExtrasに追加するのもアレだし、既存のパッケージ更新はメンテされてないし、など様々な理由で自分のバケットを作ることはあるでしょう。

自分のバケットを持った時に面倒なのが、バケットの更新追従です。 幸いにもScoopにはExcavateという自動更新する仕組みがあります。今回はこれを仕込んでみましょう。

TL;DR

  • Bucketのmanifestを記述したjsonにcheckverautoupdateセクションを設定する
  • Excavateと更新スクリプトをバケットに配置する
    • バケットフォルダで、bukcet/などフォルダを掘っている場合はスクリプトで対応が必要
  • Excavateはdocker-composeで動くので、動かしっぱなしにするか適当などこかで定期実行

実際に使用しているリポジトリ

この記事で使っている自動更新のためのスクリプトや仕組みは .githubフォルダに一式が入っています。

github.com

動作環境

バケットの自動更新は以下の環境で動作します。

  • Linuxコンテナ
  • PowerShell 7
  • 定期実行環境 (ローカルマシン、GitHub Actions、Kubernetesなど)

Linuxコンテナで動作するので、実行環境に困らないでしょう。Windowsでも実行できますが、私はLinuxのみで行っています。

Scoop App Manifest 自動更新の仕組み

3つやることがあります。

  1. 自動更新するバケットjsonを自動更新に対応させる
  2. 自動更新スクリプトをバケットのリポジトリにおく
  3. 自動更新を定期実行する

順番に見ていきましょう。

1. 自動更新するバケットjsonを自動更新に対応させる

Scoop App Manifestは、checkverautoupdateに適切な内容を記述することで自動更新対象のマニフェストとして認識されます。この辺りは公式のWikiに書いてあります。

App Manifest Autoupdate · ScoopInstaller/Scoop Wiki · GitHub

といってもWikiの内容は複雑でよくわからん.... ということで実際に使っているシンプルなパターンで説明します。

GitHub のリリースページを見る

今回のパターンはGitHub Releasesのパッケージ更新を見てバケットを構成している場合に利用できます。ただし、プレリリースではない && バージョン1.0.0やv1.0.0といったパターンでタグ公開されているの2点が前提です。

実際に利用しているctopを定義したマニフェストを用意しました。

{
    "description": "Top-like interface for container metrics",
    "homepage": "https://github.com/bcicen/ctop",
    "version": "0.7.5",
    "license": "MIT",
    "url": "https://github.com/bcicen/ctop/releases/download/v0.7.5/ctop-0.7.5-windows-amd64#/ctop.exe",
    "hash": "sha256:bffb1499d62c46b70dd25d557b653f812ccdc8b4bfb08473c063a6265faf78b3",
    "bin": "ctop.exe",
    "checkver": "github",
    "autoupdate": {
        "url": "https://github.com/bcicen/ctop/releases/download/v$version/ctop-$version-windows-amd64#/ctop.exe"
    }
}

自動バージョン更新に必要なのは、checkverとautoupdateのセクションです。

  • checkver"github"で問題ありません。これでリリースぺージのTagが\/releases\/tag\/(?:v|V)?([\d.]+)パターンで公開されていれば抽出される
  • autoupdateのセクションは、$versionを使って実際のリリースページのダウンロードurlを表現するurlがhttps://github.com/bcicen/ctop/releases/download/v0.7.5/ctop-0.7.5-windows-amd64なので、0.7.5というバージョンが $versionに置き換えられていることがわかる

これでバケットのjsonは準備が整いました。

2. 自動更新スクリプトをバケットのリポジトリにおく

リポジトリ直下にバケットのjsonを配置しているか、bucketフォルダを掘ってそこにまとめているかで微妙に異なります。

bucketフォルダを掘っている場合は、lukesampson/scoop-extrasのbinフォルダの中身を、そのまま自分のリポジトリの/binに持ってくるといいでしょう。

lukesampson/scoop-extras の bin フォルダの中身

もしもリポジトリ直下にバケットjsonを置いている場合は、以下のように$dir = "$psscriptroot/.."とします。各スクリプトにこの変更がいるので面倒です。

# auto-pr.ps1
param(
    # overwrite upstream param
    [String]$upstream = "<user>/<repo>:master"
)
if(!$env:SCOOP_HOME) { $env:SCOOP_HOME = resolve-path (split-path (split-path (scoop which scoop))) }
$autopr = "$env:SCOOP_HOME/bin/auto-pr.ps1"
# $dir = "$psscriptroot/../bucket" # checks the parent dir
$dir = "$psscriptroot/.." # checks the parent dir
iex -command "$autopr -dir $dir -upstream $upstream $($args |% { "$_ " })"

これでバケットのスクリプト配置も完了です。

NOTE: 調べるとこのスクリプト名がbucket-updater.ps1 だったり auto-pr.ps1 だったりしますが、後述のExcavatorを使う場合どちらでも対応されているので大丈夫です。auto-pr.ps1 が公式でも使ってて新しいかな?

3. 自動更新を定期実行する

自動更新方法はWikiにもいくつかのっています。

App Manifest Autoupdate · ScoopInstaller/Scoop Wiki · GitHub

個人的には、ScoopInstaller/Excavatorを用いてdocker-composeで実行するのがおすすめです。Linuxで実行できるのと、docker-composeが利用できるので実行環境を選びません。あとこの方法ならKubernetesで実行も簡単です。

github.com

docker-composeを実行するとsshキーが生成されるので、公開鍵id_rsa.pubをGitHubに登録してpushできるようにしましょう。sshキーを再利用する場合は、id_rsaも取得しておいて、docker-compose実行時にマウントさせればokです。

$ docker-compose up --build
$ docker-compose exec bucket cat /root/.ssh/id_rsa.pub
$ docker-compose exec bucket cat /root/.ssh/id_rsa

このコンテナイメージは定期的に呼び出して実行するには注意があります。/sbin/my_initを実行した上でないとscoopコマンドがコンテナ内部で見つからずエラーが出ます。

走らせっぱなしで定期実行する前提なイメージになっているので、定期的に呼び出し実行する場合はdocker-composeで次のようなラッパーコマンドを用意して呼び出すといいでしょう。

#!/bin/bash
/sbin/my_init &
chmod 600 /root/.ssh/id_rsa
pwsh ./root/scoop/lib/install.ps1
bash -x /root/excavate.sh

これでコンテナを使っていい感じに更新されます。

実行環境について

このような定期実行環境は、Kubernetesのcron job、後今日使えるようになったAmazon ECSのdocker-compose対応などが楽でしょう。

www.docker.com

GitHub Actionsでの使用は、使用条件のここに該当するかによりそうです。

any other activity unrelated to the production, testing, deployment, or publication of the software project associated with the repository where GitHub Actions are used.

バケットのデプロイ作業なので実行は許されそうですが、自前のクラスタで実行できるならそれがいいでしょう。 仮にリポジトリで実行していますが、自前Kubernetesクラスタでの実行に変更を予定しています。

以下にGitHub Actionsで実行する場合のWorkflowを示します。

name: auto update bucket

on:
  schedule:
    - cron: "0 * * * *"
  workflow_dispatch:

jobs:
  update:
    timeout-minutes: 10
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - run: openssl aes-256-cbc --pbkdf2 -d -in .github/.ssh/id_rsa.cipher -k ${{ secrets.CIPHER_KEY }} >> .github/.ssh/id_rsa
      - run: docker-compose up
        working-directory: .github

パッケージの更新ログの例です。

main_1  |
main_1  | ScriptBlock ID: f43b73d1-2181-437c-9d72-8b0797e62035
main_1  | Path: /root/scoop/lib/install.ps1
main_1  | py: 1.0.1.6
main_1  | DejaVuSansMono-Bront: couldn't match '\/releases\/tag\/(?:v|V)?([\d.]+)' in https://github.com/chrismwendt/bront/releases/latest
main_1  | bombardier: 1.2.5 (scoop version is 1.2.4) autoupdate available
main_1  | Autoupdating bombardier
main_1  | Downloading bombardier-windows-386.exe to compute hashes!
main_1  | Downloading https://github.com/codesenberg/bombardier/releases/download/v1.2.5/bombardier-windows-386.exe#/bombardier.exe (9.7 MB)...
main_1  | Computed hash: d2bfd99019ca590610c89ccf829bf4c84ed6652821fe55e7f9bcf342106fe496
main_1  | Downloading bombardier-windows-amd64.exe to compute hashes!
main_1  | Downloading https://github.com/codesenberg/bombardier/releases/download/v1.2.5/bombardier-windows-amd64.exe#/bombardier.exe (10.9 MB)...
main_1  | Computed hash: 52e0c22e3a10c06f7013bbb09d713ab52fae6fba5a86a5ffcfdd6546ed86b7cf
main_1  | Nov 22 12:17:39 3e2cb27a65fb powershell[86]: (7.0.2:9:80) [ScriptBlock_Compile_Detail:ExecuteCommand.Create.Warning] Creating Scriptblock text (1 of 1):
main_1  | {⏎⏎            $architecture = $_.Name⏎⏎            if ($json.architecture.$architecture.$prop -and $json.autoupdate.architecture.$architecture.$prop) {⏎⏎                $json.architecture.$architecture.$prop = substitute (arch_specific $prop $json.autoupdate $architecture) $substitutions⏎⏎            }⏎⏎        }
main_1  |
main_1  | ScriptBlock ID: a1ca077d-4175-458e-9ad3-e0f9e4c68c86
main_1  | Path: /root/scoop/lib/autoupdate.ps1
main_1  | Writing updated bombardier manifest
main_1  | ctop: 0.7.5
main_1  | warning: LF will be replaced by CRLF in bucket/bombardier.json.
main_1  | The file will have its original line endings in your working directory.
main_1  | Creating update bombardier (1.2.5) ...
main_1  | hub add bucket/bombardier.json
main_1  | warning: LF will be replaced by CRLF in bucket/bombardier.json.
main_1  | The file will have its original line endings in your working directory.
main_1  | hub status --porcelain -uno
main_1  | hub commit -m 'bombardier: Update to version 1.2.5'
main_1  | [master 603a742] bombardier: Update to version 1.2.5
main_1  |  1 file changed, 5 insertions(+), 5 deletions(-)
main_1  | Pushing updates ...
main_1  | hub push origin master
main_1  | Warning: Permanently added the RSA host key for IP address '140.82.112.4' to the list of known hosts.
main_1  | To github.com:guitarrapc/scoop-bucket.git
main_1  |    c5e2c3f..603a742  master -> master
main_1  | hub reset --hard
main_1  | HEAD is now at 603a742 bombardier: Update to version 1.2.5
main_1  | + '[' -f /root/bucket/bin/bucket-updater.ps1 ']'
github_main_1 exited with code 0

まとめ

難しい記述をしなくても自動更新できるのはいいことです。 実行環境だけ面倒ですね。

参考

手の入れ方が好みじゃないので参考程度にしています。 こういう公式のやり方に乗っかる場合は、コンテナに手を入れないのがおすすめです。メンテしきれなくなるので。

Scoopを使ったWindows環境構築のススメ - Hyper!!! - Qiita