Some questions of automate the process of iOS builds - ios

Recently,I try to automate the process of iOS builds using bash scripts.
I'm building the app,code signing the file using xcodebuild, and then pack the project using xcrun.
In xcode,we can set some information easily,(Summary->iOS Application Target->...)
but,how can I set the information without Xcode,I want to do that using bash scripts,like modify info-plist or other configuration files,does it works? how can I solve this question?
The second question:
Because I want to automate iOS builds in server, I need to import certificates(.p12) which clients give me into keychain automatically,the question is how can I export .p12 file automatically from keychain when clients give me the name of code signing.
Can you provide me some information of automating the process of iOS builds using bash scripts? Or do you have realize the function? Please give me some clues about that, or the program.

You can inject custom build settings by passing KEY=VALUE arguments to xcodebuild. You can then put these into Info.plist values with ${MY_BUILD_SETTING}.

I found that we can use plistbuddy to set values about info.plist,like that /usr/libexec/PlistBuddy -c "set:CFBundleShortVersionString ${version_version}" ${plist_path}
plistbuddy is a tool which can modify plist file well,using /usr/libexec/PlistBuddy -h to find help.
Hope that can help other people!

Related

iOS Sourcery with Flutter build

Im trying to build my flutter app for iOS it has a google maps key that I want to protect and not check in to source control it needs to be buildable from azure, to achieve this I'm storing my maps key as a secret variable in azure and as a system environment variable locally, I'm using Sourcery https://github.com/krzysztofzablocki/Sourcery to generate a class for me that contains this key, it all works but only the second time I build, the first build always fails.
So I'm building using this command
flutter build ios --flavor dev --verbose
Which the first run will give me the error
error: Build input file cannot be found:
'/Users/martin/xxx/xxx/xxx/ios/Runner/Credentials.generated.swift' (in target
'Runner'
Then issuing the same command again
** BUILD SUCCEEDED **
this is my run script its called before compile sources and after the flutter run script
this calls my script which calls another script to export the map api key and runs sourcery command using a .yml file as its config heres the script, (it also does some logging)
#!/bin/bash
echo "Generate Credentials Code"
CREDENTIALS_DIR="$SRCROOT/credentials"
# Set credentials if local script for adding environment variables exist
if [ -f "$CREDENTIALS_DIR/add_credentials_to_env.sh" ]; then
echo "Add credentials to environement"
source "$CREDENTIALS_DIR/add_credentials_to_env.sh"
echo "finished running add_credentials_to_env.sh"
fi
echo "RUN SOURCERY"
$SRCROOT/Pods/Sourcery/bin/sourcery --config "$SRCROOT/config.yml"
echo "FINISHED RUNNING SOURCERY"
for file in "$SRCROOT/Runner"/*; do
echo "$file"
done
and here is my config file
sources:
- .
project:
file: Runner.xcodeproj
target:
name: Runner
module: Runner
templates:
- credentials/Credentials.stencil
output:
path: ./Runner/
link:
project: Runner.xcodeproj
target: Runner
args:
mapsApiKey: ${MAPS_API_KEY_IOS}
this generates my class correctly on the first build and seems to be added correctly to the target (edited out my key) but the app will only compile if I run the build command again.
// Generated using Sourcery 1.4.2 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
public struct Credentials {
let mapsApiKey: String
}
public let credentials = Credentials(mapsApiKey:
"xxxxxxxxxxMY_KEYxxxxxxxxxxx")
Any ideas?
xcode 12.5 m1 macbook pro, swift 5
Looks like you generate the file too late. I'll suggest move your script to Aggregate and add it as a dependency to your target
Add Aggregate
Move your script to 'Run script' section
Add 'PreBuildScriptsRunner' as a dependency to your application target, make sure 'Dependencies' section on top of all other sections
Manually setting environment variables is an annoying thing developers would have to do on their own machines, and there are nicer/ more common ways of setting up private keys. After a few years of using environment variables/ bash, it still causes issues which are not easily detectable. You may want to automate/ document it, but then you have to consider developers using zsh, fish vs. bash? Also, I try to avoid using Xcode build phases where possible.
Solution? (This is what I have)
Why don't you use your CI (Azure pipeline?, I use Github workflows) to write a Xcode build configuration file (not a Swift file). The sensitive keys could be in a file Secrets.xcconfig, which is added to your Xcode as a build configuration. Then, in your Info.plist of your application, and your code can load them.
Create a file, Secrets.xcconfig:
SECRET_API_KEY = 12312rfiwhvde.wvascafsf.df325
Add it to your Xcode project, and then to the project's build configuration:
Add Secrets.xcconfig to your .gitignore
Make sure to git ignore the file before committing it to the repo. You can also keep an Example.Secrets.xcconfig which users can use. In the readme, tell users to run cp Example.Secrets.xcconfig Secrets.xcconfig and then to update values in it. Now you can clearly see what keys the application is using (its clearly in the directory). As a bonus, you can add this file the Xcode project, so that when the file is missing, it shows up in red (indicating to the user they really should acquire this file somehow):
In Info.plist, reference the variable:
<dict>
<key>SECRET_API_KEY</key>
<string>$(SECRET_API_KEY)</string>
</dict>
In your code, load the variable that was stored in Info.plist:
let key = Environment.infoDictionary["SECRET_API_KEY"] as? String
In your CI/ Azure pipeline:
Run echo "SECRET_API_KEY = $SECRET_API_KEY_SAVED_IN_CONTINUOUS_INTEGRATION" >> Secrets.xcconfig
Then you can just .gitignore the file instead of setting environment variables. When you work with other developers, you just give them this file, and nothing else needs to be done to build locally.
So I have answered your question not by solving your direct problem, but giving you a more common/ canonical way of solving this problem that many developers have faced before.

Xcode Bot: how to get .ipa path on a post trigger script?

I'm using a bot to archive an iOS app, and I need to get the .ipa product path in order to publish it into our distribution system.
Bot settings:
And using a script to print all env variables, non of them contains a path to the ipa file. In addition, some of the variables are pointed to directory that does not exist, i.e: XCS_OUTPUT_DIR
Here the env variable output:
XCS=1
XCS_ANALYZER_WARNING_CHANGE=-31
XCS_ANALYZER_WARNING_COUNT=0
XCS_ARCHIVE=/Library/Developer/XcodeServer/Integrations/Integration-771867708dfac45bba10a1998c118912/MyApp.xcarchive
XCS_BOT_ID=771867708dfac45bba10a1998c007d43
XCS_BOT_NAME='MyApp Distribution'
XCS_BOT_TINY_ID=DBB85BD
XCS_DERIVED_DATA_DIR=/Library/Developer/XcodeServer/Integrations/Caches/771867708dfac45bba10a1998c007d43/DerivedData
XCS_ERROR_CHANGE=-1
XCS_ERROR_COUNT=0
XCS_INTEGRATION_ID=771867708dfac45bba10a1998c118912
XCS_INTEGRATION_NUMBER=19
XCS_INTEGRATION_RESULT=warnings
XCS_INTEGRATION_TINY_ID=F7D4469
XCS_OUTPUT_DIR=/Library/Developer/XcodeServer/Integrations/Integration-771867708dfac45bba10a1998c118912
XCS_SOURCE_DIR=/Library/Developer/XcodeServer/Integrations/Caches/771867708dfac45bba10a1998c007d43/Source
XCS_TESTS_CHANGE=0
XCS_TESTS_COUNT=0
XCS_TEST_FAILURE_CHANGE=0
XCS_TEST_FAILURE_COUNT=0
XCS_WARNING_CHANGE=0
XCS_WARNING_COUNT=26
XCS_XCODEBUILD_LOG=/Library/Developer/XcodeServer/Integrations/Integration-771867708dfac45bba10a1998c118912/xcodebuild.log
XPC_FLAGS=0x0
XPC_SERVICE_NAME=com.apple.xcsbuildd
In addition to that, I was able to confirm that .ipa files are being created in another folder (<path to server>/IntegrationAssets/<integration id>/<integration number>/), but that path is not accessible from an env variable.
Any ideas?
Well, after a lot of research and testing and all, apparently there is something wrong with bots in the latest Xcode (7.2) + Server version (5.0.15) not loading the correct environment variables.
My current solution was to create the path manually based on existing env variables:
ARCHIVE_PATH="${XCS_ARCHIVE}"
ARCHIVE_NAME="${ARCHIVE_PATH##*/}"
IPA_NAME="${ARCHIVE_NAME%.*}.ipa"
IPA_PATH="${XCS_OUTPUT_DIR}/ExportedProduct/Apps/${IPA_NAME}"
Which I did based on:
Continuous integration Xcode Server after trigger $XCS_PRODUCT not set
Just a small update--In Xcode 9.4.1 at least $XCS_PRODUCT is set correctly, so you can just use that, e. g. for HockeyApp:
curl -F "status=2" -F "notify=1" -F "ipa=#${XCS_PRODUCT}" -H "X-HockeyAppToken: <token>" https://rink.hockeyapp.net/api/2/apps/upload

