フロントエンドはReact.js, バックエンドはRailsでAPIとして利用しているような環境で働いています。
フロントエンドを新しくリリースしたが、ブラウザキャッシュやCDNがキャッシュを返していてリリースされたバージョンが反映されていないかも!?
みたいなことが最近ありました。
で、このときの調査が大変だったので、ユーザーが見ているバージョンは何であるかわかるようにしたいなとか考えました。
ってことで
- フロントエンドはpackage.jsonに記載のバージョンを自動で更新させよう
- APIでも、現在のバージョンを返すAPIを生やしておこう
としました。
このバージョンを、GitHub Actionsを使って自動で更新させたら便利だったので紹介です。
完成系はこんな感じです
やること
前提ですが、自分はGitHubのリリース機能を利用しています。
Releaseがpublished(公開)されたら、リリースフローが走る、という流れです。
なので、リリースががされたら、次のバージョンに更新するCIを動かすことにしました。
フロントエンド
いきなりCIを書いてしまいます。
.github/workflows/update-version.yml
name: Update Version
on:
release:
types: [published]
jobs:
update-version:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main
- name: Set up Git user
run: |
git config user.name github-actions
git config user.email github-actions@github.com
- name: Update version file
run: |
npm version patch
- name: push
run: git push
それほど難しないですね。
バージョンの更新は、 npm version patch
で行っています。
このコマンドを実行をすると、
- package.jsonのバージョンが更新される
- package-lock.jsonのバージョンが更新される
- バージョン名のコミットが作成される
と上記の3つが行われます。
ここからは余談
ここで更新されたバージョンをReact.jsでも表示させてあげたかったので、それもやってしまいます。
自分はWebpackを利用しているので、その場合で書きます。
webpack.config.js
const webpack = require("webpack");
module.exports = {
...,
pluguins: [
...,
new webpack.DefinePlugin({
"process.env.APP_VERSION": JSON.stringify(
process.env.npm_package_version,
),
}),
],
}
process.env.npm_package_version
でバージョンは取得できるので、それを環境変数に組み込むようなコードです。
これを書いておけば、アプリケーション側で、以下のようなコードでバージョンが表示されます。
<strong>App Version:</strong> {process.env.APP_VERSION}
自分の場合は、こんな感じでユーザーのブラウザの情報を表示させる画面とか作っています。そこに載せました。
バックエンド
こっちは npm run patch
のようなコマンドがないので、もう少し工夫が必要です。
まず、バージョンを管理しているファイルはここです。
lib/version.rb
module YourAppName
VERSION = "1.1.41".freeze
end
Controller側でこのようにして表示していますね。
require "version"
class Api::VersionsController < Api::BaseController
def show
render json: { version: YourAppName::VERSION }
end
end
で、CI側では lib/version.rb
の
module YourAppName
VERSION = "1.1.41".freeze
end
ここのVERSIONの値を更新させます。
実際のCI
詳しく見ていきます。
name: Update Version
on:
release:
types: [published]
jobs:
update-version:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
- name: Get latest version
id: get_version
run: |
LATEST_VERSION=$(git tag --sort=-creatordate | head -1 | sed 's/^v//')
echo "Latest version: $LATEST_VERSION"
echo "::set-output name=version::${LATEST_VERSION}"
- name: Calculate next version
id: calc_next_version
run: |
# バージョン番号を分割(例: 1.0.0 -> 1, 0, 0)
echo "Version: ${{ steps.get_version.outputs.version }}"
IFS='.' read -ra VERSION_PARTS <<< "${{ steps.get_version.outputs.version }}"
MAJOR=${VERSION_PARTS[0]}
MINOR=${VERSION_PARTS[1]}
PATCH=${VERSION_PARTS[2]}
echo "Major: $MAJOR, Minor: $MINOR, Patch: $PATCH"
# パッチバージョンをインクリメント
let PATCH+=1
# 次のバージョンを計算
NEXT_VERSION="$MAJOR.$MINOR.$PATCH"
echo "Next version: $NEXT_VERSION"
echo "::set-output name=next_version::${NEXT_VERSION}"
- name: Update version file
run: |
sed -i "s/VERSION = \".*\"/VERSION = \"${{ steps.calc_next_version.outputs.next_version }}\"/" lib/version.rb
- name: Commit and push
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git add lib/version.rb
git commit -m "Update version to ${{ steps.calc_next_version.outputs.next_version }}"
git push
最初のStep(Checkout)
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
これはいつもやるcheckoutです。
このとき、ref: main
にしておいて、mainブランチで動かすように明示しています。
また、git tagでタグ一覧も取得できるようにするため。fetch-depth: 0
も書きます。
2番目のステップ(最新のバージョン取得)
- name: Get latest version
id: get_version
run: |
LATEST_VERSION=$(git tag --sort=-creatordate | head -1 | sed 's/^v//')
echo "Latest version: $LATEST_VERSION"
echo "::set-output name=version::${LATEST_VERSION}"
git tag --sort=-creatordate | head -1
このコマンドで、最新のバージョンを取得します。
またv1.0.0
のように私はしているので、vを消すように置換しそれをLATEST_VERSIONという変数に入れます。
3番目のステップ(次のバージョン番号を作る)
- name: Calculate next version
id: calc_next_version
run: |
# バージョン番号を分割(例: 1.0.0 -> 1, 0, 0)
echo "Version: ${{ steps.get_version.outputs.version }}"
IFS='.' read -ra VERSION_PARTS <<< "${{ steps.get_version.outputs.version }}"
MAJOR=${VERSION_PARTS[0]}
MINOR=${VERSION_PARTS[1]}
PATCH=${VERSION_PARTS[2]}
echo "Major: $MAJOR, Minor: $MINOR, Patch: $PATCH"
# パッチバージョンをインクリメント
let PATCH+=1
# 次のバージョンを計算
NEXT_VERSION="$MAJOR.$MINOR.$PATCH"
echo "Next version: $NEXT_VERSION"
echo "::set-output name=next_version::${NEXT_VERSION}"
ここでやっていることは、
- 2番目のステップで取得した
2.1.0
のようなバージョンを、2と1と0に分割して、VERSION_PARTSという変数に入れます。 - パッチバージョンがあるVERSION_PARTS[2]の数字に1つ加えます(0 + 1をする)
- NEXT_VERSIONという変数にバッチバージョンを更新した状態で代入(2.1.1)が代入される
です。
4番目のステップ(lib/version.rbの更新)
- name: Update version file
run: |
sed -i "s/VERSION = \".*\"/VERSION = \"${{ steps.calc_next_version.outputs.next_version }}\"/" lib/version.rb
sedを使って新しいバージョンへ置換するようにしています。
5番目のステップ(commitしてpush)
- name: Commit and push
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git add lib/version.rb
git commit -m "Update version to ${{ steps.calc_next_version.outputs.next_version }}"
git push
これで完成です。
おわりに
今回はリリースがされたら、というのをトリガーにして次のバージョンへの更新を行いました。
組織によってはリリースの流れが違うと思うので、その際はちょっとずつ変更しながら使ってください。
自分の組織では、ユーザーに届いているバージョンが確認できるようになったことは、とてもお問合せの際に役立つのでよかったなと思います。