I am currently trying to update a submodule's commit id automatically when another project has certain files changed. I have a .net webhook and I'm using the octokit.net library.
I can see in the github documentation (https://developer.github.com/v3/git/trees/#create-a-tree) there is a submodule option when creating a new tree that allows you to add a commit and path, but I can't get it to work. Octokit also has a submodule type for the NewTreeItem/TreeItem objects but no examples or documentation.
My current code is here - currently I'm passing the commit sha in as the sha parameter, but I can see this is wrong, I need to create a commit on that repo and use that sha, I just don't know how to do that before the tree is created and there isn't any documentation that I can find:
public static async Task UpdateSubmoduleInBranch(string repo, string branchName, string submodulePath, string sha, string commitComment, GitHubClient github = null)
{
//hoping this will update the sha of a submodule
// url encode branch name for github operations
branchName = HttpUtility.UrlEncode(branchName);
if (github == null) github = GetClient();
var repoId = (await github.Repository.Get(Settings.GitHub.OrgName, repo)).Id;
RepositoriesClient rClient = new RepositoriesClient(new ApiConnection(github.Connection));
var branch = await rClient.Branch.Get(repoId, branchName);
var tree = await github.Git.Tree.Get(repoId, branchName);
var newTree = new NewTree { BaseTree = tree.Sha };
newTree.Tree.Add(new NewTreeItem
{
Mode = Octokit.FileMode.Submodule,
Path = submodulePath,
Type = TreeType.Commit,
Sha = sha
});
var createdTree = await github.Git.Tree.Create(repoId, newTree);
var newCommit = new NewCommit(commitComment, createdTree.Sha, new[] { branch.Commit.Sha });
newCommit.Committer = Settings.GitHub.Committer;
var createdCommit = await github.Git.Commit.Create(Settings.GitHub.OrgName, Settings.GitHub.AppName, newCommit);
var updateRef = new ReferenceUpdate(createdCommit.Sha, false);
await github.Git.Reference.Update(repoId, "heads/" + branchName, updateRef);
}
Edit
In case anyone else is looking for this, I resolved the issue - the octokit api does not support this action, even if it looks like it does.
In addition to the PatchAsync method found at PATCH Async requests with Windows.Web.Http.HttpClient class, the following code worked for me:
public static async Task UpdateSubmoduleInBranch(string repo, string branchName, string submodulePath, string sha, string commitComment)
{
using (var client = new HttpClient())
{
try
{
//these headers authenticate with github, useragent is required.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Settings.GitHub.AuthToken);
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("mathspathway-environment-manager", "v1.0"));
var committer = Settings.GitHub.Committer;
//get the branch, and collect the sha of the current commit
var branchResponse = await client.GetAsync($"{Settings.GitHub.ApiUrl}/repos/{Settings.GitHub.OrgName}/{repo}/branches/{branchName}");
JToken branchResult = JToken.Parse(await branchResponse.Content.ReadAsStringAsync());
var currentCommitSha = branchResult["commit"].Value<string>("sha");
//create the new tree, with the mode of 160000 (submodule mode) and type of commit, and the sha of the other
//repository's commit that you want to update the submodule to, and the base tree of the current commit on this repo
var newTreeObj = new
{
base_tree = currentCommitSha,
tree = new List<Object> { new { path = submodulePath, mode= "160000", type = "commit", sha = sha}
}
};
HttpContent treeHttpContent = new StringContent(JsonConvert.SerializeObject(newTreeObj));
var treeResponse = await client.PostAsync($"{Settings.GitHub.ApiUrl}/repos/{Settings.GitHub.OrgName}/{repo}/git/trees", treeHttpContent);
var treeResponseContent = JToken.Parse(await treeResponse.Content.ReadAsStringAsync());
var treeSha = treeResponseContent.Value<string>("sha");
//Create a new commit based on the tree we just created, with the parent of the current commit on the branch
var newCommitObj = new
{
message = commitComment,
author = new { name = committer.Name, email = committer.Email, date = committer.Date },
parents = new[] { currentCommitSha },
tree = treeSha
};
HttpContent newCommitContent = new StringContent(JsonConvert.SerializeObject(newCommitObj));
var commitResponse = await client.PostAsync($"{Settings.GitHub.ApiUrl}/repos/{Settings.GitHub.OrgName}/{repo}/git/commits", newCommitContent);
var commitResponseContent = JToken.Parse(await commitResponse.Content.ReadAsStringAsync());
var commitSha = commitResponseContent.Value<string>("sha");
//create an update reference object, and update the branch's head commit reference to the new commit
var updateRefObject = new { sha = commitSha, force = false };
HttpContent updateRefContent = new StringContent(JsonConvert.SerializeObject(updateRefObject));
var updateRefResponse = await client.PatchAsync($"{Settings.GitHub.ApiUrl}/repos/{Settings.GitHub.OrgName}/{repo}/git/refs/heads/{branchName}", updateRefContent);
} catch (Exception ex)
{
Debug.WriteLine($"Error occurred updating submodule: {ex.Message}{Environment.NewLine}{Environment.NewLine}{ex.StackTrace}");
}
}
}
new NewCommit
line, I used the overload that takes a single SHA as the last param instead of the enumerable one. Gist – patridge