
カンリーホームページにおける各社ホームページのバージョン管理方法
株式会社カンリーエンジニア部の角谷(@canly_motsuo)です。
趣味のポーカーに熱中して、マニラやラスベガスに行っていたら前回のテックブログからほぼ1年が経過してしまいました。

そろそろ真面目に仕事をしないと上司から冷たい目で見られてしまうので、今回は私たちのチームで使用しているプロダクトの振り返りと、クライアントごとのバージョン管理の仕組みについてご紹介します!
カンリーホームページとは…?
HP来訪者の実店舗への来訪導線を最適化するHPが「カンリーホームページ」です。 ユーザーによって使いやすい店舗ページを作成することができます。

各クライアント様に使用していただく上での課題点
基本的に自由にカスタマイズできるCMSを提供していますが、クライアント様のHPとの整合性を保つために、機能にない設定を個別に対応する必要があります。
(要望の多い機能に関しては、正式な機能としてグロースを進めています。)

一部のお客様からの要望に応えるためにも、特別な仕様を施した機能を追加してしまうと今後のバージョンから切り離され、新しいバージョンに対応していないクライアント様のカンリーホームページが生まれてしまうことになります。

新機能や脆弱性のパッチ対応を迅速にクライアント様に提供するため、この課題に対して我々は以下のようなアプローチを取りました。
Polyrepoでリポジトリ作成、コアモジュールを使用してバージョンを管理する。
昨今、プロジェクトに関わる複数のリポジトリを単一のリポジトリで管理するMonorepoの方法が注目されていますが、我々はその逆であるPolyrepoで管理しています。
単純にPolyrepoにしたのではなく、コアとなるリポジトリを作成し、各クライアントのリポジトリはこのコアリポジトリを参照してそれぞれ別のコアモジュールのバージョン環境を作成できるようにしました。

また、テンプレートリポジトリも用意し、cloneすることで容易にクライアントリポジトリを作成できるようにもしています。
ここでなぜ現時点ではPolyrepoにしたのかと言う点については、社内の過去の背景も多少含まれていますが、以下のメリットが大きいと感じてPolyrepoにしています。
各クライアントリポジトリで、個別の対応がある程度求められているプロジェクトである(例: ヘッダーやフッター、一部特殊ケースでの要望依頼修正)
monorepo時、グロースでバージョンを上げる際には、全クライアントのバージョンが上がってしまう為、リリース時のコストが高くなることの懸念があった
(社内の背景的に、他プロジェクトと同時にリリースをする必要があり、よりリリースコストが増えてしまう点も懸念もあった)現時点でクライアント数がかなりあり、今後も増えていく中で上記のリリースタイミングを待つことなく、お客様のリリースタイミングに合わせてクライアントホームページをリリースするため
各クライアントリポジトリのpackage.jsonでそれぞれページを持たせる。
テンプレートリポジトリからcloneされたリポジトリはpackage.jsonでコアリポジトリであらかじめ用意してあるタグを参照してバージョンを取得するようにしています。
下記は、クライアント側のpackage.jsonです。(※一部コードをマスクして変数名等を改変しております)
### package.json
"scripts": {
"dev": "npm run copyToCoreModules && next dev",
"updateModules": "npm -f upgrade @canly/canly-core-repository && node ./buildPackageJson.js && npm i",
"updateModules:ci": "npm run updateModules && npm run copyToCoreModules",
"copyToCoreModules": "rm -rf .next/cache/ && node ./copyToCoreModules.js"
},
"dependencies": {
"@canly/canlyhp-core-repository": "github:canly/canlyhp-core-repository#v1.1.0"
},
そして、package.jsonのscriptsに含まれているように、CLIでコマンドを実行することで、特定のスクリプトを呼び出し、自身のnode_modulesの中に特定バージョンのコアモジュールを格納するようにしています。
我々はNext.jsを使用して開発をしており、クライアントリポジトリのフォルダ構成は下記のようになっています。
それぞれのファイルの中では、node_modules内のコアリポジトリのファイルを参照するようにしてコードにしています。

構成順としては下記の通りです。
package.json, coreToCoreModules.jsでcoreリポジトリのモジュールを取得する
core_modulesを読み取ってページを作成
build時node_modules内部のTS変換処理を含めて実行し、自身のページを展開する
下記は、クライアントリポジトリがコアリポジトリを取得する例です。
### src/page/index.tsx
import { GetServerSidePropsContext } from 'next'
import {
TopPage,
TopPageProps,
getServerSideProps as coreGetServerSideProps,
} from '@canly/canly-core-repository/src/pages/index'
const Page: React.FC<TopPageProps> = (props) => {
return <TopPage {...props} />
}
export const getServerSideProps = async (
context: GetServerSidePropsContext
) => {
const result = await coreGetServerSideProps(context)
return { props: result.props }
}
export default Page
上記のコードで分かる通り、getServerSideProps内部でAPIからデータを取得した上で、コアリポジトリからTopPageのコンポーネントを取得して描画していることがわかるかと思います。
先述したHeaderやFooterについては、_app.tsx等で全ページのコンポーネントに描画されるようにし、クライアントリポジトリごとに個別のコンポーネントが作成されるようにしています。
現時点での運用について
上記の構成に変更した上で、現時点での運用方法についておさらいしていきたいと思います。

PMがクライアント様から作成依頼を受け、仕様を受けた上で特別な仕様がないか調整した上でエンジニアにリポジトリ作成依頼
エンジニアはテンプレートリポジトリをcloneした上で、個別のクライアントリポジトリを作成
HeaderやFooter等のそのクライアントリポジトリ限定の調整を行った上で、デプロイをする
エンドユーザーにサービスを提供する
といった流れで運用しています。もちろんですが、この一連の中でエンジニアが介在する部分が少しあり、工数を取ってしまっている問題点もあります。
こうした問題を解決するために、GitHub Actionsを使用し、定期的に全クライアントリポジトリの現バージョンを取得し、ワンタップでバージョンを自動的に上げるツールの作成や、PMのタイミングで自動的に作成できるスクリプトを作成し、自動化にも努めています。
最後に
いかがでしたでしょうか。今回はカンリーホームページで各クライアントごとに、どのようにバージョンを管理しているかについてのご紹介でした。
詳細については省略させていただきましたが、ご参考になれば幸いです!
弊社ではまだまだ一緒に働く仲間を募集しています!
少しお話を聞いてみたいという方は、私のTwitter ( @canly_motsuo ) でのDMで聞いていただく、もしくは下記HRMOS(ハーモス)からカジュアル面談もできますので、お気軽にお声掛けください!