Version bump your Xcode project with Github's API
Tags: ios, ci, git, devops
Published: 2019-11-07 21:28:22

---
title: Version bump your Xcode project with Github's API
published: true
description: The process of updating the version numbers of your Xcode project using the Github Rest API 
tags: #ios #ci #git #devops
---

This post is going to describe the process of updating the version
numbers of your Xcode project using the [Github Rest API](https://developer.github.com/v3/).

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](https://help.github.com/en/github/getting-started-with-github/github-glossary#commit) 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](https://developer.github.com/v3/#authentication).


## Step 1: Get the SHA identifier for a branch

[API Reference](https://developer.github.com/v3/git/refs/#get-a-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](https://developer.github.com/v3/git/refs/#create-a-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
},
```

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](https://developer.github.com/v3/repos/contents/#get-contents)

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

[API Reference](https://developer.github.com/v3/repos/contents/#create-or-update-a-file)


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](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html) 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

[API Reference](https://developer.github.com/v3/pulls/#create-a-pull-request)

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.