Scripts

ECLogging contains a number of scripts used by ECLogging itself or by other EC Frameworks.

Some of these aren’t really intended for public use, but they’re all documented here just in case.

backup-submodules.sh:

Pushes all branches of all submodules in the current project to a remote called “backup”.

Used internally.

#!/usr/bin/env bash

## Pushes all branches of all submodules in the current project to a remote called "backup".
##
## Used internally.

git submodule foreach 'git push backup --all'

extract-script-docs.sh:

Extract any comments prefixed with “” (like this one) in a file, and write them out to a -template.md for inclusion in an appledoc documentation bundle.

#!/usr/bin/env bash

## Extract any comments prefixed with "##" (like this one) in a file, and write them out to
## a -template.md for inclusion in an appledoc documentation bundle.

output=$1
mkdir -p "$output"
echo "Output directory: $output"

shift
prefix=$1
echo "Prefix file: $prefix"

index=`cat "$prefix"`

while :; do
    shift
    file=$1
    if [[ "$file" == "" ]]; then
        break
    fi


    comments=`grep "^##" "$file"`

    base=`basename "$file"`
    name=${base%.*}

    index=`echo "$index"; echo "## $base:"; echo "${comments//##/}"; echo ""; echo ""; awk '{print "    "$0}' $file; echo ""`
#    echo "" >> "$output/$base-template.markdown"
#   awk '{print "    "$0}' $file >> "$output/$base-template.markdown"
#    echo "${comments//##/}" >> "$output/$base-template.markdown"
#    echo "" >> "$output/$base-template.markdown"
#    echo "" >> "$output/$base-template.markdown"
#   index=`echo "$index"; echo "- $base"`
#    index=`echo "$index"; cat "$output/$base-template.markdown"`
done

echo "$index" > "$output/Scripts-template.markdown"

merge-latest-submodules.sh:

Runs the merge-latest.sh script for each submodule in the current project.

Used internally.

#!/usr/bin/env bash

## Runs the merge-latest.sh script for each submodule in the current project.
##
## Used internally.

base=`dirname $0`
pushd "$base" > /dev/null
full="$PWD"
popd > /dev/null

git submodule foreach "\"$full\"/merge-latest.sh"

merge-latest.sh:

This script is an attempt to automate picking up the latest version of the “develop” branch for a module, given that it might be on a detatch HEAD at the time.

It performs the following steps:

  • rebase on the local develop branch
  • save this to a temporary branch
  • switch to the local develop branch
  • merge in the temporary branch – this should be a fast forward
  • remove the temporary branch
  • rebase on the remote “develop” from origin
  • push the resulting changed branch back to origin

    #!/usr/bin/env bash

    ## This script is an attempt to automate picking up the latest version of the “develop” branch for a module, given that it might be on a detatch HEAD at the time. ## ## It performs the following steps: ## ## – rebase on the local develop branch ## – save this to a temporary branch ## – switch to the local develop branch ## – merge in the temporary branch – this should be a fast forward ## – remove the temporary branch ## – rebase on the remote “develop” from origin ## – push the resulting changed branch back to origin

    check() { if [[ $1 != 0 ]]; then echo “failed: $2” exit $1 fi }

    status=git status --porcelain

    if [[ “$status” != “” ]]; then echo “You have local changes. Commit them first.” exit 1 fi

    # we may start on something that isn’t the develop branch # possibly a detached HEAD

    # try to apply any changes on top of our local develop

    git rebase develop check $? “rebasing on develop”

    # now fast forward develop to the merged place

    git checkout -b develop-temp check $? “making temp branch”

    git checkout develop check $? “switching back to develop”

    git merge develop-temp check $? “merging local changes”

    git branch -d develop-temp check $? “removing temp branch”

    # we should now be on a local develop branch incorporating any local changes echo fetching latest revisions git fetch

    # try to rebase again on top of any remote changes

    git rebase check $? “rebasing on origin/develop”

    # if that worked, push back the merged version

    git push check $? “pushing”

package-pseudo-framework.sh:

This script is used in iOS targets that are packaged up as “pseudo” frameworks.

The script is called from a Run Script phase, like this:

“${ECLOGGING_SCRIPTS_PATH}/package-pseudo-framework.sh”

It performs various linking and copying operations to lay out the framework bundle correctly.

#!/usr/bin/env bash

