Skip to content

Instantly share code, notes, and snippets.

@nickshanks
Forked from jpwatts/xcode-git-version.sh
Last active January 1, 2023 01:16
Show Gist options
  • Save nickshanks/9048830 to your computer and use it in GitHub Desktop.
Save nickshanks/9048830 to your computer and use it in GitHub Desktop.
#!/bin/bash
# This script automatically sets the version and short version string of
# an Xcode project from the Git repository containing the project.
#
# To use this script in Xcode, add the script's path to a "Run Script" build
# phase for your application target.
set -o errexit
set -o nounset
# First, check for git in $PATH
hash git 2>/dev/null || { echo >&2 "Git required, not installed. Aborting build number update script."; exit 0; }
# Alternatively, we could use Xcode's copy of the Git binary,
# but old Xcodes don't have this.
#GIT=$(xcrun -find git)
# Run Script build phases that operate on product files of the target that defines them should use the value of this build setting [TARGET_BUILD_DIR]. But Run Script build phases that operate on product files of other targets should use “BUILT_PRODUCTS_DIR” instead.
INFO_PLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
# Build version (closest-tag-or-branch "-" commits-since-tag "-" short-hash dirty-flag)
BUILD_VERSION=$(git describe --tags --always --dirty=+)
# Use the latest tag for short version (expected tag format "n[.n[.n]]")
LATEST_TAG=$(git describe --tags --abbrev=0)
COMMIT_COUNT_SINCE_TAG=$(git rev-list --count ${LATEST_TAG}..)
if [ $LATEST_TAG = "start" ]
then LATEST_TAG=0
fi
if [ $COMMIT_COUNT_SINCE_TAG = 0 ]; then
SHORT_VERSION="$LATEST_TAG"
else
# increment final digit of tag and append "d" + commit-count-since-tag
# e.g. commit after 1.0 is 1.1d1, commit after 1.0.0 is 1.0.1d1
# this is the bit that requires /bin/bash
OLD_IFS=$IFS
IFS="."
VERSION_PARTS=($LATEST_TAG)
LAST_PART=$((${#VERSION_PARTS[@]}-1))
VERSION_PARTS[$LAST_PART]=$((${VERSION_PARTS[${LAST_PART}]}+1))
SHORT_VERSION="${VERSION_PARTS[*]}d${COMMIT_COUNT_SINCE_TAG}"
IFS=$OLD_IFS
fi
# Bundle version (commits-on-master[-until-branch "." commits-on-branch])
# Assumes that two release branches will not diverge from the same commit on master.
if [ $(git rev-parse --abbrev-ref HEAD) = "master" ]; then
MASTER_COMMIT_COUNT=$(git rev-list --count HEAD)
BRANCH_COMMIT_COUNT=0
BUNDLE_VERSION="$MASTER_COMMIT_COUNT"
else
MASTER_COMMIT_COUNT=$(git rev-list --count $(git rev-list master.. | tail -n 1)^)
BRANCH_COMMIT_COUNT=$(git rev-list --count master..)
if [ $BRANCH_COMMIT_COUNT = 0 ]
then BUNDLE_VERSION="$MASTER_COMMIT_COUNT"
else BUNDLE_VERSION="${MASTER_COMMIT_COUNT}.${BRANCH_COMMIT_COUNT}"
fi
fi
defaults write "$INFO_PLIST" CFBundleBuildVersion "$BUILD_VERSION"
defaults write "$INFO_PLIST" CFBundleShortVersionString "$SHORT_VERSION"
defaults write "$INFO_PLIST" CFBundleVersion "$BUNDLE_VERSION"
# For debugging:
#echo "BUILD VERSION: $BUILD_VERSION"
#echo "LATEST_TAG: $LATEST_TAG"
#echo "COMMIT_COUNT_SINCE_TAG: $COMMIT_COUNT_SINCE_TAG"
#echo "SHORT VERSION: $SHORT_VERSION"
#echo "MASTER_COMMIT_COUNT: $MASTER_COMMIT_COUNT"
#echo "BRANCH_COMMIT_COUNT: $BRANCH_COMMIT_COUNT"
#echo "BUNDLE_VERSION: $BUNDLE_VERSION"
@Harold-D
Copy link

Might I suggest in stead of doing this:

defaults write "$INFO_PLIST" CFBundleBuildVersion "$BUILD_VERSION"
defaults write "$INFO_PLIST" CFBundleShortVersionString "$SHORT_VERSION"
defaults write "$INFO_PLIST" CFBundleVersion "$BUNDLE_VERSION"

You do this:

/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $SHORT_VERSION" "$INFOPLIST_FILE"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUNDLE_VERSION" "$INFOPLIST_FILE"

I have found that using "defaults write" only your created build gets the correct versioning information, but your projects info.plist file is not updated and thus the correct versioning is not shown in your project's target "general" tab.

Using PlistBuddy, you directly set your projects info.plist attributes. So the correct information is also shown in your "general" tab and, IF you let the versioning script run before the "Copy Bundle Resources" phase, the projects info.plist file is read and is put into the build.

Now, one question, PlistBuddy fails on the CFBundleBuildVersion key, and, at least in my project, this key doesn't appear in my info.plist file. Why are you setting this?

@karlvr
Copy link

karlvr commented May 8, 2014

I made a couple of little tweaks that I think are interesting:
https://gist.github.com/karlvr/c93a98d7000ecb163895

@johnboiles
Copy link

Note that when using this in a new project make sure to create a Git tag (git tag 0.0.0 perhaps) or the script will crash on git describe --tags --abbrev=0 with error fatal: No names found, cannot describe anything.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment