Fastlane iOS: Keeps building for simulator when using "iphoneos" sdk - ios

I'm attempting to write a fastlane script that zips and uploads my app and testplan to Firebase Test Lab. It is currently failing because it keeps building the app for a simulator instead of a real device. I have no idea why because with the sdk set to "iphoneos" this shouldn't be happening?
lane :QAUITests do |options|
generate(options)
#testplan = options[:testplan]
scan(
scheme: 'QAUITests', # XCTest scheme
clean: true, # Recommended: This would ensure the build would not include unnecessary files
skip_detect_devices: true, # Required
build_for_testing: true, # Required
sdk: 'iphoneos', # Required
should_zip_build_products: true, # Must be true to set the correct format for Firebase Test Lab
buildlog_path: "./fastlane/fastlane-buildlog",
configuration: 'Debug',
derived_data_path: "./DerivedData",
testplan: #testplan
)
sh "zip -r MyTests.zip build/Build/Products/* Debug-iphoneos QAUITests_Regression_iphoneos15.4-arm64.xctestrun"
sh "gcloud firebase test ios run --test MyTests.zip --device model=iphone11pro,version=14.7,locale=en_GB,orientation=portrait"
For more context, this is what I'm trying to do https://firebase.google.com/docs/test-lab/ios/command-line. They do have a plugin to help but unfortunatly this issue prevents it from working https://github.com/fastlane/fastlane-plugin-firebase_test_lab/issues/79
Exit status of command 'zip -r MyTests.zip DerivedData/Build/Products/* Debug-iphoneos QAUITests_Regression_iphoneos15.4-arm64.xctestrun' was 12 instead of 0.
zip warning: name not matched: DerivedData/Build/Products/*
zip warning: name not matched: Debug-iphoneos
zip warning: name not matched: QAUITests_Regression_iphoneos15.4-arm64.xctestrun

Related

How to use Fastlane with a CMake generated XCode project with dependency on WebP?

I have a project written in C++ where CMake is used to generate the build system for various platforms including iOS. The project has a dependency on WebP. You can find an example project on GitHub here that can be used to reproduce things & I've included the relevant source files at the end of this post for completeness.
The Xcode build system for iOS is generated using CMake as follows:
cmake -G Xcode -DCMAKE_TOOLCHAIN_FILE=third_party/ios-cmake/ios.toolchain.cmake -DPLATFORM=OS64 -DDEPLOYMENT_TARGET=15.0 -DENABLE_BITCODE=0 -S . -B cmake-build-release
We can now attempt to build/archive the app using Fastlane with the command from within the generated cmake-build-release directory:
bundle exec fastlane ios beta
However this fails due to being unable to locate various webp object files (that based on console output it appears to have previously successfully compiled):
...
▸ Compiling buffer_dec.c
▸ Compiling alpha_dec.c
▸ Building library libwebpdsp.a
...
** ARCHIVE FAILED **
▸ The following build commands failed:
▸ Libtool /Users/dbotha/Library/Developer/Xcode/DerivedData/CMakeFastlaneWebpTest-dlwvukebfiwjqvaqiepshuxqklhh/ArchiveIntermediates/CMakeFastlaneWebpTest/IntermediateBuildFilesPath/UninstalledProducts/iphoneos/libwebpdecoder.a normal (in target 'webpdecoder' from project 'CMakeFastlaneWebpTest')
▸ (1 failure)
▸ ❌ error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool: can't open file: /Users/dbotha/CLionProjects/CMakeFastlaneWebpTest/cmake-build-release/third_party/libwebp/CMakeFastlaneWebpTest.build/Release-iphoneos/webpdecode.build/Objects-normal/arm64/alpha_dec.o (No such file or directory)
▸ ❌ error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool: can't open file: /Users/dbotha/CLionProjects/CMakeFastlaneWebpTest/cmake-build-release/third_party/libwebp/CMakeFastlaneWebpTest.build/Release-iphoneos/webpdecode.build/Objects-normal/arm64/buffer_dec.o (No such file or directory)
...
Internally Fastlane attempted to build/archive the project with the following command:
xcodebuild -scheme CMakeFastlaneWebpTest -project ./CMakeFastlaneWebpTest.xcodeproj -configuration Release -destination 'generic/platform=iOS' -archivePath ./out.xcarchive archive
Interestingly an archive can be successfully generated if I use the following xcodebuild command (note how -target flag is used instead of -scheme):
xcodebuild -project CMakeFastlaneWebpTest.xcodeproj archive -target CMakeFastlaneWebpTest -configuration Release
After this successful attempt bundle exec fastlane ios beta will now also succeed as the compiled object files are where it expected them to be.
Now I'd happily workaround this issue using my xcodebuild + -target flag approach and then use the fastlane command to push to Testflight, etc. but the real project (not this toy example) takes a very long time to build so building it twice is really wasteful from a cost point of view on CI platforms.
Does anyone have any idea what's going on here & how I can successfully build things in the first instance using fastlane without my own explicit call to xcodebuild first? Or alternatively how can I have Fastlane use the successfully built objects from my workaround so it doesn't need to rebuild the entire project from scratch?
CMakeLists.txt
cmake_minimum_required(VERSION 3.23)
project(CMakeFastlaneWebpTest)
set(CMAKE_CXX_STANDARD 14)
add_executable(CMakeFastlaneWebpTest src/main.cpp)
# Skip building of unused webp tools which fail for me under ios:
set(WEBP_BUILD_ANIM_UTILS OFF)
set(WEBP_BUILD_CWEBP OFF)
set(WEBP_BUILD_DWEBP OFF)
set(WEBP_BUILD_GIF2WEBP OFF)
set(WEBP_BUILD_IMG2WEBP OFF)
set(WEBP_BUILD_VWEBP OFF)
set(WEBP_BUILD_WEBPINFO OFF)
set(WEBP_BUILD_WEBPMUX OFF)
set(WEBP_BUILD_EXTRAS OFF)
set(WEBP_BUILD_WEBP_JS OFF)
add_subdirectory(third_party/libwebp EXCLUDE_FROM_ALL)
target_link_libraries(CMakeFastlaneWebpTest PRIVATE webpdecoder webpdemux)
include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/third_party/libwebp/src)
configure_file(${CMAKE_SOURCE_DIR}/fastlane/Appfile ${CMAKE_BINARY_DIR}/fastlane/Appfile COPYONLY)
configure_file(${CMAKE_SOURCE_DIR}/fastlane/Fastfile ${CMAKE_BINARY_DIR}/fastlane/Fastfile COPYONLY)
configure_file(${CMAKE_SOURCE_DIR}/Gemfile ${CMAKE_BINARY_DIR}/Gemfile COPYONLY)
configure_file(${CMAKE_SOURCE_DIR}/Gemfile.lock ${CMAKE_BINARY_DIR}/Gemfile.lock COPYONLY)
set_target_properties(CMakeFastlaneWebpTest PROPERTIES
XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET}
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/src/iOS-Info.plist.in
MACOSX_BUNDLE_GUI_IDENTIFIER com.dbotha.CMakeFastlaneWebpTest
MACOSX_BUNDLE_BUNDLE_NAME CMakeFastlaneWebpTest
MACOSX_BUNDLE_BUNDLE_VERSION "0.1"
MACOSX_BUNDLE_SHORT_VERSION_STRING "0.1"
)
set_xcode_property(CMakeFastlaneWebpTest PRODUCT_BUNDLE_IDENTIFIER "com.dbotha.CMakeFastlaneWebpTest" All)
set_xcode_property(CMakeFastlaneWebpTest CODE_SIGN_IDENTITY "iPhone Developer" All)
set_xcode_property(CMakeFastlaneWebpTest DEVELOPMENT_TEAM "GFP63373B2" All)
fastlane/Appfile
app_identifier("com.dbotha.CMakeFastlaneWebpTest") # The bundle identifier of your app
apple_id("REPLACE_ME") # Your Apple Developer Portal username
itc_team_id("REPLACE_ME") # App Store Connect Team ID
team_id("REPLACE_ME") # Developer Portal Team ID
fastlane/Fastfile
default_platform(:ios)
platform :ios do
desc "Push a new beta build to TestFlight"
lane :beta do
build_app(scheme: "CMakeFastlaneWebpTest", configuration: "Release")
upload_to_testflight
end
end
src/main.cpp
#include <iostream>
#include <webp/demux.h>
int main() {
WebPAnimDecoderOptions decOptions;
(void)decOptions;
std::cout << "Hello, World!" << std::endl;
return 0;
}
I'm not an expert in this topic but according to the documentation you should provide workspace to build your scheme.
To build an Xcode workspace, you must pass both the -workspace and
-scheme options to define the build. The parameters of the scheme will
control which targets are built and how they are built, although you may
pass other options to xcodebuild to override some parameters of the
scheme.
Scheme controls what target will be build, and guessing by your example, since you do not provide a workspace, it gets lost in the process.
The scheme is not lost anymore if you build the target manually. Since it is already build the scheme does not have to do a thing.
My proposals:
Option 1: Try adding workspace to build_app parameters in Fastfile.
Option 2: Don't bother with building scheme just use target in the
build_app parameters in Fastfile like so: build_app(target: "CMakeFastlaneWebpTest", configuration: "Release")

How to use specific iOS version on Azure hosted agent?

I have a requirement to use exactly iOS 14.5 on our pipeline.
I tried the following:
variables:
sdk: 'iphoneos14.5' // I also tried to iphoneos
- task: Xcode#5
inputs:
actions: 'test'
configuration: '$(configuration)'
sdk: '$(sdk)'
xcWorkspacePath: 'MyProject.xcworkspace'
scheme: '$(secondaryScheme)'
xcodeVersion: 'specifyPath'
xcodeDeveloperDir: '/Applications/Xcode_13.2.1.app'
packageApp: false
destinationPlatformOption: 'iOS'
destinationSimulators: 'iPhone 11,OS=14.5'
args: '-derivedDataPath $(agent.buildDirectory)/DerivedData'
But both runs failed with
xcodebuild: error: SDK "iphoneos14.5" cannot be located.
##[error]Error: /usr/bin/xcodebuild failed with return code: 64
And
xcodebuild: error: Unable to find a destination matching the provided destination specifier:
{ platform:iOS Simulator, OS:14.5, name:iPhone 11 }
That's what I understood from reading the preinstalled software documentation. https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11-Readme.md
Grateful if you could tell me what I am doing wrong
By using your pipeline definition, I am able to reproduce your issue.
I think below YAML file will help you understand why the official document said 'iOS 14.5' is supported but you can't use it.
pool:
name: Azure Pipelines
#Your build pipeline references an undefined variable named ‘secondaryScheme’. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab. See https://go.microsoft.com/fwlink/?linkid=865972
steps:
- script: |
xcodebuild -showsdks
echo "========================================================="
sudo xcode-select -switch /Applications/Xcode_12.5.1.app/Contents/Developer
xcodebuild -showsdks
displayName: 'Command Line Script'
When you see the result of the pipeline run, I believe you will understand everything:
After switching the version, you should be able to use the specific IOS version you want.
Reference: https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow
You upload your app for notarization using notarytool command line
tool. Xcode 13 or later supports this tool, so if you have more than
one version of Xcode installed on your Mac, be sure to use the
xcode-select utility to choose an appropriate version:
% sudo xcode-select -s /path/to/Xcode13.app
Added this step
gem install xcode-install
xcversion simulators --install='iOS 13.5'
If someone finds a way to use the installed sdk for other Xcode, please comment here

Fastlane scan returns error No test bundle product for testingSpecifier

I’m trying to run my project's Unit Test cases on my current project but when I run the command:
fastlane scan
It returns this error message:
xcodebuild: error: Failed to build workspace TruDoc24x7 with scheme TruDoc24x7CI.
Reason : No test bundle product for testingSpecifier.
I also tried to write a lane for testing and add parameters to specify my workspace and scheme:
lane :tests do
run_tests(
workspace: "TruDoc24x7.xcworkspace",
scheme: "TruDoc24x7CI")
end
Can anyone tell me the reason for that error?

Build not available on iTunes Connect for internal testing through CircleCI fastlane deployment

I am currently trying to set up iOS deployment for a React-Native app, using Fastlane through CircleCI, and I am having an issue where I get to pilot in my fastlane script, I upload the build to iTunes Connect, but the build disappears from being used for TestFlight Internal Testers. If I archive locally and upload the build to iTunes Connect, it will be available for testing.
My Fastfile, using version 2.51.0
platform :ios do
lane :deploy_staging do
match(
type: "adhoc",
force: true
)
increment_build_number(
xcodeproj: './ios/MyApp.xcodeproj'
)
gym(
export_method: "ad-hoc",
scheme: "MyApp Staging",
project: "./ios/MyApp.xcodeproj"
)
pilot(
skip_submission: false,
distribute_external: false,
)
clean_build_artifacts
git_add(
path: '.'
)
git_commit(
path: '.',
message: "Deployed new staging version #{lane_context[SharedValues::BUILD_NUMBER]} [skip ci]",
)
push_to_git_remote(
local_branch: ENV["CIRCLE_BRANCH"],
remote_branch: ENV["CIRCLE_BRANCH"]
)
end
end
My circle.yml
machine:
environment:
PATH: '$PATH:$HOME/node/node-v8.1.3-darwin-x64/bin'
xcode:
version: 8.3.3
dependencies:
cache_directories:
- $HOME/node
pre:
- "ls \"$HOME/node/node-v8.1.3-darwin-x64\" || mkdir \"$HOME/node\""
- "ls \"$HOME/node/node-v8.1.3-darwin-x64\" || curl -L \"https://nodejs.org/dist/v8.1.3/node-v8.1.3-darwin-x64.tar.gz\" -o \"$HOME/node/node-v8.1.3-darwin-x64.tar.gz\""
- "ls \"$HOME/node/node-v8.1.3-darwin-x64\" || tar -xzf \"$HOME/node/node-v8.1.3-darwin-x64.tar.gz\" -C \"$HOME/node/\""
- "rm -f \"$HOME/node/node-v8.1.3-darwin-x64.tar.gz\""
override:
- npm install -g react-native-cli
- npm install
test:
override:
- npm test
post:
- mkdir -p $CIRCLE_TEST_REPORTS/junit/
- find . -type f -regex ".*/test_out/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \;
deployment:
pre:
- gem install fastlane
staging:
branch: staging
commands:
- npm run build:ios
- fastlane deploy_staging
Output from the CircleCI test
Build completed processing on iTunes Connect
Build not available (invisible) on TestFlight tab
I tried debugging this by archiving locally with the same certificates and profiles, but it uploads successfully and I am able to distribute to internal testers on TestFlight.
Thanks very much for the help.
Found the solution that helped resolve this issue.
Two parts seem to help fix it
Changing profile used from adhoc to appstore
a. I had to generate the appstore provisioning profile through match:
fastlane match appstore -a com.myapp.app.staging
Adding include_symbols and include_bitcode to my gym build parameters.
Processing took longer than normal, but after processing, it returns to the build list where pilot recognizes it and it posts to TestFlight.
My new Fastfile:
lane :deploy_staging do
match(
type: "appstore"
)
increment_build_number(
xcodeproj: './ios/MyApp.xcodeproj'
)
gym(
include_symbols: true,
include_bitcode: true,
export_method: "app-store",
scheme: "MyApp Staging",
project: "./ios/MyApp.xcodeproj"
) # Build your app - more options available
pilot
clean_build_artifacts
git_add(
path: '.'
)
git_commit(
path: '.',
message: "Deployed new staging version #{lane_context[SharedValues::BUILD_NUMBER]} [skip ci]",
)
push_to_git_remote(
local_branch: ENV["CIRCLE_BRANCH"],
remote_branch: ENV["CIRCLE_BRANCH"]
)
end

Can you pass environment variables into xcodebuild?

I have two Jenkins jobs that run our functional tests. One job is for whenever something is submitted for code review and the other job runs whenever something is pushed to master.
Because these are functional tests they test entire flows of the application which end up modifiying the user state. The issue we are having right is that every job uses the same account so whenever two Jenkins jobs are running in parallel they modify the same account which can put them in an unexpected state and fail the test.
My plan was to use Jenkins' BUILD_NUMBER environment variable and by applying a bit of arthimetic to it I could have a guaranteed unique number for the job. This unique number could then be passed into xcodebuild as an environment variable and the tests could use this number to ensure that every Jenkins' is working on a unique account.
The problem is that I cannot find any way to pass environment variables into xcodebuild. I know it is possible for you to pass in user-defined build settings via xcodebuild (or xcargs if you're using Fastlane) but those values do not seem to be accessible as environment variables. They are accessible by the preprocessor and so you could use it to export the value to your Info.plist and then read it from there. But then you have baked these value into your binary and it cannot be changed unless you rebuild it which is not ideal. Also at this point in time I could just have Jenkins write to a file on disk and have the tests read from it. It is essentially the same functionality and saves me from having to pass in build settings.
I remember using something like GCC_PREPROCESSOR_DEFINITIONS to pass my own var
I had to shell escape the quotes. I ended up coding it into my fastlane build file.
in ruby it looked like this:
tmp_other_flags = {
GCC_PREPROCESSOR_DEFINITIONS: '"DISABLE_PUSH_NOTIFICATIONS=1"',
TARGETED_DEVICE_FAMILY: '1',
DEBUG: '1'
}
other_flags = tmp_other_flags.map do |k, v|
"#{k.to_s.shellescape}=#{v.shellescape}"
end.join ' '
puts "___ Custom Flags also know as xcargs:"
puts other_flags
gym(
clean: true,
silent: false,
project: proj_xcodeproj_file,
archive_path: "build-ios-xcarchive",
destination: 'generic/platform=iOS',
use_legacy_build_api: true,
output_directory: 'build-ios',
output_name: "MyApp.ipa",
export_method: 'ad-hoc',
codesigning_identity: 'iPhone Distribution: company (12345)',
provisioning_profile_path: './dl_profile_com.company.myapp.iphone.prod_ad_hoc.mobileprovision',
scheme: 'MyApp',
configuration: 'Debug',
xcargs: other_flags
)
it ended up getting called in the shell something like this:
set -o pipefail && xcodebuild -scheme 'MyApp' -project 'platforms/ios/MyApp.xcodeproj' -configuration 'Debug' -destination 'generic/platform=iOS' -archivePath 'build-ios-xcarchive.xcarchive' GCC_PREPROCESSOR_DEFINITIONS=\"DISABLE_PUSH_NOTIFICATIONS\=1\" TARGETED_DEVICE_FAMILY=1 DEBUG=1 clean archive CODE_SIGN_IDENTITY='iPhone Distribution: My Company (Blah)' | tee '/Users/andxyz/Library/Logs/gym/MyApp-MyApp.log' | xcpretty
xcodebuild - how to define preprocessor macro?
So, perhaps you could pull in your own environment variable using ruby inside of fastlane. by adding your var into the GCC_PREPROCESSOR_DEFINITIONS section
ruby can access the environment, for example:
ENV.fetch('TERM_PROGRAM') #returns "iTerm.app" on my machine
so following along with above:
tmp_other_flags = {
GCC_PREPROCESSOR_DEFINITIONS: "MY_VARIABLE=#{ENV.fetch('MY_VARIABLE')}" ,
TARGETED_DEVICE_FAMILY: '1',
DEBUG: '1'
}
HTH
Via #alisoftware, you can use xcargs to pass additional variables in:
gym(
scheme: scheme,
xcargs: {
:PROVISIONING_PROFILE => 'profile-uuid',
:PROVISIONING_PROFILE_SPECIFIER => 'match AppStore com.bigco.App'
},
codesigning_identity: "iPhone Distribution: BigCo, Inc. ()",
)
emits this during the build:
+---------------------+-------------------------------------------------------------------------------------------------+
| Summary for gym 2.53.1 |
+---------------------+-------------------------------------------------------------------------------------------------+
| scheme | Bespoke-iOS |
| xcargs | PROVISIONING_PROFILE=profile-uuid PROVISIONING_PROFILE_SPECIFIER=match\ AppStore\ com.bigco.App |
…

Resources