Avatar Description
Rubi
  • Jul 17, 2024
  • 5 min read

Comprehensive Guide on Golang Self-upgrading binary & Mac Package.

You might think a binary upgrade is just replacing the currently running binary with a new one from the internet, there are a few additional steps to consider.

Self-upgrading binaries did require some careful handling, or you might makes your software broken at all, especially when dealing with different operating systems like macOS. Let's explore the process of implementing this feature in Go.

gopher construction worker

Key Considerations

Running Process: The binary that's currently running can't be replaced while it's executing. We need to work around this.

File Permissions: The new binary should maintain the same permissions as the original.

Atomicity: Ideally, the upgrade process should either fully complete or not happen at all.

Platform Differences: Different operating systems may require slightly different approaches.

Verification: It's good practice to verify the new binary before using it.

Signature Verification: macOS prevent you to carelessly replacing your binary and leave invalid overall package signature.

Implementation Steps

1. Setting Up your Updates API Server

An essential part of the self-updating process is having a server that can inform your application about available updates and provide the necessary files. It's kinda goes like this:

Now from the code above, basically the current running program sends information to the server, and the service decide whether there is an update or not.

2. Downloading The New Binary

Now the client is basically sending information about their os and architecture and store the file on OS's temporary folder.

3. Verifying the Binary hash

Now this is where the interesting part comes up, some people on some country might getting your update URL blocked, and if there is no hash verification, you might end up downloading a html file, or even some random response modified by user's ISP (I had this one before)

Now after you stored the downloaded file on temporary folder, verify the file's hash (we are using SHA256) like this:

Additional step: test your binary with cmd call

After doing hash verification with the binary, you might want to test your updated binary if it's actually functional on user's machine, your application might take an argument to basically output some text and the current running binary verify the output of the text, if this fails, don't do binary upgrade which the new software will probably broken after being moved to current binary location.

5. Performing the Binary Upgrade

For macOS, you are required to shipping the software as a Mac Package, because the software are required to be signed and notarized, and you can't just replace your Go binary inside Contents/MacOS because it will makes your Mac Package broken, and your user will unable to run your app at all.

macOS app package is basically a folder, the way I make the folder to be downloadable is by compressing the folder as a .zip file.

for a Linux and Windows software, you can basically just replace the single Go binary because there are no need for signature verification like macOS did

basically what you need to do is to move the downloaded binary update to currently running binary path

there is some more step happen with macOS where the downloaded file is a .ZIP file and you are required to extract the .ZIP file and move it to current application package path which usually located at /Applications/Lokal.app when you detect your current running binary location, it will be locate your Go binary path instead of your mac app's package path, you might want to try something like this:

Boom! Permission Error 😱

Now this are expected because your app location my need some more privilege to do some kind of file or folder updates, at this point, you might need to work on your Manifest-related files, but I found this interesting package called elevate, what this package basically do is invoking a command line with a sudo or Run as Administrator access

wait, your software takes additional argument? what's that for?

what this additional argument basically do is updating current binary location, but with privileged permission, the os.Args[2] is a temporary binary location while the os.Args[3] is current binary location.

Wrap Up

Implementing self-upgrading binaries in Go isn't overly complex at the end, but it does require attention to detail. Make sure to test your upgrade process thoroughly and include proper error handling.

Golang Self-upgrade Self-update

Was this post helpful?

Related articles