Auto-update GitHub submodules

Update a GitHub repository submodule to the most recent SHA, as a webhook.

Tina Huang · Aug 23, 2019

Modular.

Git submodules allow you to nest one repository inside of another. At Transposit, we use submodules because we want to open-source our docs repo to allow community contributions, but we want to keep the remainder of our website in a separate private repository. Unfortunately, this setup led to a pretty manual and unreliable process: changes would often go unpublished when team members would commit to the docs repo, but forget to update the submodule SHA.

If you've worked with Git submodules, you've probably experienced the painful process of updating the submodule SHA. The basic process is to first go to the submodule path within the parent repo and update the submodule to the new SHA. Usually, this involves doing a git pull on master. Then, in the parent repository, commit the updated SHA. Here's an example of what the diff looks like:

diff --git a/src/docs b/src/docs
index 4689aaf..1032565 160000
--- a/src/docs
+++ b/src/docs
@@ -1 +1 @@
-Subproject commit 4689aaf4e28305ca73f4958a191ba8457416f8b1
+Subproject commit 10325655b5ee0f3c89375da7ea73b14fae6b6330

I wanted to automate the process such that on commit to the submodule, the parent repository automatically updates the SHA. I found this post explaining how to use the GitHub tree API to update a submodule to a specific commit, and decided to use Transposit to build a GitHub webhook. Here's what the code to do this update looks like in Transposit:

(params) => {
let owner = env.get('parentOwner');
let repo = env.get('parentRepo');
let branch = env.get('parentBranch');
let submoduleOwner = env.get('submoduleOwner');
let submoduleRepo = env.get('submoduleRepo');
let submoduleBranch = env.get('submoduleBranch');
let submodulePath = env.get('submodulePath');

let parentSha = api.run('github.get_branch_for_repo', {owner, repo, branch})[0].commit.sha;
let submoduleSha = api.run('github.get_branch_for_repo', {owner: submoduleOwner, repo: submoduleRepo, branch: submoduleBranch})[0].commit.sha;

let treeSha = api.run('github.create_git_tree', {
owner: owner,
repo: repo,
$body: {
"base_tree": parentSha,
"tree": [
{
"path": submodulePath,
"mode": "160000",
"type": "commit",
"sha": submoduleSha
}
]
}
})[0]

let commitSha = api.run('github.create_git_commit', {
owner: owner,
repo: repo,
$body: {
"message": `Auto-update submodule ${submoduleOwner}/${submoduleRepo} to ${submoduleSha}`,
"tree": treeSha.sha,
"parents": [parentSha]
}
})[0]

let editSha = api.run('github.edit_git_ref', {
owner: owner,
ref: `heads/${branch}`,
repo: repo,
$body: {
"sha": commitSha.sha
}
})
return editSha;
}

I wrapped this in a webhook, configured GitHub to call it, and voila!

You can use this webhook as-is, or tweak it for your own needs. Here's how:

And that's it! Now commits to your submodule repository will trigger this webhook to update the parent repository. For more samples, check out some other apps.