Mastodonにリビジョンアップでの自動リロード機能を導入してみた

はじめに

この記事はMastodonにリビジョンアップという機能を追加したときの導入背景や実装などをまとめた記事になります。 主に僕個人の備忘録としてまとめています。

導入の背景などあれこれ

2017年5月から6年ほどCreatodonというMastodonのサーバーを個人的に運営しています。

gamelinks007.net

この半年くらいでユーザー数も増え、また連合するサーバーなども増え色々と管理コストなどもかかるようになってきていました。 (まあ、ユーザー数に関しては僕がサーバー運営周りで疲れ果てて、ユーザーの皆さんを引っ掻き回したので現状だと半年前と同じくらいまで減りましたけど......)

その辺の管理コストを削減するべく、最近いろいろと手を入れている状況でした。

Creatodonではmain追従と言い、最新のMastodonソースコードを定期的に追従して本番環境で動かすという運用をしています。 そのため頻繁にサーバーメンテナンスを行うことがありました。 その際に画面周りの新しい変更が反映されないことがあり、その辺の問い合わせ対応とかやってたりするのが面倒くさかったので今回リビジョンアップを導入した感じです。

なので、リビジョンアップを導入する前は - サーバーメンテナンス後にブラウザのリロードをお願いする(実際はほとんど画面周りの変更がなかったのであまりやってないですが) - 「画面周りの新機能が表示されないです」という問い合わせを起点に、「ブラウザのリロードかけてください」とアナウンス みたいなことをやってました。

で、こういうのは正直僕も面倒ですし、一々ブラウザをリロードしなきゃならないユーザーの皆さんも面倒です。

そこでリビジョンアップという機能を導入するべく対応を進めました。

ちなみにリビジョンアップについてはこちらの記事が詳しいですが、簡単に説明すると - SPAなWebアプリケーションで、新しいリリースがされた場合に古いバージョンがキャッシュされたままになっていることがある - そのためバックエンド側とのデータ不整合が発生する可能性やフロント側がクラッシュする可能性がある - それをフロント側とバックエンド側とでバージョン情報を保持し、バージョンが変更されたことを検知し、クラッシュなどを回避する というのがリビジョンアップという機能になります。

実際の挙動

以下の動画のように自動的にリロードを行うように動きます。 PWAなどで利用している場合でも自動的にリロードしてくれます。

gamelinks007.net

やったこと

実際のPRなど

実際の変更差分としては以下のPRを参照して頂ければわかるかと思います。

github.com

github.com

github.com

github.com

バックエンド側でリリースされているバージョン情報を返すように修正

MastodonではMASTODON_VERSION_SUFFIXMASTODON_VERSION_FLAGSという環境変数を設定するとv4.1.2@74c535e6f11a58acbba3bd0a3c1df283105c82afのようにAPIが返すバージョン情報を変更することができます。

これを流用し、リリースされているバージョン情報として最新のコミットのハッシュを使うようにしました。

まず以下のようなRubyのコードを追加しました。

# frozen_string_literal: true

# .env.productionを読み込む
env = File.read('.env.production')

# 最新のコミットのハッシュを取得
hash = `git show --format='%H' --no-patch`

# 最新のコミットのハッシュをMASTODON_VERSION_SUFFIXに設定
env = env.gsub!(/^MASTODON_VERSION_SUFFIX=.+$/, "MASTODON_VERSION_SUFFIX=#{hash}")

# .env.productionへ置き換えたものを書き込む
File.write('.env.production', env)

やっていることとしては最新のコミットのハッシュを取得し、正規表現を使って.env.productionにあるMASTODON_VERSION_SUFFIXへ置き換えつつ渡すようにしています。

あとはGitのpost-mergeというフックで以下のようなShellが実行されるようにしています。

#!/bin/sh

`ruby revision.rb`

post-mergeを使っている背景としては - 元々Creatodonは、さくらインターネットさんが提供しているMastodonスタートアップスクリプトで建てた - その関係でサーバーにログインし、Gitで新しいバージョンなどをpullする運用でメンテナンスしていた - なのでその運用フローをそのまま使いつつ、自動的にバージョン情報を更新するのにpost-mergeが良さそうだった という感じですね

フロント側でバージョン情報を取得し、バージョンが変わっていればリロードする機能の追加

フロント側ではAPIから取得できるバージョン情報をブラウザのlocalStorageに保存するようにしています。 またlocalStorageに保存されているバージョンとAPIが返すバージョンが異なっている場合にリロードする処理も追加しています。

import axios from 'axios'

const setLocalStorageVersion = (version) => {
  localStorage.setItem('VERSION', version)
}

const getLocalStorageRevision = () => {
  if (!localStorage.getItem('VERSION')) {
    return ''
  }
  return localStorage.getItem('VERSION')
}

const checkRevision = () => {
  axios.get('/api/v2/instance').then(response => {

    const current_version = response.data.version;

    const user_cached_version = getLocalStorageRevision();

    if (user_cached_version !== current_version) {
      alert('新しいリリースが出ています。自動的にリロードを行います。');
      setLocalStorageVersion(current_version);
      location.reload(true)
    }
  }).catch(error => { // eslint-disable-line
  });
}

あとは適当な場所にcheckRevision()を追加してる感じですね。

おわりに

とりあえず、こんな感じでリビジョンアップをMastodon本体に最小限の変更で導入できました。 今後は、画面に表示されているメッセージが簡素すぎるのでその辺をいじりたいですねー。

参考

qiita.com

dev.classmethod.jp

tech.smartcamp.co.jp

zenn.dev