fwtoolkit 0.9.3 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +8 -8
  2. data/bin/fwt +2 -112
  3. data/features/cocoapods/setup.feature +60 -0
  4. data/features/frank/model.feature +20 -0
  5. data/features/frank/setup.feature +28 -0
  6. data/features/git/create.feature +29 -0
  7. data/features/project/create.feature +25 -0
  8. data/features/step_definitions/aruba_steps.rb +11 -0
  9. data/features/step_definitions/git_steps.rb +30 -0
  10. data/features/step_definitions/project_steps.rb +50 -0
  11. data/features/step_definitions/rvm_steps.rb +11 -0
  12. data/features/step_definitions/system_steps.rb +8 -0
  13. data/features/support/env.rb +23 -0
  14. data/features/support/lib_test/aruba_fwt.rb +30 -0
  15. data/features/support/lib_test/aruba_mod.rb +28 -0
  16. data/features/support/lib_test/fake_gem.rb +47 -0
  17. data/features/xcode/create.feature +42 -0
  18. data/lib/fwtoolkit/cli/bootstrap.rb +75 -0
  19. data/lib/fwtoolkit/cli/ci.rb +52 -0
  20. data/lib/fwtoolkit/cli/cocoapods.rb +73 -0
  21. data/lib/fwtoolkit/cli/ext/thor.rb +35 -0
  22. data/lib/fwtoolkit/cli/frank.rb +113 -0
  23. data/lib/fwtoolkit/cli/fw_actions/template_dir.rb +59 -0
  24. data/lib/fwtoolkit/cli/fw_actions.rb +2 -0
  25. data/lib/fwtoolkit/cli/git.rb +42 -0
  26. data/lib/fwtoolkit/cli/ota.rb +109 -0
  27. data/lib/fwtoolkit/cli/ota_client/hockeyapp_client.rb +70 -0
  28. data/lib/fwtoolkit/cli/project.rb +83 -0
  29. data/lib/fwtoolkit/cli/thorutils.rb +14 -0
  30. data/lib/fwtoolkit/cli/xcode.rb +105 -0
  31. data/lib/fwtoolkit/cli.rb +27 -36
  32. data/lib/fwtoolkit/config/config.sample +8 -0
  33. data/lib/fwtoolkit/config.rb +41 -0
  34. data/lib/fwtoolkit/configfile.rb +36 -0
  35. data/lib/fwtoolkit/executable/executable.rb +45 -0
  36. data/lib/fwtoolkit/executable.rb +1 -0
  37. data/lib/fwtoolkit/ext/gem.rb +9 -0
  38. data/lib/fwtoolkit/ext/hash_yaml.rb +17 -0
  39. data/lib/fwtoolkit/git_client/git_client.rb +225 -0
  40. data/lib/fwtoolkit/git_client.rb +1 -0
  41. data/lib/fwtoolkit/projectfile.rb +50 -0
  42. data/lib/fwtoolkit/rake/ext/rake.rb +7 -0
  43. data/lib/fwtoolkit/rake/tasks/ci.rb +25 -0
  44. data/lib/fwtoolkit/rake/tasks/config.rb +13 -0
  45. data/lib/fwtoolkit/rake/tasks/ota.rb +31 -0
  46. data/lib/fwtoolkit/rake/tasks/project.rb +28 -0
  47. data/lib/fwtoolkit/rake/tasks/test.rb +59 -0
  48. data/lib/fwtoolkit/rake/tasks/xcode.rb +69 -0
  49. data/lib/fwtoolkit/rake/tasks.rb +20 -0
  50. data/lib/fwtoolkit/version.rb +1 -1
  51. data/lib/fwtoolkit.rb +4 -7
  52. data/spec/git_client_spec.rb +316 -0
  53. data/spec/project_config_spec.rb +40 -0
  54. data/spec/rake/project.rb +28 -0
  55. data/spec/rake/test.rb +85 -0
  56. data/spec/rake/xcode.rb +66 -0
  57. data/spec/support/aruba-doubles-rspec.rb +21 -0
  58. data/spec/support/ctx_rake.rb +23 -0
  59. data/spec/support/double_helper.rb +13 -0
  60. data/spec/support/project_generator.rb +45 -0
  61. data/templates/default_project/frank/%project_name%/%target_name%/%class_prefix%AppDelegate+Frank.h.tt +15 -0
  62. data/templates/{cucumber/AppDelegate+Frank.m.erb → default_project/frank/%project_name%/%target_name%/%class_prefix%AppDelegate+Frank.m.tt} +25 -21
  63. data/templates/{cucumber → default_project/frank/%project_name%/Frank}/features/example.feature +0 -0
  64. data/templates/{cucumber → default_project/frank/%project_name%/Frank}/features/step_definitions/launch_steps.rb +0 -0
  65. data/templates/{cucumber → default_project/frank/%project_name%/Frank}/features/support/env.rb +0 -0
  66. data/templates/{cucumber → default_project/frank/%project_name%/Frank}/features/support/mimic.rb +0 -0
  67. data/templates/default_project/frank/~template_config.rb +11 -0
  68. data/templates/default_project/frank_seed_core_data/%project_name%/Frank/features/support/models/%class_name%.rb.tt +30 -0
  69. data/templates/default_project/frank_seed_support/%project_name%/Frank/features/support/models/factories.rb.tt +10 -0
  70. data/templates/{cucumber → default_project/frank_seed_support/%project_name%/Frank}/features/support/views/my_objects_json.erb +0 -0
  71. data/templates/{cucumber → default_project/frank_seed_support/%project_name%/Frank}/features/support/views/my_objects_xml.erb +0 -0
  72. data/templates/{fwt/gitignore.erb → default_project/git/.gitignore} +0 -0
  73. data/templates/{fwt/rvmrc.erb → default_project/rvm/.rvmrc.tt} +2 -2
  74. data/templates/default_project/rvm/Gemfile +4 -0
  75. data/templates/default_project/xcode/%project_name%/%project_name%.xcodeproj/project.pbxproj.tt +583 -0
  76. data/templates/default_project/xcode/%project_name%/%project_name%.xcodeproj/project.xcworkspace/contents.xcworkspacedata.tt +7 -0
  77. data/templates/default_project/xcode/%project_name%/%project_name%.xcodeproj/xcshareddata/xcschemes/%project_name%-Release.xcscheme.tt +105 -0
  78. data/templates/default_project/xcode/%project_name%/%project_name%.xcodeproj/xcshareddata/xcschemes/%project_name%-Testing.xcscheme.tt +105 -0
  79. data/templates/default_project/xcode/%project_name%/%project_name%.xcodeproj/xcshareddata/xcschemes/%project_name%.xcscheme.tt +105 -0
  80. data/templates/default_project/xcode/%project_name%/%target_name%/%class_prefix%AppDelegate.h.tt +15 -0
  81. data/templates/default_project/xcode/%project_name%/%target_name%/%class_prefix%AppDelegate.m.tt +49 -0
  82. data/templates/{fwt/default_project/Info.plist.erb → default_project/xcode/%project_name%/%target_name%/Supporting Files/%project_name%-Info.plist} +0 -0
  83. data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/%project_name%-Prefix.pch.tt +14 -0
  84. data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/Settings.bundle/Root.plist +53 -0
  85. data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/Settings.bundle/en.lproj/Root.strings +0 -0
  86. data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/en.lproj/InfoPlist.strings +2 -0
  87. data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/fw-shared.xcconfig +49 -0
  88. data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/info.plist.h.tt +8 -0
  89. data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/main.m.tt +18 -0
  90. data/templates/default_project/xcode/%project_name%/%target_name%/Supporting Files/version.sh +159 -0
  91. data/templates/default_project/xcode/%project_name%/%tests_target_name%/%project_name%Tests.h.tt +13 -0
  92. data/templates/default_project/xcode/%project_name%/%tests_target_name%/%project_name%Tests.m.tt +32 -0
  93. data/templates/default_project/xcode/%project_name%/%tests_target_name%/Supporting Files/%tests_target_name%-Info.plist +22 -0
  94. data/templates/default_project/xcode/%project_name%/%tests_target_name%/Supporting Files/en.lproj/InfoPlist.strings +2 -0
  95. data/templates/default_project/xcode/%project_name%/Default.png +0 -0
  96. data/templates/default_project/xcode/%project_name%/Resources/Default-568h@2x.png +0 -0
  97. data/templates/default_project/xcode/%project_name%/Resources/Default.png +0 -0
  98. data/templates/default_project/xcode/%project_name%/Resources/Default@2x.png +0 -0
  99. data/templates/default_project/xcode/%project_name%.xcworkspace/contents.xcworkspacedata.tt +7 -0
  100. data/templates/default_project/xcode/FWProjectfile.tt +14 -0
  101. data/templates/default_project/xcode/Podfile.tt +4 -0
  102. data/templates/default_project/xcode/Rakefile.tt +14 -0
  103. data/templates/default_project/xcode/cruise_config.rb +3 -0
  104. data/templates/default_project/xcode/~template_config.rb +5 -0
  105. metadata +187 -141
  106. data/lib/fwtoolkit/tasks/build.rb +0 -121
  107. data/lib/fwtoolkit/tasks/ci.rb +0 -30
  108. data/lib/fwtoolkit/tasks/cocoapods.rb +0 -32
  109. data/lib/fwtoolkit/tasks/frank.rb +0 -119
  110. data/lib/fwtoolkit/tasks/helper.rb +0 -16
  111. data/lib/fwtoolkit/tasks/services.rb +0 -41
  112. data/lib/fwtoolkit/tasks.rb +0 -24
  113. data/lib/fwtoolkit/test/frank_model.rb +0 -120
  114. data/lib/fwtoolkit/test/misc_steps.rb +0 -9
  115. data/lib/fwtoolkit/test/model_helper.rb +0 -94
  116. data/lib/fwtoolkit/test/network_steps.rb +0 -60
  117. data/lib/fwtoolkit/test/pickle_steps.rb +0 -101
  118. data/lib/fwtoolkit/test/timeout_helper.rb +0 -21
  119. data/lib/fwtoolkit/test/ui_helper.rb +0 -19
  120. data/lib/fwtoolkit/test/ui_steps.rb +0 -17
  121. data/templates/cucumber/AppDelegate+Frank.h.erb +0 -15
  122. data/templates/fwt/Gemfile.erb +0 -3
  123. data/templates/fwt/Podfile.erb +0 -4
  124. data/templates/fwt/Rakefile.erb +0 -24
  125. data/templates/fwt/default_project/AppDelegate.h.erb +0 -12
  126. data/templates/fwt/default_project/AppDelegate.m.erb +0 -20
  127. data/templates/fwt/default_project/Prefix.pch.erb +0 -6
  128. data/templates/fwt/default_project/main.m.erb +0 -15
  129. data/templates/models/factories.rb.erb +0 -10
  130. 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,2 @@
1
+ require 'thor'
2
+ require 'fwtoolkit/cli/fw_actions/template_dir'
@@ -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