fwtoolkit 0.9.3 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/bin/fwt +2 -112
- data/features/cocoapods/setup.feature +60 -0
- data/features/frank/model.feature +20 -0
- data/features/frank/setup.feature +28 -0
- data/features/git/create.feature +29 -0
- data/features/project/create.feature +25 -0
- data/features/step_definitions/aruba_steps.rb +11 -0
- data/features/step_definitions/git_steps.rb +30 -0
- data/features/step_definitions/project_steps.rb +50 -0
- data/features/step_definitions/rvm_steps.rb +11 -0
- data/features/step_definitions/system_steps.rb +8 -0
- data/features/support/env.rb +23 -0
- data/features/support/lib_test/aruba_fwt.rb +30 -0
- data/features/support/lib_test/aruba_mod.rb +28 -0
- data/features/support/lib_test/fake_gem.rb +47 -0
- data/features/xcode/create.feature +42 -0
- data/lib/fwtoolkit/cli/bootstrap.rb +75 -0
- data/lib/fwtoolkit/cli/ci.rb +52 -0
- data/lib/fwtoolkit/cli/cocoapods.rb +73 -0
- data/lib/fwtoolkit/cli/ext/thor.rb +35 -0
- data/lib/fwtoolkit/cli/frank.rb +113 -0
- data/lib/fwtoolkit/cli/fw_actions/template_dir.rb +59 -0
- data/lib/fwtoolkit/cli/fw_actions.rb +2 -0
- data/lib/fwtoolkit/cli/git.rb +42 -0
- data/lib/fwtoolkit/cli/ota.rb +109 -0
- data/lib/fwtoolkit/cli/ota_client/hockeyapp_client.rb +70 -0
- data/lib/fwtoolkit/cli/project.rb +83 -0
- data/lib/fwtoolkit/cli/thorutils.rb +14 -0
- data/lib/fwtoolkit/cli/xcode.rb +105 -0
- data/lib/fwtoolkit/cli.rb +27 -36
- data/lib/fwtoolkit/config/config.sample +8 -0
- data/lib/fwtoolkit/config.rb +41 -0
- data/lib/fwtoolkit/configfile.rb +36 -0
- data/lib/fwtoolkit/executable/executable.rb +45 -0
- data/lib/fwtoolkit/executable.rb +1 -0
- data/lib/fwtoolkit/ext/gem.rb +9 -0
- data/lib/fwtoolkit/ext/hash_yaml.rb +17 -0
- data/lib/fwtoolkit/git_client/git_client.rb +225 -0
- data/lib/fwtoolkit/git_client.rb +1 -0
- data/lib/fwtoolkit/projectfile.rb +50 -0
- data/lib/fwtoolkit/rake/ext/rake.rb +7 -0
- data/lib/fwtoolkit/rake/tasks/ci.rb +25 -0
- data/lib/fwtoolkit/rake/tasks/config.rb +13 -0
- data/lib/fwtoolkit/rake/tasks/ota.rb +31 -0
- data/lib/fwtoolkit/rake/tasks/project.rb +28 -0
- data/lib/fwtoolkit/rake/tasks/test.rb +59 -0
- data/lib/fwtoolkit/rake/tasks/xcode.rb +69 -0
- data/lib/fwtoolkit/rake/tasks.rb +20 -0
- data/lib/fwtoolkit/version.rb +1 -1
- data/lib/fwtoolkit.rb +4 -7
- data/spec/git_client_spec.rb +316 -0
- data/spec/project_config_spec.rb +40 -0
- data/spec/rake/project.rb +28 -0
- data/spec/rake/test.rb +85 -0
- data/spec/rake/xcode.rb +66 -0
- data/spec/support/aruba-doubles-rspec.rb +21 -0
- data/spec/support/ctx_rake.rb +23 -0
- data/spec/support/double_helper.rb +13 -0
- data/spec/support/project_generator.rb +45 -0
- data/templates/default_project/frank/%project_name%/%target_name%/%class_prefix%AppDelegate+Frank.h.tt +15 -0
- data/templates/{cucumber/AppDelegate+Frank.m.erb → default_project/frank/%project_name%/%target_name%/%class_prefix%AppDelegate+Frank.m.tt} +25 -21
- data/templates/{cucumber → default_project/frank/%project_name%/Frank}/features/example.feature +0 -0
- data/templates/{cucumber → default_project/frank/%project_name%/Frank}/features/step_definitions/launch_steps.rb +0 -0
- data/templates/{cucumber → default_project/frank/%project_name%/Frank}/features/support/env.rb +0 -0
- data/templates/{cucumber → default_project/frank/%project_name%/Frank}/features/support/mimic.rb +0 -0
- data/templates/default_project/frank/~template_config.rb +11 -0
- data/templates/default_project/frank_seed_core_data/%project_name%/Frank/features/support/models/%class_name%.rb.tt +30 -0
- data/templates/default_project/frank_seed_support/%project_name%/Frank/features/support/models/factories.rb.tt +10 -0
- data/templates/{cucumber → default_project/frank_seed_support/%project_name%/Frank}/features/support/views/my_objects_json.erb +0 -0
- data/templates/{cucumber → default_project/frank_seed_support/%project_name%/Frank}/features/support/views/my_objects_xml.erb +0 -0
- data/templates/{fwt/gitignore.erb → default_project/git/.gitignore} +0 -0
- data/templates/{fwt/rvmrc.erb → default_project/rvm/.rvmrc.tt} +2 -2
- data/templates/default_project/rvm/Gemfile +4 -0
- data/templates/default_project/xcode/%project_name%/%project_name%.xcodeproj/project.pbxproj.tt +583 -0
- data/templates/default_project/xcode/%project_name%/%project_name%.xcodeproj/project.xcworkspace/contents.xcworkspacedata.tt +7 -0
- data/templates/default_project/xcode/%project_name%/%project_name%.xcodeproj/xcshareddata/xcschemes/%project_name%-Release.xcscheme.tt +105 -0
- data/templates/default_project/xcode/%project_name%/%project_name%.xcodeproj/xcshareddata/xcschemes/%project_name%-Testing.xcscheme.tt +105 -0
- data/templates/default_project/xcode/%project_name%/%project_name%.xcodeproj/xcshareddata/xcschemes/%project_name%.xcscheme.tt +105 -0
- data/templates/default_project/xcode/%project_name%/%target_name%/%class_prefix%AppDelegate.h.tt +15 -0
- data/templates/default_project/xcode/%project_name%/%target_name%/%class_prefix%AppDelegate.m.tt +49 -0
- data/templates/{fwt/default_project/Info.plist.erb → default_project/xcode/%project_name%/%target_name%/Supporting Files/%project_name%-Info.plist} +0 -0
- data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/%project_name%-Prefix.pch.tt +14 -0
- data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/Settings.bundle/Root.plist +53 -0
- data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/Settings.bundle/en.lproj/Root.strings +0 -0
- data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/en.lproj/InfoPlist.strings +2 -0
- data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/fw-shared.xcconfig +49 -0
- data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/info.plist.h.tt +8 -0
- data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/main.m.tt +18 -0
- data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/version.sh +159 -0
- data/templates/default_project/xcode/%project_name%/%tests_target_name%/%project_name%Tests.h.tt +13 -0
- data/templates/default_project/xcode/%project_name%/%tests_target_name%/%project_name%Tests.m.tt +32 -0
- data/templates/default_project/xcode/%project_name%/%tests_target_name%/Supporting Files/%tests_target_name%-Info.plist +22 -0
- data/templates/default_project/xcode/%project_name%/%tests_target_name%/Supporting Files/en.lproj/InfoPlist.strings +2 -0
- data/templates/default_project/xcode/%project_name%/Default.png +0 -0
- data/templates/default_project/xcode/%project_name%/Resources/Default-568h@2x.png +0 -0
- data/templates/default_project/xcode/%project_name%/Resources/Default.png +0 -0
- data/templates/default_project/xcode/%project_name%/Resources/Default@2x.png +0 -0
- data/templates/default_project/xcode/%project_name%.xcworkspace/contents.xcworkspacedata.tt +7 -0
- data/templates/default_project/xcode/FWProjectfile.tt +14 -0
- data/templates/default_project/xcode/Podfile.tt +4 -0
- data/templates/default_project/xcode/Rakefile.tt +14 -0
- data/templates/default_project/xcode/cruise_config.rb +3 -0
- data/templates/default_project/xcode/~template_config.rb +5 -0
- metadata +187 -141
- data/lib/fwtoolkit/tasks/build.rb +0 -121
- data/lib/fwtoolkit/tasks/ci.rb +0 -30
- data/lib/fwtoolkit/tasks/cocoapods.rb +0 -32
- data/lib/fwtoolkit/tasks/frank.rb +0 -119
- data/lib/fwtoolkit/tasks/helper.rb +0 -16
- data/lib/fwtoolkit/tasks/services.rb +0 -41
- data/lib/fwtoolkit/tasks.rb +0 -24
- data/lib/fwtoolkit/test/frank_model.rb +0 -120
- data/lib/fwtoolkit/test/misc_steps.rb +0 -9
- data/lib/fwtoolkit/test/model_helper.rb +0 -94
- data/lib/fwtoolkit/test/network_steps.rb +0 -60
- data/lib/fwtoolkit/test/pickle_steps.rb +0 -101
- data/lib/fwtoolkit/test/timeout_helper.rb +0 -21
- data/lib/fwtoolkit/test/ui_helper.rb +0 -19
- data/lib/fwtoolkit/test/ui_steps.rb +0 -17
- data/templates/cucumber/AppDelegate+Frank.h.erb +0 -15
- data/templates/fwt/Gemfile.erb +0 -3
- data/templates/fwt/Podfile.erb +0 -4
- data/templates/fwt/Rakefile.erb +0 -24
- data/templates/fwt/default_project/AppDelegate.h.erb +0 -12
- data/templates/fwt/default_project/AppDelegate.m.erb +0 -20
- data/templates/fwt/default_project/Prefix.pch.erb +0 -6
- data/templates/fwt/default_project/main.m.erb +0 -15
- data/templates/models/factories.rb.erb +0 -10
- data/templates/models/model.rb.erb +0 -30
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'fwtoolkit/config'
|
3
|
+
require 'fwtoolkit/cli/cocoapods'
|
4
|
+
|
5
|
+
module FWToolkit
|
6
|
+
class Bootstrap < Thor::Group
|
7
|
+
include Thor::Actions
|
8
|
+
|
9
|
+
def xcode_check
|
10
|
+
if File::exists?('/Applications/Xcode.app')
|
11
|
+
say_status :skip, 'Xcode is already installed', :blue
|
12
|
+
elsif
|
13
|
+
say_status :error, 'Xcode is not installed. Please download it from developer.apple.com', :red
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def git_check
|
18
|
+
if File.available_in_path?('git')
|
19
|
+
say_status :skip, 'Git is already installed', :blue
|
20
|
+
elsif
|
21
|
+
say_status :error, 'Git is not installed. Please review your system configuration', :red
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def rvm_check
|
26
|
+
if File.available_in_path?('rvm')
|
27
|
+
say_status :skip, 'Rvm is already installed', :blue
|
28
|
+
elsif
|
29
|
+
say_status :install, 'Installing rvm from https://get.rvm.io', :green
|
30
|
+
Dir.mktmpdir do |dir|
|
31
|
+
installer_path = File.join(dir, 'rvm_install.sh');
|
32
|
+
get 'https://get.rvm.io', installer_path
|
33
|
+
run! "bash -s stable #{installer_path}", {:capture => true}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def ruby_ver_check
|
39
|
+
default_rvm_ruby = run! 'rvm list default string', {:capture => true}
|
40
|
+
if default_rvm_ruby.include? Config.ruby_version
|
41
|
+
say_status :skip, "Ruby is already at the last version (#{Config.ruby_version})", :blue
|
42
|
+
end
|
43
|
+
say_status :install, "Installing ruby version #{Config.ruby_version} (through rvm)", :green
|
44
|
+
run! "rvm use --install #{Config.ruby_version}", {:with => 'bash -lc', :capture => true}
|
45
|
+
end
|
46
|
+
|
47
|
+
def cocoapods_check
|
48
|
+
if File.available_in_path?('pods')
|
49
|
+
say_status :skip, 'Cocoapods is already installed', :blue
|
50
|
+
elsif
|
51
|
+
invoke FWToolkit::Cocoapods, 'install', []
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class File
|
58
|
+
|
59
|
+
def available_in_path?(prog_name)
|
60
|
+
which(prog_name)!=nil
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def which cmd
|
66
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
67
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
68
|
+
exts.each do |ext|
|
69
|
+
exe = "#{path}/#{cmd}#{ext}"
|
70
|
+
return exe if File.executable? exe
|
71
|
+
end
|
72
|
+
end
|
73
|
+
return nil
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
4
|
+
require 'fwtoolkit/git_client'
|
5
|
+
require 'fwtoolkit/config'
|
6
|
+
|
7
|
+
module FWToolkit
|
8
|
+
class Ci < Thor
|
9
|
+
include Thor::Actions
|
10
|
+
|
11
|
+
desc 'add [PROJECT_NAME] [PROJECT_DIR]', 'Add a project to the CI environment'
|
12
|
+
def add(project_name, project_root)
|
13
|
+
raise Thor::Error, "\"#{project_root}\" doesn't contain a valid git repository" unless repository.initialized?
|
14
|
+
|
15
|
+
if project_exists_on_ci? project_name
|
16
|
+
say_status :skip, "There's already a project named \"#{project_name}\" on the ci at #{Config.ci_server_url}"
|
17
|
+
return
|
18
|
+
end
|
19
|
+
|
20
|
+
remotes = repository.remotes
|
21
|
+
raise Thor::Error, "\"#{project_root}\" is a local-only repository" if remotes.to_a.size == 0
|
22
|
+
|
23
|
+
remotes = repository.remotes
|
24
|
+
if remotes.keys.count == 1
|
25
|
+
remote_name = remotes.values.first
|
26
|
+
else
|
27
|
+
remote_name = ask 'There\'s more than one remote configured for this repository. Select one:', {:limited_to => remotes.keys }
|
28
|
+
end
|
29
|
+
|
30
|
+
remote_url = remotes[remote_name]
|
31
|
+
|
32
|
+
response = Net::HTTP.post_form(URI.parse("#{Config.ci_server_url}/projects"),
|
33
|
+
{'project[name]' => project_name,
|
34
|
+
'project[source_control][repository]' => remote_url,
|
35
|
+
'project[source_control][source_contro]' => 'Git',
|
36
|
+
'project[source_control][branch]' => 'dev'})
|
37
|
+
if response.kind_of? Net::HTTPSuccess
|
38
|
+
say_status :add, "Project \"#{project_name}\" successfully added to the CI environment", :green
|
39
|
+
elsif
|
40
|
+
raise Thor::Error, "Failed to add project \"#{project_name}\" to the CI"
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def project_exists_on_ci?(project_name)
|
47
|
+
response = Net::HTTP.request_get URI.parse("#{Config.ci_server_url}/builds/#{project_name}")
|
48
|
+
response.kind_if? Net::HTTPSuccess
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'fwtoolkit/cli/ext/thor'
|
3
|
+
require 'fwtoolkit/ext/gem'
|
4
|
+
|
5
|
+
module FWToolkit
|
6
|
+
class Cocoapods < Thor
|
7
|
+
include Thor::Actions
|
8
|
+
|
9
|
+
desc 'setup', 'Install and configure cocoapods for the current user'
|
10
|
+
option :'skip-install', :desc => 'Skip cocoapods gem install and only configure the fw repository'
|
11
|
+
def setup
|
12
|
+
install_cocoapods_gem unless options[:'skip-install']
|
13
|
+
setup_cocoapods_repos
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'install [PROJECT_DIR]', 'Installs pods for the project at project_dir'
|
17
|
+
def install(project_root)
|
18
|
+
add_cocoapods_to_project project_root
|
19
|
+
if File.exists? File.join(project_root, 'Podfile')
|
20
|
+
inside(project_root) { run! 'bundle exec pod install', :capture => true }
|
21
|
+
else
|
22
|
+
say_status :skip, "Unable to locate the Podfile. Skipping pod install", :blue
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def install_cocoapods_gem
|
30
|
+
if inside(ENV['HOME']) {Gem::gem_available? 'cocoapods'}
|
31
|
+
say_status :skip, 'Cocoapods is already installed', :blue
|
32
|
+
else
|
33
|
+
say_status :install, 'Installing cocoapods gem system-wise (requires root password)', :green
|
34
|
+
run! 'sudo gem install cocoapods', {:capture => true}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def setup_cocoapods_repos
|
39
|
+
# I'm avoiding calling cocoapods directly in order not to need them as a dependecy for this set of commands
|
40
|
+
say_status :warning, 'Cocoapods doesn\'t seem to be installed in your system. Please run fwt pods setup', :yellow if !Gem::gem_available?('cocoapods')
|
41
|
+
fw_repo_path = File.join(ENV['CP_REPOS_DIR'] || "~/.cocoapods", 'fw')
|
42
|
+
if Dir.exists? fw_repo_path
|
43
|
+
say_status :skip, 'FW\'s podspec repository already installed', :blue
|
44
|
+
else
|
45
|
+
say_status :configure, 'Configuring FW\'s podspec repository', :green
|
46
|
+
FileUtils.mkdir_p fw_repo_path
|
47
|
+
inside(fw_repo_path) { run! 'git clone git@github.com:FutureWorkshops/FWTPodspecs.git .', :capture => true }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_cocoapods_to_project(project_root)
|
52
|
+
gemfile = File.join(project_root, 'Gemfile')
|
53
|
+
raise Thor::Error, "Can't locate a valid Gemfile at path: #{File.expand_path project_root}" unless File.exist?(gemfile)
|
54
|
+
|
55
|
+
unless cocoapods_in_gemfile? gemfile
|
56
|
+
say_status :add, 'Add cocoapods to the Gemfile', :blue
|
57
|
+
File.open(gemfile, "a") { |f| f.write 'gem "Cocoapods"' }
|
58
|
+
run! 'bundle', :capture => true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def cocoapods_in_gemfile?(gemfile_path)
|
63
|
+
File.read(gemfile_path).split("\n").select{ |line| line =~ /gem .Cocoapods./ }.count == 0
|
64
|
+
end
|
65
|
+
|
66
|
+
# def gemfile_gems_installed?(project_root)
|
67
|
+
# inside(project_root) do
|
68
|
+
# system('bundle check')
|
69
|
+
# $?.success?
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
class Thor
|
4
|
+
module Actions
|
5
|
+
alias_method :run_base, :run
|
6
|
+
def run(command, config={})
|
7
|
+
command_output = run_base(command, config)
|
8
|
+
if config[:raise_errors] && !$?.success?
|
9
|
+
command_output.gsub!(/e\[\d+m/, '') #Stripping colors
|
10
|
+
say "An error has occured while running: \"#{command}\"", :red
|
11
|
+
say 'Command output:'
|
12
|
+
raise Thor::Error, "\n***\n#{command_output}***"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def run!(command, config={})
|
17
|
+
config.merge!({ :raise_errors => true })
|
18
|
+
run command, config
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
class Thor
|
25
|
+
module Actions
|
26
|
+
class EmptyDirectory
|
27
|
+
protected
|
28
|
+
def convert_encoded_instructions(filename)
|
29
|
+
filename.gsub(/%(.*?)%/) do |initial_string|
|
30
|
+
config[$1.strip.to_sym] or call_public_method($1.strip) or initial_string
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'xcodeproj'
|
3
|
+
require 'core_data'
|
4
|
+
require 'fwtoolkit/ext/gem'
|
5
|
+
require 'fwtoolkit/projectfile'
|
6
|
+
require 'fwtoolkit/cli/fw_actions'
|
7
|
+
require 'fwtoolkit/cli/thorutils'
|
8
|
+
|
9
|
+
module Xcodeproj
|
10
|
+
class Project
|
11
|
+
def core_data_files(target_name=nil)
|
12
|
+
mom_files = Array.new
|
13
|
+
targets_list = target_name ? self.targets.select { |t| t.name == target_name } : self.targets
|
14
|
+
targets_list.each do |t|
|
15
|
+
mom_files << t.source_build_phase.files.select{ |f| f.display_name =~ /.*?\.xcdatamodeld/ }.map{ |f| f.file_ref.current_version.path }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class String
|
22
|
+
def ruby_format
|
23
|
+
self.gsub(/(.)([A-Z])/,'\1_\2').downcase
|
24
|
+
end
|
25
|
+
|
26
|
+
def ruby_format!
|
27
|
+
replace ruby_format
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module FWToolkit
|
32
|
+
class Frank < Thor
|
33
|
+
include Thor::Actions
|
34
|
+
include FWToolkit::ThorUtils
|
35
|
+
source_root_templates!
|
36
|
+
|
37
|
+
add_runtime_options!
|
38
|
+
|
39
|
+
desc 'setup [PROJECT_DIR]', 'Sets up Frank for the project at project_dir and on the given target'
|
40
|
+
option :target,
|
41
|
+
:banner => 'Xcode target',
|
42
|
+
:desc => 'Specify the build target that Frank should use. Default: the one specified by :target in the FWProjectFile'
|
43
|
+
def setup(project_dir)
|
44
|
+
destination_root = project_dir
|
45
|
+
Projectfile.load!(project_dir)
|
46
|
+
|
47
|
+
if !options[:force] && frank_alredy_set_up?(project_dir)
|
48
|
+
say_status :skip, 'Frank is already set up for the project'
|
49
|
+
return
|
50
|
+
end
|
51
|
+
say 'Setting up Frank'
|
52
|
+
|
53
|
+
template_conf = Projectfile.config
|
54
|
+
Projectfile.config[:target_name] ||= options[:target] if options[:target]
|
55
|
+
|
56
|
+
inside(File.join(destination_root, template_conf[:project_name])) { run! "frank setup --target #{Projectfile.target_name}" }
|
57
|
+
|
58
|
+
#remove files we're going to overwrite
|
59
|
+
FileUtils.rm File.join(destination_root, template_conf[:project_name], 'Frank', 'features', 'step_definitions', 'launch_steps.rb')
|
60
|
+
FileUtils.rm File.join(destination_root, template_conf[:project_name], 'Frank', 'features', 'support', 'env.rb')
|
61
|
+
|
62
|
+
template_directory 'templates/default_project/frank', destination_root, template_conf
|
63
|
+
end
|
64
|
+
|
65
|
+
desc 'model [PROJECT_DIR]', 'Creates Frank model files from CoreData for the project at project_dir'
|
66
|
+
option :modelfile,
|
67
|
+
:banner => 'core data xcdatamodel file',
|
68
|
+
:desc => 'Specify the position of the xcdatamodel file you want to extract the Core Data entity from. By default, fwtoolkit searches recursively inside project_dir for all *.xcdatamodel files'
|
69
|
+
def model(project_dir)
|
70
|
+
say 'Creating model files out of core data entities'
|
71
|
+
destination_root = project_dir
|
72
|
+
Projectfile.load!(project_dir)
|
73
|
+
|
74
|
+
if options[:modelfile].nil?
|
75
|
+
mom_files = Dir.glob(File.join(project_dir, '**/*.xcdatamodel'))
|
76
|
+
|
77
|
+
if mom_files.size > 1
|
78
|
+
mom_file = ask('I\'ve found more than one core data model, which one would you like to use?', mom_files)
|
79
|
+
elsif mom_files.size == 1
|
80
|
+
mom_file = mom_files[0]
|
81
|
+
else
|
82
|
+
raise Thor::Error, "Can't locate a valid core data model for #{mom_file}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
template_conf = Projectfile.config
|
87
|
+
|
88
|
+
model = CoreData::DataModel.new File.join(mom_file, 'contents')
|
89
|
+
|
90
|
+
model.entities.each do |entity|
|
91
|
+
seed_conf = template_conf.clone
|
92
|
+
seed_conf[:class_name] = remove_prefix(template_conf[:class_prefix], entity.name).ruby_format
|
93
|
+
seed_conf[:model] = entity
|
94
|
+
directory 'templates/default_project/frank_seed_core_data', destination_root, seed_conf
|
95
|
+
end
|
96
|
+
|
97
|
+
template_conf[:models] = model.entities
|
98
|
+
directory 'templates/default_project/frank_seed_support', destination_root, template_conf
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def remove_prefix(prefix, entity)
|
104
|
+
entity[/^#{prefix}/] = '' if entity[/^#{prefix}/]
|
105
|
+
entity
|
106
|
+
end
|
107
|
+
|
108
|
+
def frank_alredy_set_up?(project_dir)
|
109
|
+
File.exists? File.join(project_dir, File.basename(project_dir), 'Frank')
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'thor/actions'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
class Thor
|
7
|
+
module Actions
|
8
|
+
def template_directory(source, *args, &block)
|
9
|
+
config = args.last.is_a?(Hash) ? args.pop : {}
|
10
|
+
destination = args.first || source
|
11
|
+
action TemplateDirectory.new(self, source, destination || source, config, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
class TemplateDirectory < Directory
|
15
|
+
|
16
|
+
module DSL
|
17
|
+
class Support
|
18
|
+
include Thor::Actions
|
19
|
+
include Singleton
|
20
|
+
def initialize
|
21
|
+
super nil, options, config
|
22
|
+
end
|
23
|
+
end
|
24
|
+
def project_config
|
25
|
+
yield(Thor::Util.escape_globs(destination), config)
|
26
|
+
end
|
27
|
+
|
28
|
+
def support
|
29
|
+
Support.instance
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
include DSL
|
34
|
+
|
35
|
+
protected
|
36
|
+
def execute!
|
37
|
+
super
|
38
|
+
|
39
|
+
lookup = Thor::Util.escape_globs(destination)
|
40
|
+
lookup = file_level_lookup(lookup)
|
41
|
+
|
42
|
+
files(lookup).select{|f| f =~ /~template_config.rb/ }.each do |file_source|
|
43
|
+
conf = File.open(file_source, 'r:utf-8') { |f| f.read }
|
44
|
+
# Work around for Rubinius incomplete encoding in 1.9 mode
|
45
|
+
if conf.respond_to?(:encoding) && conf.encoding.name != "UTF-8"
|
46
|
+
conf.encode!('UTF-8')
|
47
|
+
end
|
48
|
+
base.shell.say_status :executing, "configuration file: #{base.relative_to_original_destination_root(file_source)}", :green
|
49
|
+
begin
|
50
|
+
eval(conf, binding, file_source)
|
51
|
+
FileUtils.rm file_source
|
52
|
+
rescue Exception => e
|
53
|
+
raise Thor::Error, "Invalid `#{file_source}` file: #{e.message}", e.backtrace
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'fwtoolkit/cli/fw_actions'
|
3
|
+
require 'fwtoolkit/git_client'
|
4
|
+
require 'fwtoolkit/cli/thorutils'
|
5
|
+
|
6
|
+
module FWToolkit
|
7
|
+
class Git < Thor
|
8
|
+
include Thor::Actions
|
9
|
+
include FWToolkit::ThorUtils
|
10
|
+
|
11
|
+
source_root_templates!
|
12
|
+
|
13
|
+
desc 'new', 'Creates a git repository and commits to master all the files in project_dir'
|
14
|
+
def new(project_root)
|
15
|
+
destination_root = project_root
|
16
|
+
repository = GitClient::Repository.new(project_root)
|
17
|
+
raise Thor::Error, "There's already a repository at path: \"#{project_root}\"" if repository.initialized?
|
18
|
+
|
19
|
+
template_directory 'templates/default_project/git', destination_root
|
20
|
+
begin
|
21
|
+
repository.init
|
22
|
+
repository.add_files_to_index
|
23
|
+
repository.commit('First commit')
|
24
|
+
rescue GitClient::GitError => e
|
25
|
+
raise Thor::Error, e.message + "\n#{e.git_output}"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'update', 'Updates submodules for the repository in project_dir'
|
31
|
+
def update(project_root)
|
32
|
+
repository = GitClient::Repository.new(project_root)
|
33
|
+
raise Thor::Error, "There's no initialized repository at path: #{project_root}" unless git_repo.initialized?
|
34
|
+
|
35
|
+
begin
|
36
|
+
repository.submodule_update :init => true
|
37
|
+
rescue GitError => e
|
38
|
+
raise Thor::Error, e.message + "\n#{e.git_output}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'rake'
|
3
|
+
require 'fwtoolkit/projectfile'
|
4
|
+
require 'fwtoolkit/xcode'
|
5
|
+
require 'fwtoolkit/config'
|
6
|
+
require 'fwtoolkit/hockeyapp_client'
|
7
|
+
require 'fwtoolkit/hash_yaml'
|
8
|
+
|
9
|
+
module XcodedBuild
|
10
|
+
def self.create_ipa(xcarchive_path, ipa_output, dev_id=nil, provisionin_profile=nil)
|
11
|
+
provisioning_profile = File.expand_path provisioning_profile
|
12
|
+
raise Thor::Error, "Unable to locate the provisioning profile: #{provisioning_profile}" unless File.exists?(provisioning_profile)
|
13
|
+
|
14
|
+
app_path = find_unique! xcarchive_path, '*.app'
|
15
|
+
raise Thor::Error, "Unable to find any .app file at path: #{xcarchive_path}" unless app_path
|
16
|
+
|
17
|
+
ipa_output = File.expand_path ipa_output
|
18
|
+
|
19
|
+
cmd_line = "xcrun -sdk iphoneos PackageApplication -v #{app_path} -o #{ipa_output}"
|
20
|
+
cmd_line += "--sign #{dev_id}" if dev_id
|
21
|
+
cmd_line += "--embed #{provisioning_profile}" if provisioning_profile
|
22
|
+
run! "xcrun -sdk iphoneos PackageApplication -v #{app_path} -o #{ipa_output}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.find_unique!(root, file)
|
26
|
+
files_in_root = Dir["#{root}/**/#{file}"]
|
27
|
+
raise Thor::Error, "Found more than one \"#{file}\" at path: #{root}" if apps_in_root.size > 1
|
28
|
+
files_in_root.size > 0 ? File.expand_path(files_in_root.first) : nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module FWToolkit
|
33
|
+
class Ota < Thor
|
34
|
+
include Thor::Actions
|
35
|
+
|
36
|
+
desc 'ipa [PROJECT_ROOT]', 'Creates an ipa file signed with the credentials defined in the FWProjectfile for the project at project_root'
|
37
|
+
option :sign,
|
38
|
+
:type => :boolean,
|
39
|
+
:default => :true,
|
40
|
+
:desc => 'If true, the dev_id defined in the project file is ignored and the app is signed with the dev_id defined on FWProjectfile. Default: true'
|
41
|
+
def sign(project_root)
|
42
|
+
invoke FWToolkit::Xcode, 'build', [project_root], :clean => true, :archive => true, :type => 'testing', :sdk => 'iphoneos'
|
43
|
+
Projectfile.load! project_root
|
44
|
+
|
45
|
+
if options[:sign]
|
46
|
+
XcodeBuild.create_ipa xcarchive_path, ipa_path, Projectfile.dev_id, Projectfile.provisioning_profile
|
47
|
+
else
|
48
|
+
XcodeBuild.create_ipa xcarchive_path, ipa_path
|
49
|
+
end
|
50
|
+
|
51
|
+
say_status :created, "IPA at: #{ipa_path} signed with developer id: #{dev_id}", :green
|
52
|
+
end
|
53
|
+
|
54
|
+
desc 'upload [PROJECT_ROOT]', 'Upload a new build to FW\'s Hockeyapp account'
|
55
|
+
option :sign,
|
56
|
+
:type => :boolean,
|
57
|
+
:default => true,
|
58
|
+
:desc => 'If true, the dev_id defined in the project file is ignored and the app is signed with the dev_id defined on FWProjectfile. Default: true'
|
59
|
+
def upload(project_root)
|
60
|
+
if options[:sign]
|
61
|
+
invoke('sign')
|
62
|
+
else
|
63
|
+
invoke FWToolkit::Xcode, 'build', [project_root], :clean => true, :archive => true, :type => 'testing', :sdk => 'iphoneos'
|
64
|
+
end
|
65
|
+
|
66
|
+
Projectfile.load! project_root
|
67
|
+
|
68
|
+
raise Thor::Error, "Unable to find the xcarchive file at path: #{ipa}" unless File.exists?(ipa_path)
|
69
|
+
raise Thor::Error, "Unable to find the dSYM file at path: #{dsym}" unless File.exists?(dsym_path)
|
70
|
+
|
71
|
+
uploader = FWToolkit::OTA::HockeyappClient.new(Config.hockeyapp_api_token, Projectfile.app_token)
|
72
|
+
|
73
|
+
begin
|
74
|
+
uploader.upload_build ipa_path, dsym_path
|
75
|
+
rescue StandardError => e
|
76
|
+
say_status :error, "Error uploading to hockeyapp: #{e.message}", :red
|
77
|
+
end
|
78
|
+
|
79
|
+
say_status :upload, "Uploaded a new version to Hockeyapp", :green
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def ipa_path
|
86
|
+
File.join xcarchive_path, "#{Projectfile.project_name}.ipa"
|
87
|
+
end
|
88
|
+
|
89
|
+
def dsym_path
|
90
|
+
File.join xcarchive_path, 'dSYMs' "#{Projectfile.project_name}.app.dSYM"
|
91
|
+
end
|
92
|
+
|
93
|
+
def artifacts_dir
|
94
|
+
File.join Config.artifacts_tmp_dir, Projectfile.project_name
|
95
|
+
end
|
96
|
+
|
97
|
+
def xcarchive_path
|
98
|
+
if @xcarchive_path.nil
|
99
|
+
build_conf = Hash.hash_from_yaml File.join(Config.artifacts_tmp_dir, Projectfile.project_name, 'env.log')
|
100
|
+
|
101
|
+
@xcarchive_path = build_conf[:ARCHIVE_PATH]
|
102
|
+
raise Thor::Error, "Cannot locate the xcarchive for the project" unless File.exists?(xcarchive)
|
103
|
+
end
|
104
|
+
@xcarchive_path
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'hockeyapp'
|
2
|
+
require 'fwtoolkit/config'
|
3
|
+
require 'rubyzip'
|
4
|
+
|
5
|
+
module FWToolkit
|
6
|
+
module OTA
|
7
|
+
class HockeyappClient
|
8
|
+
|
9
|
+
class CmdUploader
|
10
|
+
def initialize(api_token, app_id)
|
11
|
+
HockeyApp::Config.configure { |config| config.token = api_token }
|
12
|
+
@client = HockeyApp.build_client
|
13
|
+
@app = HockeyApp::App.from_hash({"public_identifier" => app_id}, @client)
|
14
|
+
end
|
15
|
+
|
16
|
+
def upload_build(bin_file, dsym_file = nil, notes = nil)
|
17
|
+
raise ArgumentError, "No valid ipa file exists at path #{bin_file}" unless bin_file.exists?
|
18
|
+
raise ArgumentError, "No valid dsym_file file exists at path #{dsym_file}" unless (dsym_file.nil? || dsym_file.exists?)
|
19
|
+
zip_dsym = zipped_dsym(dsym_file) if dsym_file
|
20
|
+
new_version = HockeyApp::Version.new(@app, @client)
|
21
|
+
new_version.ipa = bin_file
|
22
|
+
new_version.dsym = zipped_dsym(zip_dsym)
|
23
|
+
new_version.notes = notes
|
24
|
+
@client.post_new_version new_version
|
25
|
+
end
|
26
|
+
|
27
|
+
def dsym_path_from_xcarchive(xcarchive_path)
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def
|
32
|
+
end
|
33
|
+
|
34
|
+
# class GuiUploader
|
35
|
+
# def initialize(api_token, app_id)
|
36
|
+
# @api_token = api_token
|
37
|
+
# @app_id = app_id
|
38
|
+
# end
|
39
|
+
|
40
|
+
# def upload_build(bin_file, dsym_file = nil, notes = nil)
|
41
|
+
# raise ArgumentError, 'Not implemented'
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
|
45
|
+
# There are two kind of uploader: internal and gui. The second one utilizes hockeyapp's mac app to provide
|
46
|
+
def initialize(api_token, app_id, uploader=:cmd_uploader)
|
47
|
+
@uploader = uploader==:cmd_uploader ? CmdUploader.new(api_token, app_id) : GuiUploader.new(api_token, app_id)
|
48
|
+
end
|
49
|
+
|
50
|
+
def upload_build(bin_file, dsym_file = nil, notes = nil)
|
51
|
+
@uploader.upload_build bin_file, dsym_file, notes
|
52
|
+
raise ArgumentError, "No valid ipa file exists at path #{bin_file}" unless bin_file.exists?
|
53
|
+
raise ArgumentError, "No valid dsym_file file exists at path #{dsym_file}" unless (dsym_file.nil? || dsym_file.exists?)
|
54
|
+
zip_dsym = zipped_dsym(dsym_file) if dsym_file
|
55
|
+
new_version = HockeyApp::Version.new(@app, @client)
|
56
|
+
new_version.ipa = bin_file
|
57
|
+
new_version.dsym = zipped_dsym(zip_dsym)
|
58
|
+
new_version.notes = notes
|
59
|
+
@client.post_new_version new_version
|
60
|
+
end
|
61
|
+
|
62
|
+
def zipped_dsym(dsym_file)
|
63
|
+
zip_file = "#{dsym_file}.zip"
|
64
|
+
Zip::ZipFile.open(zip_file, Zip::ZipFile::CREATE) do |zipfile|
|
65
|
+
zipfile.add(dsym_file)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|