🦕

Deno の CLI アプリケーションで自身を最新バージョンに更新する技

2022/06/20に公開

Deno はスクリプトを名前をつけてインストールする機能があります。
Aleph.js の aleph や Velociraptor の vr などはその機能を使っています。

https://deno.land/manual/tools/script_installer#script-installer

例えば、 Velociraptor の例では、

bash
deno install -qAn vr https://deno.land/x/velociraptor@1.4.0/cli.ts

とすることで、vr という名前で https://deno.land/x/velociraptor@1.4.0/cli.ts を実行することができます。
非常に便利です。
オプションの qquiet という意味で、インストール時のログを隠し、A はいつもの deno run -A と同じで、実行時の権限を全て許可し、nname という意味で、後ろに指定した名前を付けてインストールします。
上書きインストールする場合は、f オプションを追加する必要があります。

アンインストールする際は、

bash
deno uninstall vr

でいけます。

しかし、このようにインストールしたものを最新版に更新したいというときには少し工夫が必要です。

deno.land で公開している前提で進めます。
モジュールの公開方法は、公式の解説 を参考にしてください。

うまくいかない方法

最新版に更新する方法は、Deno の機能として提供されていないので自分で実装する必要があります。
まず、思いつくのは次のような方法かもしれません。

install.ts
// 自身の最新版をインストールする
const upgradeSelf = () => {
    Deno.run({
        cmd: [
            Deno.execPath(), // 今使われている deno のバイナリのパス
            "install",
            "-fqAn", // 上書きインストールなので -f を追加
            "your_module", // 名前
            "https://deno.land/x/[your_module]/cli.ts" // バージョンを指定しない
        ]
    })
}

バージョンを指定しなければ自動で最新版が使われると思うかもしれませんがそうではありません。
基本的にはキャッシュされているバージョンのものが使われてしまいます。

そのため、自分で最新バージョンのタグを見つける必要があります。
次のようにすることで最新バージョンを取得することができます。

うまくいく方法

install.ts
// 自身の最新版をインストールする
const upgradeSelf = async () => {

    // バージョンを指定せずに fetch する
    const res = await fetch("https://deno.land/x/[your_module]/")

    console.log(res)
    /*
    Response {
        body: ReadableStream { locked: false },
        bodyUsed: false,
        headers: Headers {
            "access-control-allow-origin": "*",
            "content-length": "13",
            "content-type": "text/plain;charset=UTF-8",
            date: "Mon, 20 Jun 2022 02:08:51 GMT",
            server: "deno/asia-northeast1-a",
            vary: "Accept-Encoding"
        },
        ok: false,
        redirected: true,
        status: 404,
        statusText: "Not Found",
        url: "https://deno.land/x/[your_module]@v0.0.9"
    }
    */

    const url = res.url

    console.log(url) // https://deno.land/x/[your_module]@v0.0.9

    Deno.run({
        cmd: [
            Deno.execPath(),
            "install",
            "-fqAn",
            "your_module",
            `${url}/cli.ts`
        ]
    })
}

通常、ブラウザでバージョンを指定せずにアクセスした場合、最新バージョンにリダイレクトされるのを利用します。

バージョンを指定しないで fetch() したレスポンスは、ステータスコード 404 になり、本来アクセスすべき URL も一緒に渡されます。

その URL を使うことで最新バージョンのスクリプトを指定することができます。

deno.land の仕様が変わったら使えなくなってしまいますが、考え方的にはこんな感じでやれば最新タグを簡単に取得できます。
自分で別のサーバーを用意したり、バージョンを変えるごとに何かを書き換える必要がないので楽です。

GitHubで編集を提案

Discussion