Skip to content

wislon/ci-build-revision-utils

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ci-build-revision-utils

Continuous Integration :: Simple Build-Revision CLI utilities

This is a couple of .NET exes I built over a couple of hours to automate the incrementing of build revision numbers in

  • Xamarin/Android AndroidManifest.xml files
  • .NET AssemblyInfo related files

Why bother?

Xamarin.Android apps contain both kinds of version resources. And I got tired of incrementing one and forgetting to do the others. And if it's friction, or a pain point for me, I prefer to automate it away if I can.

So this is the stupidest thing that actually works.

All these utilities do is load and parse a given file name looking for specific string/regex matches. If they are able to locate the requested bits of information, they'll auto-increment the build revision #, and write the file back out again.

And that is all they do. Their primary function is simply to operate as part of a custom build/build-tool chain, so it's up to the developer to integrate them with their CI process (examples below).

Yes, I know, TeamCity and some other CI toolkits can do this too. I can't use TeamCity in a couple of current projects, which is why I slapped these little guys together.

Application versions are displayed as a set of numbers indicating

major_version.minor_version.build_#.build_revision

This is usually displayed in the format: 1.0.1.2 or 1.0.2.0-alpha or something similar. Normally what happens when you (re)build an app, these would be incremented automatically for you. Some "increments" aren't actually increments. They could be git commit hashes, timestamps, you name it.

In the case of these utilities, all they do is try and locate something that looks like a set of numbers like this, and if they find it, they'll increment the build revision # (the last number in the quartet).

For example, if the existing version # is 1.2.3.4, then running these utilities will simply update that last digit to 1.2.3.5.

Yes, they could do so much more, and I'm sure you've got a million ideas about how yours would be 10x more awesome. But this solved an immediate problem I had.

AndroidManifestUtil (for AndroidManifest.xml files)

This updates the AndroidManifest.xml file commonly found in Android and Xamarin.Android projects.

Normally when you build your Android app, you have to provide two version values, a version code (versionCode in the manifest file) and a version name (versionName in the manifest file).

The versionCode value is a single integer used by the Google Play store to determine whether the app is a newer version than one it already has.

The versionName is a set of numbers (and perhaps other identifiers), as described above, which you can use for "proper" version tracking in your app; maybe for analytics or crash reporting or something else).

AndroidManifestUtil extracts the versionName attribute from the manifest xml node, tries to parse it to extract the 4 numbers, increments the last one (the build revision), and writes it back to the file.

It will also extract the versionCode attribute from the manifest xml node, increment it, and writes it back to the file.

The Google Play store depends on this versionCode attribute to know that you've updated your app in the store, but the numbers don't need to be sequential. As long as your new version has a higher versionCode value than the previous one, that's cool.

If you've got some funky version numbering you do (e.g. '1.0.rc-02.02Jan2014'), then you're on your own.

Usage

Theres's a test AndroidManifest.xml file in the project folder which looks something like:

<?xml version="1.0" encoding="utf-8"?>
<!-- dummy xml file for /manifest/android:versionName attribute revision # increment testing -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
android:installLocation="auto" package="io.wislon.testApp" 
android:versionCode="1" 
android:versionName="1.0.1.1">
  <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="19" />
  <application android:icon="@drawable/Icon" android:label="testApp">
  </application>
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.INTERNET" />
</manifest>

As you can see, the versionName is set to 1.0.1.1.

Run the utility from the command line (or script):

AndroidManifestUtil.exe -filename=.\AndroidManifest.xml [-increment-build-number]

If you use the -increment-build-number cli argument, it'll increment the BUILD number (the 3rd set in the quartet) and RESET the revision to '0'.

It'll give you some basic info about what it's doing, or if it's got a problem:

Loading D:\Test\AndroidManifest.xml
Loaded D:\Test\AndroidManifest.xml
Loading manifest node...
Getting manifest attributes...
Found android:versionCode attribute: 1
Updating android:versionName attribute to 2
Writing out updated manifest file: D:\Test\AndroidManifest.xml
Done
Loading D:\Test\AndroidManifest.xml
Loaded D:\Test\AndroidManifest.xml
Loading manifest node...
Getting manifest attributes...
Found android:versionName attribute: 1.0.1.1
Current build revision: '1.0.1.1'
New build revision: 1.0.1.2
Updating android:versionName attribute to 1.0.1.2
Writing out updated manifest file: D:\Test\AndroidManifest.xml
Done

...and now it looks like:

<?xml version="1.0" encoding="utf-8"?>
<!-- dummy xml file for /manifest/android:versionName attribute revision # increment testing -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
android:installLocation="auto" package="io.wislon.testApp" 
android:versionCode="2" 
android:versionName="1.0.1.2">
<snip />
</manifest>

