🐱

GitHub Actionsでgit submoduleを更新する

5 min read

GitHub Actionsでsubmoduleを扱う記事は多くありますが、個人でやっている方法についてまとめたいと思います。

submodule自体については、以下の記事がsubmoduleを利用するユースケースなども含めて、かなり分かりやすくまとまっていると思います。

https://www.m3tech.blog/entry/git-submodule

GitHub Actionsの作成

作成したGitHub Actionsですが、ポイントは以下のようになっています。

  • actions/checkoutでsubmoduleもHTTPでcloneする
    • SSH鍵設定は不要
  • git submodule update --remote --recursiveコマンドで更新する
  • cronで自動実行する
  • workflow_dispatchで手動実行も可能

今回作成したものは以下のリポジトリに置いてあります。

https://github.com/ymmmtym/templates

またGitHub Actionsを作成するリポジトリには、既にsubmoduleを追加していることを前提に作成していきます。

actions/checkoutの確認

actions/checkoutのドキュメントを確認します。

https://github.com/actions/checkout

普段はおまじないだと思って脳死で書いているところですが、改めて使用方法を確認してみます。

- uses: actions/checkout@v2
  with:
    snip...

    # Whether to checkout submodules: `true` to checkout submodules or `recursive` to
    # recursively checkout submodules.
    #
    # When the `ssh-key` input is not provided, SSH URLs beginning with
    # `git@github.com:` are converted to HTTPS.
    #
    # Default: false
    submodules: ''

submoduleと言うパラメータが使用できるとのことなので、submodule: recursiveに設定して使用してみます。

コメントアウトに記載の通り、パラメータssh-keyが設定されていない場合は、
checkoutのステップでsubmoduleもcloneするように設定すると自動的にHTTPでcloneするように切り替わるようです。

.gitmodulesにssh形式で記載されている場合でも適用されます。(勝手に書き変わる事はありません)

git configの設定

git submodule update --remoteは、submoduleをリモートリポジトリの内容に更新するコマンドですが、デフォルトのbranchがmasterとなっています。

これを他のbranchに変更する場合は、ローカルリポジトリで以下を実行します。

: git config -f .gitmodules submodule.<リポジトリ>.branch <ブランチ>

# 例
git config -f .gitmodules submodule.template-gin.branch main

これをsubmodule分だけ実行すると、.gitmodulesファイルにbranch = mainといった内容が追加されます。(以下は今回のリポジトリにあるものです)

$ cat .gitmodules
[submodule "template-rails"]
	path = template-rails
	url = git@github.com:ymmmtym/template-rails.git
	branch = master
[submodule "template-mkdocs"]
	path = template-mkdocs
	url = git@github.com:ymmmtym/template-mkdocs.git
	branch = main
[submodule "template-deno"]
	path = template-deno
	url = git@github.com:ymmmtym/template-deno.git
	branch = main
[submodule "template-gin"]
	path = template-gin
	url = git@github.com:ymmmtym/template-gin.git
	branch = master
[submodule "template-django-polls"]
	path = template-django-polls
	url = git@github.com:ymmmtym/template-django-polls.git
	branch = master
[submodule "template-vue"]
	path = template-vue
	url = git@github.com:ymmmtym/template-vue.git
	branch = master
[submodule "template-node-express"]
	path = template-node-express
	url = git@github.com:ymmmtym/template-node-express.git
	branch = master

この状態でgit submodule update --remote --recursiveを実行すると、submodule毎に設定されているbranchに対して更新を行うことができます。

workflowの作成

作成したymlは以下になります。

https://github.com/ymmmtym/templates/blob/main/.github/workflows/update_submodules.yml
update_submodules.yml
name: Update submodules
on:
  schedule:
    - cron:  '0 * * * *'
  workflow_dispatch:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  update_submodules:
    name: Update submodules
    runs-on: ubuntu-18.04
    env:
      TZ: "Asia/Tokyo"
    if: contains(github.event.head_commit.message, '[ci skip]') == false
    steps:
    - uses: actions/checkout@v2
      with:
        submodules: recursive
    - name: Update submodules
      id: update
      run: git submodule update --remote --recursive 
    - name: Run git status
      id: status
      run: echo "::set-output name=status::$(git status -s)"
    - name: Add and commit files
      run: |
        git add .
        git config --local user.email "action@github.com"
        git config --local user.name "GitHub Action"
        git commit -m "Update submodules at $(date "+DATE: %Y-%m-%d TIME: %H:%M:%S")"
      if: ${{ steps.status.outputs.status }}
    - name: Push changes
      uses: ad-m/github-push-action@master
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        branch: main
      if: ${{ steps.status.outputs.status && github.event_name == 'push' }}

やっている内容についてはシンプルになっていて、git status -sで差分があった場合は、git add .git commitしてmainブランチにpushするworkflowになっています。
デフォルトでは、cronとworkflow_dispatchによってmainブランチで起動したものと、mainブランチにpushされたものが更新され、pushされます。

その他

https://qiita.com/kshimo69/items/ac22d414d756ea08943f

git submodule foreach git pull origin masterを実行する方法があると知り試しましたが、私の環境では問題点がありました。
まずgit submodule foreachは全てのsubmoduleに対して、同じgit動作を行うことになります。
なので、mainとmasterなどbranch名が混在する時にはエラーになってしまいます。

https://haibara-works.hatenablog.com/entry/2020/06/13/035619

repository_dispatchを利用して、submoduleが更新されたことをトリガーにworkflowを実行できるみたいです。OSS以外をsubmoduleとして導入している場合のベストプラクティスだと感じています。

Reference

https://git-scm.com/book/ja/v2/Git-のさまざまなツール-サブモジュール
https://stackoverflow.com/questions/64407333/using-github-actions-to-automatically-update-the-repos-submodules
https://qiita.com/kshimo69/items/ac22d414d756ea08943f
https://haibara-works.hatenablog.com/entry/2020/06/13/035619
https://www.m3tech.blog/entry/git-submodule