## This script is used in iOS targets that are packaged up as "pseudo" frameworks.
##
## The script is called from a Run Script phase, like this:
##
## "${ECLOGGING_SCRIPTS_PATH}/package-pseudo-framework.sh"
##
## It performs various linking and copying operations to lay out the framework bundle correctly.

FRAMEWORK_ROOT="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}"

mkdir -p "${FRAMEWORK_ROOT}/Versions"
/bin/ln -sfh A "${FRAMEWORK_ROOT}/Versions/Current"
/bin/ln -sfh Versions/Current/Headers "${FRAMEWORK_ROOT}/Headers"
/bin/ln -sfh Versions/Current/Resources "${FRAMEWORK_ROOT}/Resources"
/bin/ln -sfh "Versions/Current/${PRODUCT_NAME}" "${FRAMEWORK_ROOT}/${PRODUCT_NAME}"

pull-latest-submodules.sh:

Runs the merge-latest.sh script for each submodule in the current project.

Used internally.

#!/usr/bin/env bash

## Runs the merge-latest.sh script for each submodule in the current project.
##
## Used internally.

base=`dirname $0`
pushd "$base" > /dev/null
full="$PWD"
popd > /dev/null

git submodule foreach "git pull --ff-only"

sign-bundled.sh:

Re-sign every plugin and framework using the bundle id and code signing identity of the target

You can use this script in a Run Script phase to ensure that all plugins and frameworks are signed consistently.

BUNDLEID=`/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" ${INFOPLIST_FILE}`

## Re-sign every plugin and framework using the bundle id and code signing identity of the target
##
## You can use this script in a Run Script phase to ensure that all plugins and frameworks are signed consistently.

echo "Signing PlugIns"
for f in "${CODESIGNING_FOLDER_PATH}/Contents/PlugIns/"*
do
    BUNDLEID=`/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$f/Contents/Info.plist"`
    codesign -f -i ${BUNDLEID} -vv -s "${CODE_SIGN_IDENTITY}" "$f"
done

echo "Signing Frameworks"
for f in "${CODESIGNING_FOLDER_PATH}/Contents/Frameworks/"*
do
    BUNDLEID=`/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$f/Resources/Info.plist"`
    codesign -f -i ${BUNDLEID} -vv -s "${CODE_SIGN_IDENTITY}" "$f"
done

test-common.sh:

#!/usr/bin/env bash

# Common code for test scripts

if [[ $project == "" ]];
then
    echo "Need to define project variable."
    exit 1
fi

echo "Setting up tests for $project"

pushd "$base/.." > /dev/null
build="$PWD/test-build"
ocunit2junit="$PWD/ECUnitTests/Resources/Scripts/ocunit2junit/ocunit2junit.rb"
popd > /dev/null

sym="$build/sym"
obj="$build/obj"

rm -rf "$build"
mkdir -p "$build"

testout="$build/out.log"
testerr="$build/err.log"

#if [[ "$testMac" == "" ]]; then
#    testMac=true
#fi

#if [[ "$testIOS" == "" ]]; then
#    testIOS=true
#if

config="Debug"

