Class: Fastlane::Helper::SettingsBundleHelper

Inherits:
Object
  • Object
show all
Defined in:
lib/fastlane/plugin/settings_bundle/helper/settings_bundle_helper.rb

Defined Under Namespace

Classes: Settings

Class Method Summary collapse

Class Method Details

.expand_macros(target, setting_value, configuration) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/fastlane/plugin/settings_bundle/helper/settings_bundle_helper.rb', line 166

def expand_macros(target, setting_value, configuration)
  # TODO: Properly balance these delimiters. Currently it will also match
  # $(SETTING} and ${SETTING). See the pending spec.
  matches = /\$[{(]([^})]+)[})]/.match(setting_value)
  return setting_value if matches.nil?

  macro_name = matches[1]
  return setting_value if macro_name.nil?

  expanded_macro = macro_name == "SRCROOT" ? "." : expanded_build_setting(target, macro_name, configuration)
  return setting_value if expanded_macro.nil?

  setting_value.gsub!(/\$[{(]#{macro_name}[})]/, expanded_macro)

  expand_macros target, setting_value, configuration
end

.expanded_build_setting(target, setting_name, configuration) ⇒ Object



158
159
160
161
162
163
164
# File 'lib/fastlane/plugin/settings_bundle/helper/settings_bundle_helper.rb', line 158

def expanded_build_setting(target, setting_name, configuration)
  setting_values = target.resolved_build_setting setting_name
  return if setting_values.nil?
  setting_value = setting_values[configuration]
  return if setting_value.nil?
  expand_macros target, setting_value, configuration
end

.formatted_value(value, settings) ⇒ Object

Takes a value, a version number and a build number and returns a formatted string. :version is replaced by the version number. :build is replaced by the build number. Neither the version nor the build number is required. Omitting both from the format will result in the format being returned as the value.

:value: A string value containing :version, :build, neither or both :settings: A Settings struct containing settings from a project



39
40
41
# File 'lib/fastlane/plugin/settings_bundle/helper/settings_bundle_helper.rb', line 39

def formatted_value(value, settings)
  value.gsub(/:version/, settings.version.to_s).gsub(/:build/, settings.build.to_s)
end

.settings_from_project(project, configuration, target_name) ⇒ Object

Takes an open Xcodeproj::Project and extracts the current settings, returning a Settings struct with settings data. Raises on error.

:project: An open Xcodeproj::Project via Xcodeproj::Project.open, e.g. :configuration: A valid build configuration in the project :target_name: A valid target name in the project or nil to use the first application target



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/fastlane/plugin/settings_bundle/helper/settings_bundle_helper.rb', line 50

def settings_from_project(project, configuration, target_name)
  if target_name
    target = project.targets.find { |t| t.name == target_name }
    raise "Target named \"#{target_name}\" not found" if target.nil?
  else
    # find the first non-test, non-extension target
    # TODO: Make this a :target parameter
    target = project.targets.find { |t| !t.test_target_type? && !t.extension_target_type? }
    raise "No application target found" if target.nil?
  end

  # this can differ from one configuration to another.
  # take from Release, since only one Settings.bundle per project
  # (not per configuration)
  release_info_plist_path = expanded_build_setting target, "INFOPLIST_FILE", configuration

  raise "Info.plist not found for configuration #{configuration}" if release_info_plist_path.nil?

  project_parent = File.dirname project.path

  release_info_plist_path = File.join project_parent, release_info_plist_path

  # try to open and parse the Info.plist. raises on failure.
  info_plist = File.open(release_info_plist_path) { |f| Plist.parse_xml f }
  raise "Failed to parse plist file #{release_info_plist_path}" if info_plist.nil?

  # increments already happened. read the current state.
  current_marketing_version = info_plist["CFBundleShortVersionString"]
  current_build_number = info_plist["CFBundleVersion"]

  raise "CFBundleShortVersionString not found in Info.plist" if current_marketing_version.nil?
  raise "CFBundleVersion not found in Info.plist" if current_build_number.nil?

  Settings.new current_marketing_version, current_build_number
end

.update_settings_plist_title_setting(project, bundle_name, file, key, value) ⇒ Object

Takes an open Xcodeproj::Project, extracts the settings bundle and updates the specified setting key in the specified file to the specified value. Only valid for title items. Raises on error.

:project: An open Xcodeproj::Project, obtained from Xcodeproj::Project.open, e.g. :bundle_name: (String) Regex to identify the bundle to look for, usually Settings.bundle. :file: A settings plist file in the Settings.bundle, usually “Root.plist” :key: A valid NSUserDefaults key in the Settings.bundle :value: A new value for the key



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/fastlane/plugin/settings_bundle/helper/settings_bundle_helper.rb', line 96

def update_settings_plist_title_setting(project, bundle_name, file, key, value)
  settings_bundle = project.files.find { |f| f.path =~ /#{bundle_name}/ }

  raise "#{bundle_name} not found in project" if settings_bundle.nil?

  # The #real_path method returns the full resolved path to the Settings.bundle
  settings_bundle_path = settings_bundle.real_path

  plist_path = File.join settings_bundle_path, file

  # raises IOError
  settings_plist = File.open(plist_path) { |f| Plist.parse_xml f }

  raise "Could not parse #{plist_path}" if settings_plist.nil?

  preference_specifiers = settings_plist["PreferenceSpecifiers"]

  raise "#{file} is not a settings plist file" if preference_specifiers.nil?

  # Find the specifier for the supplied key
  title_specifier = preference_specifiers.find do |specifier|
    specifier["Key"] == key
  end

  raise "preference specifier for key #{key} not found in #{file}" if title_specifier.nil?
  raise "preference for key #{key} must be of type title" unless title_specifier["Type"] == "PSTitleValueSpecifier"

  # Update to the new value. Old value need not be present.
  title_specifier["DefaultValue"] = value.to_s

  # Save (raises)
  Plist::Emit.save_plist settings_plist, plist_path
end

.xcodeproj_path_from_params(params) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/fastlane/plugin/settings_bundle/helper/settings_bundle_helper.rb', line 130

def xcodeproj_path_from_params(params)
  return params[:xcodeproj] if params[:xcodeproj]

  # Adapted from commit_version_bump
  # https://github.com/fastlane/fastlane/blob/master/fastlane/lib/fastlane/actions/commit_version_bump.rb#L21

  # This may not be a git project. Search relative to the Gemfile.
  repo_path = Bundler.root

  all_xcodeproj_paths = Dir[File.expand_path(File.join(repo_path, '**/*.xcodeproj'))]
  # find an xcodeproj (ignoring the Cocoapods one)
  xcodeproj_paths = Fastlane::Actions.ignore_cocoapods_path(all_xcodeproj_paths)

  # no projects found: error
  UI.user_error!('Could not find a .xcodeproj in the current repository\'s working directory.') and return nil if xcodeproj_paths.count == 0

  # too many projects found: error
  if xcodeproj_paths.count > 1
    repo_pathname = Pathname.new repo_path
    relative_projects = xcodeproj_paths.map { |e| Pathname.new(e).relative_path_from(repo_pathname).to_s }.join("\n")
    UI.user_error!("Found multiple .xcodeproj projects in the current repository's working directory. Please specify your app's main project: \n#{relative_projects}")
    return nil
  end

  # one project found: great
  xcodeproj_paths.first
end