この記事は連載物のひとつになっています。
前回扱った内容が前提になっているため(下に概要は載せていますが)一読しておくことをおすすめします。
守章: Scoopの基礎、簡単なManifestの作成
破章: バージョン管理と自動アップデート ← (今回はこれ)
離章: Scoopを用いた環境構築する際の勘所
今回はKaoriYa版Vimなどを題材に、Scoopでのバージョン管理を担うcheckver属性とautoupdate属性について解説します。
そして今回も環境構築には触れません。公式WikiよりApp Manifest Autoupdateの和訳です。
たぶんいないだろうけどKaoriYa版Vimのかんたん導入だけ知りたい人は前回の記事を読んだ上でこれを使ってください。
おさらい
前回はScoopを使うメリットと利用法を解説しました。PowerShell3.0以上があれば導入も操作も簡単です。
そして下のようなJSONをGitリポジトリに登録し公開することで、誰でも自由に拡張できることも知りました。
{
"##": "実行中にUAC昇格ポップアップが出ます。",
"license": {
"identifier": "Freeware",
"url": "http://technet.microsoft.com/ja-jp/sysinternals/bb469936"
},
"depends": [
"sudo"
],
"version": "2.0",
"url": "http://download.sysinternals.com/files/Ctrl2Cap.zip",
"hash": "2d8c06374da140beda79ac1940ab2b06a56a9af182dba70a6338313d768a2ac2",
"installer": {
"script": [
"sudo $dir\\ctrl2cap.exe /install",
"if ($? == $false) {",
" Write-Host '===================================' -Foreground Red",
" Write-Host 'ctrl2capの実行に失敗しました。' -Foreground Red",
" Write-Host '===================================' -Foreground Red",
"}"
]
},
"bin": "ctrl2cap.exe",
"shortcuts": [
[
"ctrl2cap.exe",
"Ctrl2cap"
]
],
"notes": [
"再設定をしたい場合は、以下の手順で行ってください。",
"- スタートメニューから「Ctrl2cap」で検索、管理者権限で実行",
"- 管理者権限のプロンプトで 'ctrl2cap /uninstall' を実行",
]
}
前回紹介したうち主要な属性の概要です。
属性 | 値 |
---|---|
bin | パスを通したいファイル |
depends | 依存するManifest |
hash | hash値 ※準必須属性 |
installer | インストール処理 |
notes | インストールの全処理完了後に出す文 |
shortcuts | スタートメニューに作るショートカット |
url | ダウンロードするファイル ※必須属性 |
version | バージョン ※必須属性 |
## | コメント |
補足: architecture属性、extract_dir属性
バージョン管理について知る前に、前回紹介できなかったarchitecture属性そしてextract_dir属性について話しておきます。
以下に今回メインの題材となるKaoriYa版Vim(以降 Vim 表記)の簡単なManifestを載せておきます。
{
"homepage": "https://github.com/koron/vim-kaoriya",
"description": "Vim + kaoriya build system, without gvim.",
"version": "8.1.0005.20180520",
"architecture": {
"32bit": {
"url": "https://github.com/koron/vim-kaoriya/releases/download/v8.1.0005-20180520/vim81-kaoriya-win32-8.1.0005-20180520.zip",
"hash": "afacd6e27304136f5ebae3edddf6e747f410880fa6a986d80be772e03ef56d36",
"extract_dir": "vim81-kaoriya-win32"
},
"64bit": {
"url": "https://github.com/koron/vim-kaoriya/releases/download/v8.1.0005-20180520/vim81-kaoriya-win64-8.1.0005-20180520.zip",
"hash": "53e8dd08e2249ce8a54784e16469151a7bf9857ac9acf3a1b341ac4e7da26fb2",
"extract_dir": "vim81-kaoriya-win64"
}
},
"bin": "vim.exe"
}
url/hash/extract_dirと3つの属性がarchitecture属性内の32bitおよび64bit属性に移動しています。
architecture属性を用いることで、このようにアーキテクチャ毎にリソースが異なる場合でも分離することができます。
参考までに、architecture属性によって分割できる属性は以下の通り。
- url
- hash
- bin
- shortcuts
- pre_install
- post_install
- installer
- uninstaller
- extract_dir
そしてextract_dir属性は、使用するディレクトリを抜き取るための属性です。擬似的にルートディレクトリを変更する属性、とも言えるでしょうか。
説明のために64bit版zipファイルを展開したときのツリービューを載せておきます。
.
└── vim81-kaoriya-win64
├── lua
│ ├── ...
~
├── vim.exe
├── vimrc
├── vimrun.exe
├── winpty-agent.exe
├── winpty.dll
└── xxd.exe
このままだとルート直下にvim81-kaoriya-win64
しか存在していない状態となってしまい、もしvimにパスを通すならbin属性にvim81-kaoriya-win64\\vim.exe
を渡さなければなりません。
しかしextract_dir属性に任意のディレクトリ、今回はvim81-kaoriya-win64
を指定することで実質的にルートが変更され、bin属性の値がvim.exe
とシンプルになるだけでなく後述するようにバージョン変更にも強くなります。
それでは、本題であるバージョン管理のための属性について見ていきましょう。
checkver属性
checkver属性は以下の内容に対して検索をかけることで、バージョンの取得を行うための属性です。
- GitHubリリースページのURL
- HTMLファイル
- JSON
ちなみに前回解説し忘れてしまったのですが、version属性も含めてバージョンは英数字と+-._
の組み合わせで構成されている必要があります。
GitHubのリリースURLから取得
一番楽なのはGitHubの最新版リリースページのURLを見る方法です。
nvmとcmderがこれを採用しているので見てみましょう。
"homepage": "https://github.com/coreybutler/nvim-windows",
"checkver": "github",
"homepage": "https://cmder.net",
"checkver": {
"github": "https://github.com/cmderdev/cmder"
},
ご覧の通り、homepage属性にGitHubリポジトリを当てているかで表記が変わります。
当てている場合はcheckver属性にgithub
を、そうでない場合はgithub属性を持ったオブジェクトを当てます。処理内容はどちらも同じで、https://github.com/USERNAME/REPONAME/releases/latest
のリダイレクト先URLにあるタグからバージョンを取得します。
ちなみにこの方法が使えるのは、
- プレリリース版ではない
- タグが
^v?[\d.$]+$
の正規表現に適合する
これら2点を満たしている場合のみです。限定的。
HTMLから取得
つづいて、HTMLの中から探す方法を見てみましょう。
一番よく行われているのはダウンロードするファイル名を検索する方法です。
たとえばgitのcheckverは以前こんな感じになっていました。
"checkver": {
"url": "https://github.com/git-for-windows/git/releases/latest",
"re": "v(?<version>[\\d\\w.]+)/PortableGit-(?<short>[\\d.]+).*\\.exe"
},
url属性に対象となるURLを指定するのですが、指定したHTMLにバージョン情報しか記載されていないことはまずないので、re(もしくはregex)属性も付け加えます。こうすることで得られるHTMLから正規表現を用いて情報を抜き出すことができます。
この正規表現にはちょっとした拡張があり、丸括弧中の先頭に?<VAR_NAME>
という接頭辞をつけることで、一致した内容に名前をつけることができます。ここでversion
という名前をつけるとそれがバージョンとして認識される仕組みです。丸括弧がひとつだけなら?<version>
すら不要です。
一方で、バージョンの構成要素が分散してしまっていたり、そのまま抜き出そうとすると曖昧すぎるマッチングになってしまう例もあるかと思います。このような場合はreplace属性を利用しましょう。
拙作のwinmerge-jpがこれを使っているので例にします。
"checkver": {
"re": "<td nowrap=\"\">(?<v1>[\\d]+.[\\d]+.[\\d]+)+-jp-(?<v2>[\\d]+)</td>",
"replace": "${v1}.${v2}"
},
- Before:
<td nowrap="">2.16.6+-jp-4</td>
- After:
2.16.6.4
このようにreplace属性内でマッチ結果をつなぎあわせることでバージョンを生成することができます。
それと、x264のリリースページのように、マッチする箇所が昇順に複数並んでいるけどそのうちの一番下にあるファイル名からコードを抜き出したい場合もあったりします。
そういうときはreverse属性にtrue
を指定してください。
"checkver": {
"url": "https://download.videolan.org/pub/videolan/x264/binaries/win64/",
"re": "x264-r(?<version>[\\d]+)-(?<commit>[a-fA-F0-9]{7}).exe",
"reverse": true
},
またhomepage属性に指定したURLにバージョンが入っている場合は省略した記法ができます。中身はre属性と同じですね。
"homepage": "http://www.7-zip.org/",
"checkver": "Download 7-zip ([^\\ ]+)",
JSONから取得
あまり見ないですが、JSONファイルにバージョンがある場合はjp属性にJSONPathで指定することで取得できます。
以下にNuGetのcheckverを挙げておきます。
"checkver": {
"url": "https://dist.nuget.org/index.json",
"jp": "$.artifacts[0].versions[0].version"
},
実際にやってみる
一通り方法が分かったところで、先程のVimにcheckver属性を付けてみましょう。
このリポジトリのタグは^v?[\d.]+$
の正規表現にマッチしないので、リリースページURLを利用する方法は使えません。
ここはわかりやすくリリースページのHTMLから取得する方法を採りましょう。
{
"homepage": "https://github.com/koron/vim-kaoriya",
"description": "Vim + kaoriya build system, without gvim.",
"version": "8.1.0005.20180520",
"architecture": {
"32bit": {
"url": "https://github.com/koron/vim-kaoriya/releases/download/v8.1.0005-20180520/vim81-kaoriya-win32-8.1.0005-20180520.zip",
"hash": "afacd6e27304136f5ebae3edddf6e747f410880fa6a986d80be772e03ef56d36",
"extract_dir": "vim81-kaoriya-win32"
},
"64bit": {
"url": "https://github.com/koron/vim-kaoriya/releases/download/v8.1.0005-20180520/vim81-kaoriya-win64-8.1.0005-20180520.zip",
"hash": "53e8dd08e2249ce8a54784e16469151a7bf9857ac9acf3a1b341ac4e7da26fb2",
"extract_dir": "vim81-kaoriya-win64"
}
},
"bin": "vim.exe",
"checkver": {
"url": "https://github.com/koron/vim-kaoriya/releases/latest",
"re": "vim(?<short>[\\d]+)-kaoriya-win32-(?<code>[\\d.]+)-(?<date>[\\d]{8}).zip",
"replace": "${code}.${date}"
}
}
code
に該当する部分だけでもいいのですが、個人的には更新日も知りたかったので入れてみました1。
ついでにcheckver属性を編集したら現在のversion属性もこれに合致するかどうか確認しておく癖を付けておくとなおいいです。
autoupdate属性
autoupdate属性ではcheckver属性で取得したバージョンを用いて、以下の属性を更新するための雛形を定義できます。
- url
- hash
- extract_dir
- notes(バージョン情報の使用不可)
今回はurl属性を例に解説していきます。
バージョン情報を埋め込む
バージョンは$version
という文字をそのまま埋め込むだけです。
"autoupdate": {
"url": "https://beyondgrep.com/ack-$version-single-file#/ack-single-file"
}
またバージョンがドット区切りであるならば各構成要素を示す特殊変数も使えます。
$version
が4つに区切られている場合は順に$majorVersion
$minorVersion
$patchVersion
$buildVersion
となります。
"version": "4.76",
"url": "https://justgetflux.com/flux-setup4-76.exe#/flux-setup.exe",
"autoupdate": {
"url": "https://justgetflux.com/flux-setup$majorVersion-$minorVersion.exe#/flux-setup.exe"
}
architecture属性でurlが分けられている場合は、autoupdate属性内にarchitecture属性をつくります。
"autoupdate": {
"architecture": {
"64bit": {
"url": "https://static.rust-lang.org/dist/rust-$version-x86_64-pc-windows-gnu.msi"
},
"32bit": {
"url": "https://static.rust-lang.org/dist/rust-$version-i686-pc-windows-gnu.msi"
}
}
}
checkver属性で作った値を利用する
checkverのre属性で変数を設定することができましたが、これをautoupdate属性内で利用することができます。
ただしreplace属性とは違って、abc
という変数はmatchAbc
と変数名が変わります。
先ほどのVimにはこれが使えそうですね。
"checkver": {
"url": "https://github.com/koron/vim-kaoriya/releases/latest",
"re": "vim(?<short>[\\d]+)-kaoriya-win32-(?<code>[\\d.]+)-(?<date>[\\d]{8}).zip",
"replace": "${code}.${date}"
},
"autoupdate": {
"architecture": {
"32bit": {
"url": "https://github.com/koron/vim-kaoriya/releases/download/v$matchCode-$matchDate/vim$matchShort-kaoriya-win32-$matchCode-$matchDate.zip"
},
"64bit": {
"url": "https://github.com/koron/vim-kaoriya/releases/download/v$matchCode-$matchDate/vim$matchShort-kaoriya-win64-$matchCode-$matchDate.zip"
}
}
},
hash属性を設定する
url属性の値が変われば、当然それに紐づくhash属性も変化させなければいけません。
しかしありがたいことにhash属性については指定しなくても自動でダウンロードしたファイルから算出してくれるので基本的に指定する必要はありません。
もし個別で用意してあるのであればhash属性として付け加えることができます。
指定方法や抽出方法も充実しているので、詳しくは公式Wikiをご覧ください。
実際にやってみる
url属性の書き方は既に説明したので、あとはextract_dir属性だけすが、こいつにはurl属性と同じ変数と特殊変数が使えます。
ということでVimのManifestは以下のもので完成形となります。
{
"homepage": "https://github.com/koron/vim-kaoriya",
"description": "Vim + kaoriya build system, without gvim.",
"version": "8.1.0005.20180520",
"architecture": {
"32bit": {
"url": "https://github.com/koron/vim-kaoriya/releases/download/v8.1.0005-20180520/vim81-kaoriya-win32-8.1.0005-20180520.zip",
"hash": "afacd6e27304136f5ebae3edddf6e747f410880fa6a986d80be772e03ef56d36",
"extract_dir": "vim81-kaoriya-win32"
},
"64bit": {
"url": "https://github.com/koron/vim-kaoriya/releases/download/v8.1.0005-20180520/vim81-kaoriya-win64-8.1.0005-20180520.zip",
"hash": "53e8dd08e2249ce8a54784e16469151a7bf9857ac9acf3a1b341ac4e7da26fb2",
"extract_dir": "vim81-kaoriya-win64"
}
},
"bin": "vim.exe",
"checkver": {
"url": "https://github.com/koron/vim-kaoriya/releases/latest",
"re": "vim(?<short>[\\d]+)-kaoriya-win32-(?<code>[\\d.]+)-(?<date>[\\d]{8}).zip",
"replace": "${versionCode}.${date}"
},
"autoupdate": {
"architecture": {
"32bit": {
"url": "https://github.com/koron/vim-kaoriya/releases/download/v$matchCode-$matchDate/vim$matchShort-kaoriya-win32-$matchCode-$matchDate.zip",
"extract_dir": "vim$matchShort-kaoriya-win32"
},
"64bit": {
"url": "https://github.com/koron/vim-kaoriya/releases/download/v$matchCode-$matchDate/vim$matchShort-kaoriya-win64-$matchCode-$matchDate.zip",
"extract_dir": "vim$matchShort-kaoriya-win64"
}
}
}
}
バージョンの自動更新を実行しよう
さて、checver属性もautoupdate属性も付けましたが、いったいどのタイミングで更新がされるのでしょうか?
利用者のscoop update
実行時? いえいえ、これもManifest作成者がやらなくちゃいけないんです。
Windows上で自動更新する
Scoopにはもともとcheckver.ps1
というManifest自動更新用のスクリプトが用意されており、extras Bucketなどはこれを利用するための同名スクリプトが用意されています。特に理由がない限りはextras Bucketにあるcheckver.ps1
をそのまま自分のリポジトリのbinディレクトリ内に入れておきましょう2。
これによって、リポジトリルートにて.\bin\checkver.ps1 MANIFEST
を叩くと公式で管理されているバージョン確認処理が、-u
オプションを付けることで自動更新の処理も併せて実行されるようになっています。
ただいちいちリポジトリに移動してコマンドを叩くのも面倒なので、いっそのこと関数にしてみましょう。どうせならscoop update
の際に一緒に実行できれば幸せですよね。
というわけでPowerShellスクリプトを作ってみました。
# globalのuser.nameとBucket所有者名が一致するものを自動更新
# * リモート名は"origin"のみ対応
# * sshを使うようになっていないもの(=初期状態)はスルーするので
# 事前に該当リポジトリ内で"git remote set-url"を実行する必要あり
# * 自動でcommitおよびpushをするので注意
$my_name = git config --global user.name
$my_buckets = @()
$prompt_current_dir = Get-Location
$scoop_root = [environment]GetEnvironmentVariable('SCOOP', 'User')
if (!$scoop_root) {
$scoop_root = "$HOME\scoop"
}
Get-ChildItem $scoop_root\buckets\* | ForEach-Object {
Set-Location $_
$url = git remote get-url --push "origin"
if ($url -clike "git@*/$my_name/*") {
$my_buckets += $_
}
}
foreach ($bucket in $my_buckets) {
Set-Location $bucket
if (Test-Path .\bin\checkver.ps1) {
Write-Host "Check Bucket: $_" -ForegroundColor Green
Get-ChildItem .\*.json | ForEach-Object {
$json = $_.Basename
.\bin\checkver.ps1 $json -u
if ($(git diff $_)) {
Write-Host " => Update: $json" -ForegroundColor Blue
git commit -a -m "Update: $json"
}
}
if ($(git diff)) {
Write-Host " => Push bucket..."
git push
}
}
}
Set-Location $prompt_current_dir
こいつにパスを通してupdate_buckets; scoop update; scoop update *
をエイリアスにしておくことで、Scoopのアップデートと同時に独自Bucketも更新がされるようになります。
Dockerで全自動更新する (推奨)
おそらく公式が利用している手法で、cronによる自動更新のみならずプルリクの自動適用にも対応しています。GitHubにリポジトリが公開されているので利用してみましょう。
GitHub - ScoopInstaller/Excavator: 🕳️ This container runs the updating services for all scoop manifest repos
GitHub - dooteeen/Excavator-JP: Fork of ScoopInstaller/Excavator. (私の作成例)
-
自分のbucketに自動更新を反映させるためのスクリプトを用意。
binディレクトリの中にbucekt-updater.ps1を作成、指示通りの内容を記述して保存します。BUCKET-ROOT/bin/bucket-updater.ps1# scoop本体のauto-pr.ps1を起動。内部で前述のcheckver.ps1が実行される。 param( # 引数にbucket情報が必要なのでここを編集 # 例: "hogeman/bucket-hoge:master" [String]$upstream = "<GIT_USERNAME>/<BUCKET>: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/.." # checks the parent dir iex -command "$autopr -dir $dir -upstream $upstream $($args |% { "$_ " })"
-
タイムゾーン設定のためDockerfileを編集。
Excavator/DockerfileFROM phusion/baseimage:0.11 # Set timezone. RUN apt update && apt install -y --no-install-recommends tzdata ENV TZ "Asia/Tokyo" # Use baseimage-docker's init system. CMD ["/sbin/my_init"] # 以下省略
-
READMEにしたがって以下のようにdocker-compose.ymlを編集し、
docker-compose up -d --build
で実行。Excavator/docker-compose.ymlversion: "3" services: bucket: build: # imagesの代わりにbuildを利用 # 手順2で編集したDockerfileを用いる設定 context: . dockerfile: Dockerfile deploy: mode: global volumes: - ssh:/root/.ssh - logs:/root/log environment: # 必要な情報をここに入力しておく GIT_USERNAME: "my-git-name" GIT_EMAIL: "my-address@gmail.com" BUCKET: "username/bucket-name" # GitHub上にあるリポジトリを指定 CRONTAB: "0 0 * * *" # crontabの書式で実行頻度を指定 volumes: ssh: logs:
-
生成されたSSHの公開鍵をGitHubに登録。あとは勝手に更新してくれるようになります。
上記の例ではGitHub上における単一リポジトリを対象としていますが、少し編集するだけで他ホスティングサービスに対応させたり複数のbucketを管理したりすることもできます。
まとめ
- checkver属性でHTMLなどからバージョンの取得方法が指定できる
- autoupdate属性で更新内容を定義できる
- url属性、extract_dir属性はバージョン情報とcheckverのre属性のマッチ結果も使える
- hash属性は基本的に指定しなくてもいい
- Dockerが使えれば更新の全自動化が可能。手動で行う人のために
checkver.ps1
も完備
あとがき
今回は自動更新にまつわる機能についての記事になりました。公式Wikiがかなり充実しているので記事にする必要もないかと思いましたが、Scoop普及のためにいろいろ頑張ってみました。
そしてこの記事を書くにあたって改めて、この自動アップデートすらも簡単に済ませられるScoopのお手軽さに感動しました。皆さんも勿論この便利さに涙を流しながら喜んでいることでしょう。私には分かってます。
あ、次回はいよいよ環境構築についての話です……が、書けるかどうかすら危うい。おのれWindows!!