This post is going to describe the process of updating the version
numbers of your Xcode project using the Github Rest API.
In this approach I am creating an automated pull request (PR) with the changes. This guide expects the project to be utilising the plist file
to maintain the version and build numbers.
There are five steps to complete this task:
• Get the SHA for the head of the branch you would like to apply the changes
• Create a new branch from that SHA
• Retrieve the current contents of the plist file
• Update that file with the new version strings
• Create a PR with these changes
I am not going to cover the authentication with Github, it is very well documented.
Step 1: Get the SHA identifier for a branch
Perform a get request to:
https://api.github.com/repos/:owner/:repo/git/refs/heads/:branchName
Substituting variables for your project. Then pull out the SHA variable from the JSON decoded response:
json.object.sha
This is basically just a reference to the last commit made in the branch you selected. We can let the API know that our new branch should originate from here.
Step 2: Create a new branch to contain the updates
Post a JSON object containing the name of the new branch in the format below, and supply the SHA retrieved in Step 1. The format of the ref
content is important, if it doesn't start with refs
and have at least two slashes, it will be rejected.
body: {
ref: "refs/heads/" + branchName,
sha: sourceSha
},
The endpoint in this case is:
https://api.github.com/repos/:owner/:repo/git/refs/
Step 3: Get the original contents of the Plist file
We need to get the original contents of the plist so that we can apply the specific changes.
Get the contents from this URL:
https://api.github.com/repos/:owner/:repo/contents/:pathToPlist
The pathToPlist
parameter is specific to your project it can be found on the Github website. In my case it was:
https://api.github.com/repos/:owner/:repo/contents/SupportingFiles/Project-Info.plist
You may need to pass in a parameter if you need to get the contents from a branch other than your main one:
ref: "develop"
The contents of the response are base64
encoded, most languages have a standard function for decrypting this. In my case I am using Javascript and it looked like this:
Buffer.from(json.content, "base64").toString()
You also need to retrieve the blob SHA that is returned here, my understanding is this is basically just a hash of the file used for integrity checks rather than the one used earlier as a reference to a commit.
Step 4: Apply the changes to the plist file and Put to API
First of all we need to update the file contents, this is not going to be the best function you have seen but it gets the job done (Also written in Javascript... by a Swift dev):
const updatePlistContentVersions = function(content, shortVersionString) {
const splitContent = content.split(/\n/);
splitContent.forEach(function(value, i) {
if (value === "\t<key>CFBundleShortVersionString</key>") {
splitContent[i + 1] = "\t<string>" + shortVersionString + "</string>";
}
if (value === "\t<key>CFBundleVersion</key>") {
splitContent[i + 1] =
"\t<string>" + shortVersionString + ".$(BUILD_NUMBER)</string>";
}
});
return splitContent.join("\n");
};
The structure of a plist file is described here It is XML based and there are plenty of guides on parsing it, In my case I went for the simple approach of splitting by new lines, then looking for the keys I wanted to change.
There are two keys we are updating here:
CFBundleShortVersionString
: Which is the main version indicator.
CFBundleVersion
: Describes the build of a given version.
After finding them I literally just replace the entire contents of the next line with a new one containing the updated values. Following that join the array of lines back into a single string.
Finally you will need to re-encode the string back into base64
. In Javascript it looks like this:
Buffer.from(updatedPlistContent).toString("base64")
.
To apply the changes to the branch created earlier we will create a Put request to API URL of the plist, which is the same as Step 3. The body of this request should look like this:
body: {
content: updatedPlistContent,
sha: blobSha,
branch: branchName,
message: "Update version to " + shortVersionString
},
• content
Which is the base64
encoded version of the contents retrieved earlier containing our amendments.
• sha
which is the blob SHA from Step 3.
• branch
The branch created in Step 2, which is where these changes will be applied.
• message
The commit message for these changes. This is basically just a simple commit but through an API.
Step 5: Create a Pull Request with all the changes
Another Post request, now to:
https://api.github.com/repos/:owner/:repo/pulls
The contents of the body of this request is:
body: {
title: title,
body: description,
head: sourceBranch,
base: baseBranch
},
• title
The headline of the PR, best to give a clear description here, maybe even call out a bot created it.
• body
The description of the PR.
• head
The branch the changes are coming from, in our case its Step 2.
• base
The destination of these changes, probably your main development branch.
Conclusion
We have created a script that will apply a small change to a Github project. The use cases for this are pretty vast. In my own case I attached them to a single Slack bot command:
@Bot Update the version number to x.x.x
.
This command is actualy apart of a set of similar scripts that enable the automation of a release. The Github API is huge and very well documented, you can do anything you can think of so I encourage you to have a look.
Have a think about automating: tagging a release, creating branches, and highlighting changes in branches. Slack is a great way to
externalise them to the greater team too. Let the product team do all the releasing etc... if you dare.
Top comments (0)