report()
{
#    pushd "$build" > /dev/null
    "$ocunit2junit" < "$testout" > /dev/null
    reportdir="$build/reports/$2/$1"
    mkdir -p "$reportdir"
    mv test-reports/* "$reportdir" 2> /dev/null
    rmdir test-reports
#    popd > /dev/null
}

commonbuild()
{
    echo "Building $1 for $3"
    xcodebuild -workspace "$project.xcworkspace" -scheme "$1" -sdk "$3" $4 -config "$config" $2 OBJROOT="$obj" SYMROOT="$sym" > "$testout" 2> "$testerr"
    result=$?
    if [[ $result != 0 ]]; then
        cat "$testerr"
        echo
        echo "** BUILD FAILURES **"
        echo "Build failed for scheme $1"
        exit $result
    fi

    report "$1" "$3"

    failures=`grep failed "$testout"`
    if [[ $failures != "" ]]; then
        echo $failures
        echo
        echo "** UNIT TEST FAILURES **"
        echo "Tests failed for scheme $1"
        exit $result
    fi

}

macbuild()
{
    if $testMac ; then

        commonbuild "$1" "$2" "macosx"

    fi
}

iosbuild()
{
    if $testIOS; then

        if [[ $2 == "test" ]];
        then
            action="build TEST_AFTER_BUILD=YES"
        else
            action=$2
        fi

        commonbuild "$1" "$action" "iphonesimulator" "-arch i386"

    fi
}

iosbuildproject()
{

    if $testIOS; then

        echo Building target $2 of project $1

        cd "$1"
        xcodebuild -project "$1.xcodeproj" -config "$config" -target "$2" -arch i386 -sdk "iphonesimulator" build OBJROOT="$obj" SYMROOT="$sym" > "$testout" 2> "$testerr"
        result=$?
        cd ..
        if [[ $result != 0 ]]; then
            cat "$testerr"
            echo
            echo "** BUILD FAILURES **"
            echo "Build failed for scheme $1"
        exit $result
        fi

    fi

}

iostestproject()
{

    if $testIOS; then

        echo Testing target $2 of project $1

        cd "$1"
        xcodebuild -project "$1.xcodeproj" -config "$config" -target "$2" -arch i386 -sdk "iphonesimulator" build OBJROOT="$obj" SYMROOT="$sym" TEST_AFTER_BUILD=YES > "$testout" 2> "$testerr"
        result=$?
        cd ..
        if [[ $result != 0 ]]; then
            cat "$testerr"
            echo
            echo "** BUILD FAILURES **"
            echo "Build failed for scheme $1"
            exit $result
        fi

        report "$1" "iphonesimulator"

    fi

}

testflight-extract-url.py:

Python script to extract the URL from the json results returned by TestFlight

#!/usr/bin/env python

## Python script to extract the URL from the json results returned by TestFlight

import json
import sys

result = json.load(sys.stdin)
url = result['config_url']

print url

testflight-upload.sh:

Script which uploads the target to Testflight. Use this script as a Post-Action script in the Archive phase of a Scheme.

The API token to use is read from the defaults system. To set it use

 defaults write com.elegantchaos.testflight-upload API_TOKEN <token>

The Team Token is passed in to the script as a first parameter, since it varies with each project. The second parameter should be the name of a TestFlight distribution list. All users in the list will be notified when the target has been uploaded.

#!/bin/bash

## Script which uploads the target to Testflight.
## Use this script as a Post-Action script in the Archive phase of a Scheme.
##
## The API token to use is read from the defaults system. To set it use
##     defaults write com.elegantchaos.testflight-upload API_TOKEN <token>
##
## The Team Token is passed in to the script as a first parameter, since it varies with each project.
## The second parameter should be the name of a TestFlight distribution list. All users in the list will be notified
## when the target has been uploaded.

rm /tmp/upload.log

TMP=/tmp/testflight-upload
LOG="${TMP}/upload.log"
ERROR_LOG="${TMP}/error.log"

rm "${LLOG}"
rm "${ERROR_LOG}"

mkdir -p "$TMP"
echo "Uploading..." > "${LOG}"
echo "" > "${ERROR_LOG}"

GIT=/usr/bin/git

APITOKEN=`defaults read com.elegantchaos.testflight-upload API_TOKEN`
if [[ "${APITOKEN}" == "" ]]; then
    echo "Need to set the TestFlight API token using 'defaults write com.elegantchaos.testflight-upload API_TOKEN <token>'" >> "${ERROR_LOG}"
    open "${ERROR_LOG}"
    exit 1
fi


# team token and distribution list are per-project settings, so should be passed in
TEAMTOKEN="$1"
DISTRIBUTION="$2"

# set this to true to show a confirmation dialog before doing the upload
CONFIRM_MESSAGE=false

# set this to true to use the git log as the default upload message
DEFAULT_MESSAGE_IS_GIT_LOG=true

MESSAGE=""

if $DEFAULT_MESSAGE_IS_GIT_LOG; then
    # use the git log since the last upload as the upload message
    MESSAGE=`cd "$PROJECT_DIR"; $GIT log --oneline testflight-upload..HEAD`
    if [[ $? != 0 ]]; then
        MESSAGE="first upload"
    fi

else
    # default to the last saved message
    if [ -e "${TMP}/upload.txt" ]; then
        MESSAGE=`cat ${TMP}/upload.txt`
    fi
fi

if $CONFIRM_MESSAGE; then
    # use applescript to ask about the upload
    MESSAGE=`osascript -e "tell application id \"com.apple.dt.Xcode\" to text returned of (display dialog \"Upload archive?\" default answer \"$MESSAGE\")"`
    if [[ $? != 0 ]] ; then
        echo "Upload cancelled" >> "${LOG}"
        exit 1
    fi
fi

# archive the last commit message, just in case we want it
echo "$MESSAGE" > "${TMP}/upload.txt"

SCRIPT_DIR=`dirname $0`

# make the ipa
echo "Making $EXECUTABLE_NAME.ipa as ${CODE_SIGN_IDENTITY}" >> "${LOG}"
APP="$ARCHIVE_PRODUCTS_PATH/Applications/$EXECUTABLE_NAME.app"
DSYM="$ARCHIVE_DSYMS_PATH/$EXECUTABLE_NAME.app.dSYM"
IPA="$TMPDIR/$EXECUTABLE_NAME.ipa"
XCROOT=`/usr/bin/xcode-select -print-path`
XCRUN="$XCROOT/usr/bin/xcrun"

echo "$XCRUN" -sdk iphoneos PackageApplication "$APP" -o "$IPA" --sign "${CODE_SIGN_IDENTITY}" --embed "${APP}/${EMBEDDED_PROFILE_NAME}" &> "${TMP}/xcrun.txt"

"$XCRUN" -sdk iphoneos PackageApplication "$APP" -o "$IPA" --sign "${CODE_SIGN_IDENTITY}" --embed "${APP}/${EMBEDDED_PROFILE_NAME}" &> "${TMP}/xcrun.log"

if [[ $? == 0 ]] ; then

        CURLLOG="${TMP}/curl.log"

        echo "Uploading to Test Flight with notes:" >> "${LOG}"
        echo "\"${MESSAGE}\"" >> "${LOG}"
        echo "" >> "${LOG}"
        echo "Distribution list ${DISTRIBUTION} will be mailed." >> "${LOG}"
        echo "" >> "${LOG}"

        zip -q -r "${DSYM}.zip" "${DSYM}"
        rm "$CURLLOG"
        curl http://testflightapp.com/api/builds.json --form file="@${IPA}" --form dsym="@${DSYM}.zip" --form api_token="${APITOKEN}" --form team_token="${TEAMTOKEN}" --form notes="${MESSAGE}" --form notify=True --form distribution_lists="${DISTRIBUTION}" -o "${CURLLOG}"
        CONFIG_URL=`"${SCRIPT_DIR}/testflight-extract-url.py" < "${CURLLOG}"`

        if [[ $? == 0 ]] ; then
            echo "Upload done." >> "${LOG}"
            open "${CONFIG_URL}"

            # update the git tag
            cd "$PROJECT_DIR";
            $GIT tag -f testflight-upload

            # clean up if the upload worked
            rm "${DSYM}.zip"
            rm "${IPA}"

        else
            echo "Test Flight returned error:" > "${ERROR_LOG}"
            cat "${CURLLOG}" >> "${ERROR_LOG}"
            open "${ERROR_LOG}"

        fi
else

    echo "Failed to build IPA"  >> "${ERROR_LOG}"
    open "${TMP}/xcrun.log"
    exit 1
fi

update-version.sh:

Script which takes the line count of the git log and sets it as the CFBundleVersion number in the target’s Info.plist

The script also adds a ECVersionCommit key to the Info.plist with the full SHA1 hash of the current commit.

To use this script, add a Run Script phase to a target, and include this line

 "${ECLOGGING_SCRIPTS_PATH}/update-version.sh"



#!/bin/bash

## Script which takes the line count of the git log and sets it as the CFBundleVersion number in the target's Info.plist
##
## The script also adds a ECVersionCommit key to the Info.plist with the full SHA1 hash of the current commit.
##
## To use this script, add a Run Script phase to a target, and include this line
##     "${ECLOGGING_SCRIPTS_PATH}/update-version.sh"
##

PLIST="$1"
if [ "$PLIST" == "" ]; then
    PLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
fi

VERSION=`git log --oneline | wc -l`
COMMIT=`git rev-parse HEAD`

# update the plist in the built app
/usr/libexec/PlistBuddy -c "Add :ECVersionCommit string commit" "$PLIST"
/usr/libexec/PlistBuddy -c "Set :ECVersionCommit $COMMIT" "$PLIST"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $VERSION" "$PLIST"

echo "Bumped build number to $VERSION ($COMMIT) in $PLIST"