Note: there is NO relationship between the build versionName and the versionCode, tho most people like to keep them 'in step' with each other, neither of them is derived from the other

PListUtil (for Info.plist files)

This updates the Info.plist file commonly found in iOS and Xamarin.iOS projects.

Normally when you build your iOS app, you have to provide a version string (CFBundleVersion in the plist file).

The CFBundleVersion is a set of numbers (and perhaps other identifiers), as described above, which you can use for "proper" version tracking in your app; maybe for analytics or crash reporting or something else).

PlistUtil extracts the CFBundleVersion attribute from the plist file, tries to parse it to extract the version numbers, increments the last one (the revision or 'patch' version), and writes it back to the file.

If you use the -increment-build-number cli argument, it'll increment the BUILD number (the 3rd set in the quartet) and RESET the revision to '0'.

Usage

Theres's a test Info.plist file in the project folder which looks something like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"[]>
<plist version="1.0">
  <dict>
    ...
    <key>CFBundleVersion</key>
    <string>1.0.1.1</string>
    ...
  </dict>
</plist>

As you can see, the CFBundleVersion is set to 1.0.1.0.

Run the utility from the command line (or script):

PlistUtil.exe -filename="Info.plist" [-increment-build-number]

It'll give you some basic info about what it's doing, or if it's got a problem:

Loading: Info.plist
New build number: 2
Current CFBundleShortVersionString: '1.0.1.1'
New     CFBundleShortVersionString: '1.0.1.2'
Saving: Info.plist
Done

...and now it looks like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"[]>
<plist version="1.0">
  <dict>
    ...
    <key>CFBundleVersion</key>
    <string>1.0.1.2</string>
    ...
  </dict>
</plist>

AssemblyInfoUtil (for updating .NET AssemblyInfo-related files)

This updates the AssemblyVersion and AssemblyFileVersion values commonly found in AssemblyInfo.cs (or global/shared versions of them) in .NET projects.

It simply opens the file you specify (as text), and searches for for the quartet of numbers. If it finds one (or both), it will parse out the build revision #, increment it, and then write it back to the file.

Usage

Similarly to the AndroidManifestUtil, simply point the -filename parameter at the file containing the version strings you're interested in updating:

Run the utility from the command line (or script):

AssemblyInfoUtil.exe -filename=.\GlobalVersionInfo.cs.txt [-increment-build-number]

(this test file a '.txt' extension because Visual Studio kept trying to compile it into the application, and then couldn't write to it when running the debugger :)). This won't be a problem when you point it at a 'real' file from a command line.

This file will look something like:

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// sample text file for revision # increment update testing. 
// Application just reads and writes text, it doesn't 
// care about the file extension

// You can specify all the values or you can default the Build and Revision 
// Numbers by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.1.1")]
[assembly: AssemblyFileVersion("1.0.1.1")]
D:\test\AssemblyInfoUtil.exe -filename=.\GlobalVersionInfo.cs.txt
Loading .\GlobalVersionInfo.cs.txt
Loaded .\GlobalVersionInfo.cs.txt
Looking for AssemblyVersion line...
Current build revision: '1.0.1.1'
New build revision: 1.0.1.2
Looking for AssemblyFileVersion line...
Current build revision: '1.0.1.1'
New build revision: 1.0.1.2
Revision updated. Writing out new .\GlobalVersionInfo.cs.txt
Done

With the result:

[assembly: AssemblyVersion("1.0.1.2")]
[assembly: AssemblyFileVersion("1.0.1.2")]

And that's pretty much it.

Sure, they can be tweaked to add flags for turn this on, but don't do this, or ignore this thing and do that instead. But this was the simplest thing that actually worked and did exactly what I need it to do.

Build Loops

Be careful when you integrate this kind of thing with CI servers or services (like TeamCity) that push and pull code changes and rebuild on new changes that appear in your source control system.

If your build script increments the number (as it's supposed to), and then pushes the update back to source control, and your source control triggers a build because of the new code change, you'll end up in a build/commit/push/trigger/pull/increment/build loop.

CC image of http://en.wikipedia.org/wiki/File:Tesseract.gif

What is this? I can't even...

This is not your build server's fault. Nor your source control's. But it's not always an easy fix. One of them has to be told how to ignore the other in certain cases, so it doesn't start the loop.

...and in the spirit of "I needed this now, and since I'm publishing it for me, I may as well put it up for other folks", Andrew Harcourt (@uglybugger) provides a possible solution over at https://teamcity-github-filter.azurewebsites.net/).

License

The utils in this repo released under the free-for-all MIT License, so if you want to copy it and do better stuff with it, you go right ahead! :)

About

CI build-revision cli utilities

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages