Removing linked Pods_framework in post_install Podfile hook - ruby

I currently have the following situation in my flutter iOS/Android development process:
Every time flutter build runs it executes pod install which installs the regular Flutter Podfile
# Uncomment this line to define a global platform for your project
platform :ios, '10.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'false'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path
return [];
end
generated_key_values = {}
skip_line_start_symbols = ["#", "/"]
File.foreach(file_abs_path) do |line|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
plugin = line.split(pattern=separator)
if plugin.length == 2
podname = plugin[0].strip()
path = plugin[1].strip()
podpath = File.expand_path("#{path}", file_abs_path)
generated_key_values[podname] = podpath
else
puts "Invalid plugin specification: #{line}"
end
end
generated_key_values
end
target 'Runner' do
use_frameworks!
use_modular_headers!
# Flutter Pod
copied_flutter_dir = File.join(__dir__, 'Flutter')
copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
# Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
# That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
# CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
unless File.exist?(generated_xcode_build_settings_path)
raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
unless File.exist?(copied_framework_path)
FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
end
unless File.exist?(copied_podspec_path)
FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
end
end
# Keep pod path relative so it can be checked into Podfile.lock.
pod 'Flutter', :path => 'Flutter'
# Plugin Pods
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
system('rm -rf .symlinks')
system('mkdir -p .symlinks/plugins')
plugin_pods = parse_KV_file('../.flutter-plugins')
plugin_pods.each do |name, path|
symlink = File.join('.symlinks', 'plugins', name)
File.symlink(path, symlink)
pod name, :path => File.join(symlink, 'ios')
end
end
target 'OneSignalNotificationServiceExtension' do
use_frameworks!
pod 'OneSignal', '>= 2.9.3', '< 3.0'
end
as seen at the end to enable OneSignal push notifications in my app, I've added the OneSignalNotificationServiceExtension. Since the Flutter Runner needs use_frameworks!, I have to add this line to the OneSignal Extension target as well.
This leads to the following file being included unter "General" > "Framework and Libraries" on my OneSignal Target ("Pods_OneSignalNotificationServiceExtension.framework"):
wrongly linked file
But this file probably doesn't exist so the build fails.
If I manually remove this file from XCode, the build works.
But since running Flutter in debug mode from my IDE runs pod install again, I can't remove this link, so my idea was to automate the removing in the post_install hook inside the Podfile.
But since I'm neither really familiar with Ruby nor seem to be able to find good documentation for methods/properties in this callback, I'm not getting it to work.
Here's something I've tried so far:
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
if target.name == 'Pods-OneSignalNotificationServiceExtension'
all_filerefs = installer.pods_project.files
all_filerefs.each do |fileref|
#puts(fileref.path)
if fileref.path.end_with? "Pods_OneSignalNotificationServiceExtension.framework"
puts("Found Pods_OneSignalNotificationServiceExtension.framework fileref.")
build_phase = target.frameworks_build_phase
#puts("Determining if build phase needs correction.")
#all_filerefs.delete(fileref)
build_phase.remove_file_reference(fileref)
puts("Removing Pods_OneSignalNotificationServiceExtension.framework from OneSignalNotificationServiceExtension target")
end
end
end
end
end
But neither removing it from all_filerefs nor build_phase.remove_file_reference is quite working. Does anybody know how I can access the linked files from the "Framework and Libraries" section in XCode and how to remove said .framework-file?
Thanks a lot!

With the right google search I've managed to find a single result regarding this topic: https://titanwolf.org/Network/Articles/Article?AID=18711f19-4d55-49b9-a49e-8c4652dc0262#gsc.tab=0
I've took his function from "Fourth, add or remove framework to target"
def updateCustomFramework(project,target,path,name,command)
# Get useful variables
frameworks_group = project.groups.find { |group| group.display_name == 'Frameworks' }
frameworks_build_phase = target.build_phases.find { |build_phase| build_phase.to_s == 'FrameworksBuildPhase' }
embed_frameworks_build_phase = nil
target.build_phases.each do |phase|
if phase.display_name == 'Embed Frameworks'
embed_frameworks_build_phase = phase
end
# puts phase
end
if command == :add
# Add new "Embed Frameworks" build phase to target
if embed_frameworks_build_phase.nil?
embed_frameworks_build_phase = project.new(Xcodeproj::Project::Object::PBXCopyFilesBuildPhase)
embed_frameworks_build_phase.name = 'Embed Frameworks'
embed_frameworks_build_phase.symbol_dst_subfolder_spec = :frameworks
target.build_phases << embed_frameworks_build_phase
end
# Add framework search path to target
['Debug', 'Release'].each do |config|
paths = ['$(inherited)', path]
orgpath = target.build_settings(config)['FRAMEWORK_SEARCH_PATHS']
if orgpath.nil?
puts "path is empty----------"
target.build_settings(config)['FRAMEWORK_SEARCH_PATHS'] = paths
else
puts "path is not empty----------"
paths.each do |p|
if !orgpath.include?(p)
orgpath << p
end
end
end
end
# Add framework to target as "Embedded Frameworks"
framework_ref = frameworks_group.new_file("#{path}#{name}")
exsit = false
embed_frameworks_build_phase.files_references.each do |ref|
if ref.name = name
exsit = true
end
end
if !exsit
build_file = embed_frameworks_build_phase.add_file_reference(framework_ref)
frameworks_build_phase.add_file_reference(framework_ref)
build_file.settings = { 'ATTRIBUTES' => ['CodeSignOnCopy'] }
end
else
frameworks_build_phase.files_references.each do |ref|
#puts(ref.path)
if ref.path == "#{name}"
puts "delete #{name}"
frameworks_build_phase.remove_file_reference(ref)
#embed_frameworks_build_phase.remove_file_reference(ref)
break
end
end
end
end
And only changed if ref.name == "#{name}" to if ref.path == "#{name}".
I also realized that I can't use the pods_projects from the installer but need to open the xcodeproj-file myself. Here is my final post_install script:
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
project_path = 'Runner.xcodeproj'
project = Xcodeproj::Project.open(project_path) # Opening the Runner.xcodeproj in the same folder as the Podfile
project.targets.each do |target|
if target.name == "OneSignalNotificationServiceExtension" # Find the OneSignal Target
updateCustomFramework(project, target, '', 'Pods_OneSignalNotificationServiceExtension.framework', ':remove') # Run the function on the target with the framework name
end
end
project.save() # Don't forget to save the project, or the changes won't stay
end
after that a flutter clean was necessary and afterwards the flutter build works like a charm. Hopefully in the future this will save someone 6 hours of time.

Related

Firebase Admob build fails during Archive

My app in general works fine but when trying to set up for release the build fails because "Module'firebase_admob'not found"
//
// Generated file. Do not edit.
//
#import "GeneratedPluginRegistrant.h"
#if __has_include(<firebase_admob/FLTFirebaseAdMobPlugin.h>)
#import <firebase_admob/FLTFirebaseAdMobPlugin.h>
#else
#import firebase_admob;
#endif
#if __has_include(<webview_flutter/FLTWebViewFlutterPlugin.h>)
#import <webview_flutter/FLTWebViewFlutterPlugin.h>
#else
#import webview_flutter;
#endif
#implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[FLTFirebaseAdMobPlugin registerWithRegistrar:[registry registrarForPlugin:#"FLTFirebaseAdMobPlugin"]];
[FLTWebViewFlutterPlugin registerWithRegistrar:[registry registrarForPlugin:#"FLTWebViewFlutterPlugin"]];
}
#end
I coded the app in android studio, and am following the guide for release at https://flutter.dev/docs/deployment/ios
I believe the issue may have to do with not installing the admob pod into my podfile, but I have never had to do that before. But here is my podfile anyways:
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path
return [];
end
generated_key_values = {}
skip_line_start_symbols = ["#", "/"]
File.foreach(file_abs_path) do |line|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
plugin = line.split(pattern=separator)
if plugin.length == 2
podname = plugin[0].strip()
path = plugin[1].strip()
podpath = File.expand_path("#{path}", file_abs_path)
generated_key_values[podname] = podpath
else
puts "Invalid plugin specification: #{line}"
end
end
generated_key_values
end
target 'Runner' do
use_frameworks!
use_modular_headers!
# Flutter Pod
copied_flutter_dir = File.join(__dir__, 'Flutter')
copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
# Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
# That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
# CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
unless File.exist?(generated_xcode_build_settings_path)
raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
unless File.exist?(copied_framework_path)
FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
end
unless File.exist?(copied_podspec_path)
FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
end
end
# Keep pod path relative so it can be checked into Podfile.lock.
pod 'Flutter', :path => 'Flutter'
# Plugin Pods
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
system('rm -rf .symlinks')
system('mkdir -p .symlinks/plugins')
plugin_pods = parse_KV_file('../.flutter-plugins')
plugin_pods.each do |name, path|
symlink = File.join('.symlinks', 'plugins', name)
File.symlink(path, symlink)
pod name, :path => File.join(symlink, 'ios')
end
end
# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
install! 'cocoapods', :disable_input_output_paths => true
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
end
I had the same problem, and found the solution here: https://github.com/flutter/flutter/issues/47394
It seems like you need to open Runner.xcworkspace instead of Runner.xcodeproj when you archive.

Xcode warning: Multiple targets match implicit dependency for product

I have an app that comes in two flavors (paid and free). One of my frameworks is called AppBootstrap, which has two subspecs (FreeVersion and PaidVersion)
Now Xcode keeps giving this annoying warning (I aim for zero warnings in my project so I do not just want to ignore it, slippery slope and stuff like that ;) )
Multiple targets match implicit dependency for product reference 'AppBootstrap.framework'. Consider adding an explicit dependency on the intended target to resolve this ambiguity. (in target 'The Flight Tracker Free' from project 'The Flight Tracker')
Target 'AppBootstrap-FreeVersion' (in project 'AppBootstrap')
Target 'AppBootstrap-PaidVersion' (in project 'AppBootstrap')
I googled it a bit but I could not find how to solve this.
I tried
* adding it in the 'Link Binary With Libraries' build phase but that did not solve it.
* adding it to the dependencies phase but they do not show up there.
* changing the '-framework AppBootstrap' in 'Build settings => Other Linker Flags' to '-framework AppBootstrap-FreeVersion' but that just ends up in errors.
My podfile (simplified)
source 'custom-pods/link'
source 'https://cdn.cocoapods.org/'
platform :ios, '9.0'
install! 'cocoapods',
:generate_multiple_pod_projects => true,
:incremental_installation => true
use_frameworks!
inhibit_all_warnings!
workspace 'MyApp'
target 'MyAppFree' do
pod 'AppBootstrap/FreeVersion'
end
target 'MyAppPaid' do
pod 'AppBootstrap/PaidVersion'
end
AppBootstrap podspec
Pod::Spec.new do |s|
s.name = 'AppBootstrap'
s.version = '3.18.2'
s.summary = 'iOS App Bootstrap Module.'
s.platforms = { ios: '9.0' }
s.swift_version = '5.0'
s.description = <<-DESC
Contains classes to bootstrap ios apps.
DESC
s.homepage = ---
s.license = { type: 'MIT', file: 'LICENSE' }
s.author = { --- }
s.source = { --- }
s.frameworks = [
'Foundation',
'UIKit'
]
s.subspec 'PaidVersion' do |sub|
sub.dependency 'Advertisement/Core'
sub.source_files = [
'AppBootstrap/source/**/*.swift'
]
sub.resources = [
'AppBootstrap/support/Localization/*.lproj',
'AppBootstrap/support/ConsentText.plist',
'AppBootstrap/support/Images.xcassets'
]
sub.pod_target_xcconfig = {
'APPLICATION_EXTENSION_API_ONLY' => 'NO'
}
sub.pod_target_xcconfig = {
'OTHER_SWIFT_FLAGS' => '-D"PAIDVERSION"'
}
end
s.subspec 'FreeVersion' do |sub|
sub.dependency 'Advertisement/Ads'
sub.source_files = [
'AppBootstrap/source/**/*.swift'
]
sub.resources = [
'AppBootstrap/support/Localization/*.lproj',
'AppBootstrap/support/ConsentText.plist',
'AppBootstrap/support/Images.xcassets',
'AppBootstrap/support/Colors.xcassets',
'AppBootstrap/support/Fonts/*.otf'
]
sub.pod_target_xcconfig = {
'APPLICATION_EXTENSION_API_ONLY' => 'NO'
}
sub.pod_target_xcconfig = {
'OTHER_SWIFT_FLAGS' => '-D"FREEVERSION"'
}
end
s.subspec 'Undefined' do |sub|
sub.dependency 'Advertisement/Core'
sub.source_files = [
'AppBootstrap/source/**/*.swift'
]
sub.resources = [
'AppBootstrap/support/Localization/*.lproj',
'AppBootstrap/support/ConsentText.plist',
'AppBootstrap/support/Images.xcassets',
'AppBootstrap/support/Fonts/*.otf'
]
sub.pod_target_xcconfig = {
'APPLICATION_EXTENSION_API_ONLY' => 'NO'
}
sub.pod_target_xcconfig = {
'OTHER_SWIFT_FLAGS' => '-D"UNDEFINEDVERSION"'
}
end
s.default_subspec = 'Undefined'
end
Any help/advice is greatly appreciated =)
the error is just happen in Pruduct -> Archive.
you can pod install for single target before archive.
source 'custom-pods/link'
source 'https://cdn.cocoapods.org/'
platform :ios, '9.0'
install! 'cocoapods',
:generate_multiple_pod_projects => true,
:incremental_installation => true
use_frameworks!
inhibit_all_warnings!
def MyAppPods (scheme)
if scheme == 'MyAppFree'
pod 'AppBootstrap/FreeVersion'
end
if scheme == 'MyAppPaid'
pod 'AppBootstrap/PaidVersion'
end
end
workspace 'MyApp'
scheme = ENV['scheme']
if scheme
target scheme do
MyAppPods scheme
end
else
target 'MyAppFree' do
MyAppPods 'MyAppFree'
end
target 'MyAppPaid' do
MyAppPods 'MyAppPaid'
end
end
scheme='MyAppFree' pod install
It's happened to me when I bump iOS min version in Podfile but forgot to bump it for frameworks (targets) in Podfile
One simple fix for your case is not using subspec. Instead, create 2 different specs.
e.g
target 'MyAppFree' do
pod 'AppBootstrapFreeVersion'
end
target 'MyAppPaid' do
pod 'AppBootstrapPaidVersion'
end

Measuring xcodebuild durations for all targets (including dependent ones)

Is it possible to measure time, that single xcodebuild command consumes to build every distinct target?
Let's say I have a target, which depends on some cocoapods: pod1 and pod2.
I build my target using xcodebuild. I can measure overall time.
I need to measure times, that were separately spent on pod1, pod2 and my target
I tried to find the answer in xcodebuild's output, but failed to do so.
Thanks in advance!
I ended up writing a custom ruby script for modifing every target of my xcodeproj and of Pods.xcodeproj. This script adds two build phases that log the target name and current timestamp into an output file. One build phase executes first, an the other one executes last. Later on I simply substract one timestamp from another in a separate script.
Here is the result of the script:
The output file will look like this (after sorting)
Alamofire end: 1510929112.3409
Alamofire start: 1510929110.2161
AlamofireImage end: 1510929113.6925
AlamofireImage start: 1510929112.5205
Path to the output file (/a/ci_automation/metrics/performance-metrics/a.txt on the screenshot) is not hardcoded anyhow. Instead, you pass it as a parameter of a ruby script like this:
$ruby prepare-for-target-build-time-profiling.rb ${PWD}/output.txt
Note, that this script requires cocoapods 1.3.1 (maybe 1.3).
Here is the ruby script: ruby prepare-for-target-build-time-profiling.rb
#!/usr/bin/env ruby
require 'xcodeproj'
require 'cocoapods'
require 'fileutils'
def inject_build_time_profiling_build_phases(project_path)
project = Xcodeproj::Project.open(project_path)
log_time_before_build_phase_name = '[Prefix placeholder] Log time before build'.freeze
log_time_after_build_phase_name = '[Prefix placeholder] Log time after build'.freeze
puts "Patching project at path: #{project_path}"
puts
project.targets.each do |target|
puts "Target: #{target.name}"
first_build_phase = create_leading_build_phase(target, log_time_before_build_phase_name)
last_build_phase = create_trailing_build_phase(target, log_time_after_build_phase_name)
puts
end
project.save
puts "Finished patching project at path: #{project_path}"
puts
end
def create_leading_build_phase(target, build_phase_name)
remove_existing_build_phase(target, build_phase_name)
build_phase = create_build_phase(target, build_phase_name)
shift_build_phase_leftwards(target, build_phase)
is_build_phase_leading = true
inject_shell_code_into_build_phase(target, build_phase, is_build_phase_leading)
return build_phase
end
def create_trailing_build_phase(target, build_phase_name)
remove_existing_build_phase(target, build_phase_name)
build_phase = create_build_phase(target, build_phase_name)
is_build_phase_leading = false
inject_shell_code_into_build_phase(target, build_phase, is_build_phase_leading)
return build_phase
end
def remove_existing_build_phase(target, build_phase_name)
existing_build_phase = target.shell_script_build_phases.find do |build_phase|
build_phase.name.end_with?(build_phase_name)
# We use `end_with` instead of `==`, because `cocoapods` adds its `[CP]` prefix to a `build_phase_name`
end
if !existing_build_phase.nil?
puts "deleting build phase #{existing_build_phase.name}"
target.build_phases.delete(existing_build_phase)
end
end
def create_build_phase(target, build_phase_name)
puts "creating build phase: #{build_phase_name}"
build_phase = Pod::Installer::UserProjectIntegrator::TargetIntegrator
.create_or_update_shell_script_build_phase(target, build_phase_name)
return build_phase
end
def shift_build_phase_leftwards(target, build_phase)
puts "moving build phase leftwards: #{build_phase.name}"
target.build_phases.unshift(build_phase).uniq! unless target.build_phases.first == build_phase
end
def inject_shell_code_into_build_phase(target, build_phase, is_build_phase_leading)
start_or_end = is_build_phase_leading ? "start" : "end"
build_phase.shell_script = <<-SH.strip_heredoc
timestamp=`echo "scale=4; $(gdate +%s%N/1000000000)" | bc`
echo "#{target.name} #{start_or_end}: ${timestamp}" >> #{$build_time_logs_output_file}
SH
end
def parse_arguments
$build_time_logs_output_file = ARGV[0]
if $build_time_logs_output_file.to_s.empty? || ! $build_time_logs_output_file.start_with?("/")
puts "Error: you should pass a full path to a output file as an script's argument. Example:"
puts "$ruby prepare-for-target-build-time-profiling.rb /path/to/script/output.txt"
puts
exit 1
end
end
def print_arguments
puts "Arguments:"
puts "Output path: #{$build_time_logs_output_file}"
puts
end
def clean_up_before_script
if File.exist?($build_time_logs_output_file)
FileUtils.rm($build_time_logs_output_file)
end
build_time_logs_output_folder = File.dirname($build_time_logs_output_file)
unless File.directory?(build_time_logs_output_folder)
FileUtils.mkdir_p(build_time_logs_output_folder)
end
end
def main
parse_arguments
print_arguments
clean_up_before_script
inject_build_time_profiling_build_phases("path/to/project.xcodeproj")
inject_build_time_profiling_build_phases("path/to/pods/project.xcodeproj")
end
# arguments:
$build_time_logs_output_file
main

Install ImageMagick and start your lane again error when applying icon overlay

To give some context, I am currently using a program called badge to apply an icon overlay to my app icons. Works great locally on my computer, however when I run the command badge inside an xcode bot before integration, I get the following error:
Install ImageMagick and start your lane again!
I have ran the brew update && brew install imagemagick command numerous times on the _xcsbuildd (Xcode Server) user to install ImageMagick and I still get the error. Checking the runner.rb file under the /Library/Ruby/Gems/2.0.0/gems/badge-0.7.1/lib/badgedirectory, I found the code that throws the exception. I have unanswered questions that will hopefully give me the next debugging datapoint.
1.) where does require require 'fastimage' require 'timeout' require 'mini_magick' point to in the file system? Is there any way to echo out the location when invoked so I can confirm it's in the correct directory?
2.) Looks like the runner.rb has a method named check_imagemagick! to determine if imagemagick is installed if I were to make an educated guess.. Can someone plase explain what this logic is doing?
return if `which convert`.include?('convert')
return if `which gm`.include?('gm')
Here's the full code from the runner.rb file:
require 'fastimage'
require 'timeout'
require 'mini_magick'
module Badge
class Runner
##retry_count = Badge.shield_io_retries
def run(path, options)
check_imagemagick!
glob = "/**/*.appiconset/*.{png,PNG}"
glob = options[:glob] if options[:glob]
UI.message "FP:" + glob
UI.message "P:" + path
app_icons = Dir.glob("#{path}#{glob}")
UI.verbose "Verbose active...".blue
UI.verbose "Parameters: #{options.inspect}".blue
alpha_channel = false
if options[:alpha_channel]
alpha_channel = true
end
if app_icons.count > 0
UI.message "Start adding badges...".green
shield = nil
response_error = false
begin
timeout = Badge.shield_io_timeout
timeout = options[:shield_io_timeout] if options[:shield_io_timeout]
Timeout.timeout(timeout.to_i) do
shield = load_shield(options[:shield]) if options[:shield]
end
rescue Timeout::Error
UI.error "Error loading image from shield.io timeout reached. Skipping Shield. Use --verbose for more info".red
rescue OpenURI::HTTPError => error
response = error.io
UI.error "Error loading image from shield.io response Error. Skipping Shield. Use --verbose for more info".red
UI.error response.status if $verbose
response_error = true
end
if ##retry_count <= 0
UI.error "Cannot load image from shield.io skipping it...".red
elsif response_error
UI.message "Waiting for #{timeout.to_i}s and retry to load image from shield.io tries remaining: #{##retry_count}".red
sleep timeout.to_i
##retry_count -= 1
return run(path, options)
end
icon_changed = false
app_icons.each do |full_path|
icon_path = Pathname.new(full_path)
icon = MiniMagick::Image.new(full_path)
result = MiniMagick::Image.new(full_path)
if !options[:no_badge]
result = add_badge(options[:custom], options[:dark], icon, options[:alpha], alpha_channel, options[:badge_gravity])
icon_changed = true
end
if shield
result = add_shield(icon, result, shield, alpha_channel, options[:shield_gravity], options[:shield_no_resize])
icon_changed = true
end
if icon_changed
result.format "png"
result.write full_path
end
end
if icon_changed
UI.message "Badged \\o/!".green
else
UI.message "Did nothing... Enable --verbose for more info.".red
end
else
UI.error "Could not find any app icons...".red
end
end
def add_shield(icon, result, shield, alpha_channel, shield_gravity, shield_no_resize)
UI.message "'#{icon.path}'"
UI.verbose "Adding shield.io image ontop of icon".blue
current_shield = MiniMagick::Image.open(shield.path)
if icon.width > current_shield.width && !shield_no_resize
current_shield.resize "#{icon.width}x#{icon.height}<"
else
current_shield.resize "#{icon.width}x#{icon.height}>"
end
result = composite(result, current_shield, alpha_channel, shield_gravity || "north")
end
def load_shield(shield_string)
url = Badge.shield_base_url + Badge.shield_path + shield_string + ".png"
file_name = shield_string + ".png"
UI.verbose "Trying to load image from shield.io. Timeout: #{Badge.shield_io_timeout}s".blue
UI.verbose "URL: #{url}".blue
shield = Tempfile.new(file_name).tap do |file|
file.binmode
file.write(open(url).read)
file.close
end
end
def check_imagemagick!
return if `which convert`.include?('convert')
return if `which gm`.include?('gm')
UI.error("You have to install ImageMagick or GraphicsMagick to use `badge`")
UI.error("")
UI.error("Install it using (ImageMagick):")
UI.command("brew update && brew install imagemagick")
UI.error("")
UI.error("Install it using (GraphicsMagick):")
UI.command("brew update && brew install graphicsmagick")
UI.error("")
UI.error("If you don't have homebrew, visit http://brew.sh")
UI.user_error!("Install ImageMagick and start your lane again!")
end
def add_badge(custom_badge, dark_badge, icon, alpha_badge, alpha_channel, badge_gravity)
UI.message "'#{icon.path}'"
UI.verbose "Adding badge image ontop of icon".blue
if custom_badge && File.exist?(custom_badge) # check if custom image is provided
badge = MiniMagick::Image.open(custom_badge)
else
if alpha_badge
badge = MiniMagick::Image.open(dark_badge ? Badge.alpha_dark_badge : Badge.alpha_light_badge)
else
badge = MiniMagick::Image.open(dark_badge ? Badge.beta_dark_badge : Badge.beta_light_badge)
end
end
badge.resize "#{icon.width}x#{icon.height}"
result = composite(icon, badge, alpha_channel, badge_gravity || "SouthEast")
end
def composite(image, overlay, alpha_channel, gravity)
image.composite(overlay, 'png') do |c|
c.compose "Over"
c.alpha 'On' unless !alpha_channel
c.gravity gravity
end
end
end
end
I appreciate any help. Hopefully I am clear. I'm going to brush up on Ruby basics in the meantime.
I'm no XCode master, but it sounds like you don't have your XCode server or bot configured correctly.
which convert
should return the path to the 'convert' binary and the same goes for
which gm
try running those commands on your XCode server and see what you get back.
Just a guess, but you may need to add the path of those binaries to the bot's environment variables.

pod spec lint: Attempt to read non existent folder

Trying to lint a local pod spec
$ pod spec lint MyPod.podspec
I'm getting
[!] Attempt to read non existent folder /private/tmp/CocoaPods/Lint/Pods/MyPod.
I checked /private/tmp/CocoaPods/Lint/Pods/ where I didn't find my podspec indeed,
but I found it in /private/tmp/CocoaPods/Lint/Pods/Local Podspecs/
What can cause this and/or how can I debug ?
For info here is my pod spec
Pod::Spec.new do |s|
s.name = "MyPod"
s.version = "0.0.1"
s.summary = "A pretty cool pod"
s.author = { "Me" => "me#home.net" }
s.license = 'MIT'
s.homepage = "http://www.mypod.net"
s.source = { :path => "." }
s.source_files = '*.{h,m}'
s.platform = :ios, '6.0'
s.requires_arc = true
end
:path seems to cause the trouble, :git works
In my case it was caused by commented s.source line as I was testing it before with local podspec and sources. When you'll commit your podspec to the repo make sure to run pod repo update
This can also happen if you have not yet pushed your podspec's targeted tags to your origin/repo. I assume the same might also be true if you are targeting a non-existent git, branch, etc.

Resources