How to fix Xcode's code sign issues on Jenkins?

I have done the usual steps of making the xcodebuild work on jenkins, which would be installing "keychain & provisioning profiles" plugin, uploading the right keychain+profiles in the plugin's configuration, and using security -v unlock-keychain, and PROVISIONING_PROFILE in the xcodebuild command.
the build goes forward O.K., even runs the defined postbuild scripts defined in the Xcode project, and creates the .app folder with all the necessary content, but fails here...
/Users/Shared/Jenkins/Home/jobs/iOS_feature/workspace/build/Applications/MyApp.app/Frameworks/libswiftUIKit.dylib: User interaction is not allowed.
*** error: Couldn't codesign /Users/Shared/Jenkins/Home/jobs/iOS_feature/workspace/build/Applications/MyApp.app/Frameworks/libswiftUIKit.dylib: codesign failed with exit code 1
Any ideas what could cause this? from what I understand, the Keychain+ProvisioningProfiles are in place since the build doesn't fail here.
firstly, are you passing your keychain password to the security -v unlock-keychain command (using -p)? it's definitely not an ideal solution, because it probably requires exposing your password in clear text on your build server.
our workaround for this problem is to login to the actual machine (remote screen sharing or locally sitting at the machine). run a single build from the desktop, whether it be command line or in Xcode. (you might be asked to "always allow" but i'm not 100% sure under which scenarios this will happen).
from then on, all your remote CI builds should pass through code signing successfully.
It's not due to reverse DNS failing or self signed certs on the Jenkins box is it?
We had similar issues though not on Jenkins but Xcode 7 does these extra two checks we found.
Does CodeSign have permission to use the private key?
Whenever I see this error, this is usually the solution:
https://stackoverflow.com/a/22637896/78496

Xcode 6 beta CFBundleShortVersionString not found when building [duplicate]

With Xcode 5's new Asset Library, adding images and organizing them has never been easier. However, it seems as if it has broken some scripts I use for creating builds.
I have a script within my Run Script Phase that sets the CFBundleVersion to be the current timestamp within the plist. In the script, I execute this statement:
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $timestamp" $BUILT_PRODUCTS_DIR/$INFOPLIST_PATH
However, when this gets executed, the following statement displays:
Set: Entry, ":CFBundleVersion", Does Not Exist
File Doesn't Exist, Will Create: /Users/SpacePyro/Library/DerivedData/BundleTest-duikdqngfmrovnagrcsvdcuxxstz/Build/Products/Debug-iphoneos/BundleTest.app/Info.plist
It seems like this happens on clean builds. The plist doesn't seem to get generated until midway through the build, presumably due to the Asset Libraries.
I've also used this command, and while it doesn't throw the error, it still blows away my changes (assume INFO_PLIST="${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/Info"):
defaults write $INFO_PLIST CFBundleVersion $timestamp
This used to not be the case when I started using the Asset Library to organize my app icons and splash images. Anyone know why this happens? And better yet, is there a workaround to add this value to the plist? I've already tried placing the script in a pre-action build phase, as well as the post-action build phase. I've also tried running the command after the build has completed, but when I try to codesign and package it up, it says that the signature is invalid due to plist modification.
If no reasonable solution exists, I guess I could always de-migrate from Asset Libraries until I can get my scripts to work.
I had similar issue once, and here is what finally helped me out:
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${INFOPLIST_FILE}")
buildNumber=$(($buildNumber + 1))
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "${INFOPLIST_FILE}"
(Use INFOPLIST_FILE directly, not $BUILT_PRODUCTS_DIR/$INFOPLIST_PATH)
Hope this could be useful .
Figured this one out, and it was a silly one. Turns out you can just move the script phase to the very end. I didn't even know these were movable, or that it mattered! But by dragging the Run Script phase to the bottom as such, the scripts were able to run and modify things as needed.
I had the same problem, In my case, I had a wrong file path to the XXX-Info.plist file:
Build Settings -> Packaging -> Info.plist File
I changed it it's actual location and start working.
If your plist file is Preprocessed-Info.plist, then change the value of "Preprocess Info.plist File" (INFOPLIST_PREPROCESS) to "Yes" (true) like this:
Search in Build settings for $(SRCROOT) and remove it.
transform it from
$(SRCROOT)/TestProject/Info.plist
to
TestProject/Info.plist

Info.plist | Modify Version String

Our OSX Application running on have some modules which are running on other platform too, i.e. most of the part are ported to different platform and some are common across all platform,
When come to version no, to be consistent with the other platform port, we need to maintain a common version say, AppVersion.h which has the version string.
Now to show on the UI , i need to copy the same version string from AppVersion.h file to Info.plist
Are there any workaround for the same, i.e. run some pre-build script which reads the version string and update the info.plist.
Thanks in advance.
Absolutely! This is what I use as a build phase, after the resource copy. I break down the problem a bit because this is part of a much larger script.
#!/bin/sh
export PlistBuddy="/usr/libexec/PlistBuddy"
# TODO: Get OUR_VERSION here.
export appPlist="${BUILT_PRODUCTS_DIR}/${EXECUTABLE_FOLDER_PATH}/Info.plist"
$PlistBuddy $appPlist -c "set :CFBundleShortVersionString '$OUR_VERSION'"
$PlistBuddy $appPlist -c "set :CFBundleVersion '$OUR_VERSION'"
As to how to get OUR_VERSION? I leave that open to you. In my case, I simply grepped the file in the script. For me, this was an xcconfig but for you it'll be the header. If you've got command line experience, you might have a better way to extract the symbol meaning from the .h file.
Note that this sets CFBundleShortVersionString (the user-visible version) and CFBundleVersion (the internal version) to the same value. That's probably what you want. If not, fix that. :)

Resources