DEV Community

Jonathan Wilson
Jonathan Wilson

Posted on

Version bump your Xcode project with Github's API

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

API Reference

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

API Reference

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
},
Enter fullscreen mode Exit fullscreen mode

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

API Reference

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"
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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

API Reference

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");
};
Enter fullscreen mode Exit fullscreen mode

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
},
Enter fullscreen mode Exit fullscreen mode

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

API Reference

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
},
Enter fullscreen mode Exit fullscreen mode

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)