How to Track Decoupled Branding Repositories in Builds Without Git Submodules
• 3 minute read
There are multiple options how to separate a whitelabel app project from brand customizations. One is decoupling the individual repositories completely. There is a solution to still keep track on which customization revision was used.
A topic which I am already drafting an article about is how to organize an Xcode project with a whitelabel app and its customizations. In this article I want to focus on a single detail of one such option.
A typical constraint I have to work with is that no customer specific information is allowed in the core repository. At our company we have achieved a setup in which the core project is a Git submodule of a brand specific customizations repository. I never was content with this solution as it causes repository management overhead and some manual Git juggling. After thinking about it for a longer time I developed an idea which will be implemented in a fresh project where our product basically is written from scratch.
The setup is inverted. There is the whitelabel app project with includes a whitelabel default customization. Additional customizations are integrated by simply cloning other Git repositories into the dedicated “Customizations” folder. Bespoke customizations are modified copies of the default customization. To achieve the confidental separation of whitelabel app and brand specific customizations the additional customizations are independent Git repositories which also are ignored by the superordinate main project.
When the app is built there is no atomic and straight forward way to determine which customization and what revision of it has been used. More often than enough this is a question raised when app builds do not behave as expected during quality assurance and testing.
The possibilities of the Xcode build system come in handy. I created a build post-action in the main app scheme which executes a shell script that automates the resolution and storage of this information.
The referenced script does not have to be outsourced into a dedicated file. The code can also be included directly in the post-action. I personally came to the conclusion it is easier to edit scripts this way and more clear to read in version control history.
#!/bin/sh RECEIPT_FILE_PATH="$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/Receipt.plist" echo "Supposed receipt file: $RECEIPT_FILE_PATH" echo '<?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> </dict> </plist>' > "$RECEIPT_FILE_PATH" cd "$WORKSPACE_PATH/../../Customizations/Current" CUSTOMIZATION_REVISION=$(git rev-parse HEAD) echo "Resolved customization revision: $CUSTOMIZATION_REVISION" /usr/libexec/PlistBuddy -c "Add :CustomizationRevision string '$CUSTOMIZATION_REVISION'" "$RECEIPT_FILE_PATH"
This script creates a property list in the built app bundle which currently holds nothing more than the commit hash of the customization repository used. The customization in use is resolved by the change into the “Customizations/Current” directory which actually is a symbolic link on a specific customization folder. More on that in a future article. In case the default customization is used, the hash equals the main projects latest commit hash.
This way every build contains a clear reference to the customization commit used. I consider this safe to ship because it does not expose any sensitive information. Of course this could be extended with additional information.