ios-box 0.2.1 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +33 -27
- data/bin/ios-box +1 -1
- data/ios-box.gemspec +5 -2
- data/lib/ios-box.rb +2 -6
- data/lib/ios-box/cli.rb +89 -86
- data/lib/ios-box/config.rb +30 -16
- data/lib/ios-box/deploy.rb +96 -0
- data/lib/ios-box/deploy/testflight.rb +35 -0
- data/lib/ios-box/iosbox.rb +122 -122
- data/lib/ios-box/tools.rb +6 -6
- data/lib/ios-box/tools/build.rb +41 -42
- data/lib/ios-box/tools/config.rb +36 -0
- data/lib/ios-box/tools/deploy.rb +179 -0
- data/lib/ios-box/tools/version.rb +28 -29
- data/lib/ios_box.rb +1 -1
- metadata +45 -9
- data/lib/ios-box/version.rb +0 -5
data/README.md
CHANGED
@@ -1,23 +1,20 @@
|
|
1
1
|
IosBox
|
2
2
|
======
|
3
3
|
|
4
|
-
IosBox is
|
5
|
-
It includes rake tasks that take care of updating app Info.plist with proper
|
6
|
-
version information (e.g. build date, GIT hash, etc).
|
7
|
-
Further version will integrate deployment options, such as deploy beta versions
|
8
|
-
to TestFlight.
|
4
|
+
IosBox is tool that integrates with XCode to automate some of the tasks.
|
9
5
|
|
10
6
|
Current Features
|
11
7
|
----------------
|
12
8
|
|
13
9
|
Currently IosBox supports following features:
|
14
10
|
|
15
|
-
* <b>Build Prepare</b> (`
|
16
|
-
Build prepare task generates new build
|
11
|
+
* <b>Build Prepare</b> (`build prepare`)
|
12
|
+
Build prepare task generates new build and bundle version and stores
|
17
13
|
it to application Info.plist.
|
18
|
-
It also
|
14
|
+
It also prepares buildcache for further tasks.
|
19
15
|
|
20
|
-
* <b>Version
|
16
|
+
* <b>Version Management</b> (`version`)
|
17
|
+
IosBox offers simple
|
21
18
|
IosBox offers simple tasks to bump version numbers. Either it is patch, minor or
|
22
19
|
major version bump, IosBox automatically handles increasing current version number.
|
23
20
|
|
@@ -29,39 +26,48 @@ In the roadmap are following features (but not yet planned)
|
|
29
26
|
* Asset management, slicing assets according to receipt etc.
|
30
27
|
* More to come, open for suggestions...
|
31
28
|
|
32
|
-
Installation
|
33
|
-
------------
|
29
|
+
# Installation
|
34
30
|
|
35
31
|
Install `IosBox` gem if you haven't done yet so
|
36
32
|
|
37
33
|
$ gem install ios-box
|
38
34
|
|
39
|
-
Create in the root of your project folder `Rakefile` -file with following contents:
|
40
|
-
|
41
|
-
require 'ios-box'
|
42
|
-
|
43
|
-
IosBox::Tasks.new do |config|
|
44
|
-
config.target = "iosboxdev"
|
45
|
-
end
|
46
|
-
|
47
35
|
Integrate toolbox with your XCode project by executing following command:
|
48
36
|
|
49
|
-
$
|
37
|
+
$ ios-box integrate
|
50
38
|
|
51
39
|
Notice! This command will modify your XCode project file and therefore can make your project to stop working.
|
52
40
|
Make sure you have proper backups done.
|
53
41
|
|
54
|
-
|
42
|
+
# Usage
|
43
|
+
|
44
|
+
Run `ios-box help` in project folder to see available commands.
|
45
|
+
|
46
|
+
# Commands
|
47
|
+
|
48
|
+
## ios-box integrate
|
49
|
+
|
50
|
+
Integrates ios-box to current project. During integration process you can
|
51
|
+
choose which targets build preparation task is ran.
|
52
|
+
|
53
|
+
## ios-box build prepare
|
54
|
+
|
55
|
+
This task prepares build process and can be only ran during XCode build phase.
|
56
|
+
|
57
|
+
## ios-box version show
|
58
|
+
|
59
|
+
Displays current version information of the project.
|
60
|
+
|
61
|
+
## ios-box version build
|
55
62
|
|
56
|
-
|
63
|
+
Increments build number.
|
57
64
|
|
58
|
-
|
59
|
-
-----
|
65
|
+
## ios-box version bump [major|minor]
|
60
66
|
|
61
|
-
|
67
|
+
Bumps marketing version by one step. By default it increases patch level
|
68
|
+
but if ptional argument is given, either major or minor version is increased.
|
62
69
|
|
63
|
-
Copyright
|
64
|
-
---------
|
70
|
+
# Copyright
|
65
71
|
|
66
72
|
Copyright © 2011 Mikko Kokkonen. See LICENSE.txt for
|
67
73
|
further details.
|
data/bin/ios-box
CHANGED
data/ios-box.gemspec
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
require File.expand_path('../lib/ios-box
|
2
|
+
require File.expand_path('../lib/ios-box', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["Mikko Kokkonen"]
|
@@ -13,9 +13,12 @@ Gem::Specification.new do |gem|
|
|
13
13
|
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
14
|
gem.name = "ios-box"
|
15
15
|
gem.require_paths = ["lib"]
|
16
|
-
gem.version =
|
16
|
+
gem.version = IOSBox::VERSION
|
17
17
|
|
18
18
|
gem.add_runtime_dependency "thor"
|
19
19
|
gem.add_runtime_dependency "grit"
|
20
20
|
gem.add_runtime_dependency "plist"
|
21
|
+
gem.add_runtime_dependency "rubyzip"
|
22
|
+
gem.add_runtime_dependency "rest-client"
|
23
|
+
gem.add_runtime_dependency "nokogiri"
|
21
24
|
end
|
data/lib/ios-box.rb
CHANGED
data/lib/ios-box/cli.rb
CHANGED
@@ -1,96 +1,99 @@
|
|
1
|
+
require 'ios-box/config'
|
1
2
|
require 'ios-box/tools'
|
2
3
|
require 'ios-box/iosbox'
|
3
4
|
require 'thor'
|
4
5
|
require 'pbxproject'
|
5
6
|
|
6
|
-
module
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
# Create iosbox configuration file
|
81
|
-
create_file ".iosbox" do
|
82
|
-
"# ios-box configuration file\n" +
|
83
|
-
"# https://github.com/owl-forestry/ios-box\n" +
|
84
|
-
"#\n" +
|
85
|
-
"config.project = \"#{project}\"\n" +
|
86
|
-
"#{_targets}\n"
|
7
|
+
module IOSBox
|
8
|
+
class CLI < Thor
|
9
|
+
include Thor::Actions
|
10
|
+
|
11
|
+
desc "version COMMAND", "Version related subcommands"
|
12
|
+
subcommand "version", Tools::Version
|
13
|
+
|
14
|
+
desc "build COMMAND", "Build related subcommands"
|
15
|
+
subcommand "build", Tools::Build
|
16
|
+
|
17
|
+
desc "config COMMAND", "Configuration related subcommands"
|
18
|
+
subcommand "config", Tools::Config
|
19
|
+
|
20
|
+
desc "deploy COMMAND", "Deploymend subcommands"
|
21
|
+
subcommand "deploy", Tools::Deploy
|
22
|
+
|
23
|
+
desc "integrate [PROJECT]", "Integrates ios-box with project, by default first founded"
|
24
|
+
def integrate(project = nil)
|
25
|
+
shell = Thor::Shell::Basic.new
|
26
|
+
|
27
|
+
# Find our project
|
28
|
+
if project.nil?
|
29
|
+
project = Dir["*.xcodeproj"].first
|
30
|
+
end
|
31
|
+
|
32
|
+
unless File.exists?(project) and File.directory?(project)
|
33
|
+
shell.error "Project #{project} is not valid."
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
|
37
|
+
shell.say "Integrating to project #{project}"
|
38
|
+
|
39
|
+
# Find our project file, either from command-line or just using first one
|
40
|
+
xcode = project || Dir["*.xcodeproj"].first
|
41
|
+
raise "Cannot find project.pbxproj in #{xcode}" unless File.file? "#{xcode}/project.pbxproj"
|
42
|
+
|
43
|
+
# Load our project file
|
44
|
+
pbx = PBXProject::PBXProject.new :file => "#{xcode}/project.pbxproj"
|
45
|
+
pbx.parse
|
46
|
+
|
47
|
+
# If target missing, fetch first target
|
48
|
+
# Find all targets
|
49
|
+
targets = pbx.find_item :type => PBXProject::PBXTypes::PBXNativeTarget
|
50
|
+
if targets.nil? or targets.empty?
|
51
|
+
raise "XCode project does not have any targets!"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Generate build phase
|
55
|
+
# Try to find build phases
|
56
|
+
# prebuilds = pbx.find_item(:type => PBXProject::PBXTypes::PBXShellScriptBuildPhase) || []
|
57
|
+
# prebuilds.select! do |c|
|
58
|
+
# !c.comment.match(/ios-box prepare/).nil?
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
prebuilds = [] # Always add new build phase
|
62
|
+
if prebuilds.empty?
|
63
|
+
initPhase = PBXProject::PBXTypes::PBXShellScriptBuildPhase.new(
|
64
|
+
:shellPath => '/bin/sh',
|
65
|
+
:shellScript => "\"(cd $PROJECT_DIR; ios-box build prepare)\"",
|
66
|
+
:showEnvVarsInLog => 0,
|
67
|
+
:name => '"ios-box prepare"'
|
68
|
+
)
|
69
|
+
initPhase.comment = "ios-box prepare"
|
70
|
+
|
71
|
+
pbx.add_item initPhase
|
72
|
+
else
|
73
|
+
initPhase = prebuilds.first
|
74
|
+
end
|
75
|
+
|
76
|
+
targets.each do |target|
|
77
|
+
if shell.yes?("Integrate with target #{target.name.value}? [yn]")
|
78
|
+
# Inject buildphase to target
|
79
|
+
# Add to target
|
80
|
+
target.add_build_phase initPhase, 0
|
87
81
|
end
|
88
|
-
# Append buildcache to gitignore
|
89
|
-
send((File.exists?(".gitignore") ? :append_to_file : :create_file), ".gitignore", ".buildcache\n")
|
90
|
-
|
91
|
-
# Write project file
|
92
|
-
pbx.write_to :file => "#{xcode}/project.pbxproj"
|
93
82
|
end
|
83
|
+
|
84
|
+
# Create iosbox configuration file
|
85
|
+
config = Config.new
|
86
|
+
config.project = project
|
87
|
+
config.targets = targets.collect{|c| c.name.value}
|
88
|
+
config.growl = true # Enable Growl support
|
89
|
+
config.deploy = {'autonotes' => true} # Generate deployment changelogs from GIT logs
|
90
|
+
config.save(".iosbox")
|
91
|
+
|
92
|
+
# Append buildcache to gitignore
|
93
|
+
send((File.exists?(".gitignore") ? :append_to_file : :create_file), ".gitignore", ".buildcache\n")
|
94
|
+
|
95
|
+
# Write project file
|
96
|
+
pbx.write_to :file => "#{xcode}/project.pbxproj"
|
94
97
|
end
|
95
98
|
end
|
96
99
|
end
|
data/lib/ios-box/config.rb
CHANGED
@@ -1,23 +1,37 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
class Config
|
4
|
-
attr_accessor :project
|
5
|
-
attr_reader :targets
|
1
|
+
require 'yaml'
|
2
|
+
require 'ostruct'
|
6
3
|
|
7
|
-
|
4
|
+
module IOSBox
|
5
|
+
class Config < OpenStruct
|
6
|
+
attr_accessor :file
|
7
|
+
|
8
|
+
def self.load(file)
|
9
|
+
if File.exists?(file)
|
10
|
+
config = self.new(YAML.load(File.read(file)))
|
11
|
+
else
|
8
12
|
config = self.new
|
9
|
-
config.instance_eval(File.read(file), file)
|
10
|
-
config
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize
|
14
|
-
@targets = []
|
15
|
-
@project = nil
|
16
13
|
end
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
|
15
|
+
config.file = file
|
16
|
+
config
|
17
|
+
end
|
18
|
+
|
19
|
+
def save(file = nil)
|
20
|
+
puts "Saving config to #{file || @file}"
|
21
|
+
File.open(file || @file, 'w') {|io| io.puts @table.to_yaml }
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_a
|
25
|
+
res = []
|
26
|
+
@table.collect do |k,v|
|
27
|
+
if v.kind_of?(Hash)
|
28
|
+
res << ["#{k.to_s}:", ""]
|
29
|
+
v.each {|k,v| res << [" #{k.to_s}", v]}
|
30
|
+
else
|
31
|
+
res << [k.to_s, v]
|
32
|
+
end
|
20
33
|
end
|
34
|
+
res
|
21
35
|
end
|
22
36
|
end
|
23
37
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'plist'
|
2
|
+
require 'zip/zip'
|
3
|
+
|
4
|
+
module IOSBox
|
5
|
+
module Deploy
|
6
|
+
autoload :Testflight, 'ios-box/deploy/testflight'
|
7
|
+
|
8
|
+
class Deployer
|
9
|
+
attr_reader :iosbox
|
10
|
+
|
11
|
+
def initialize(iosbox)
|
12
|
+
@iosbox = iosbox
|
13
|
+
|
14
|
+
if iosbox.config.growl
|
15
|
+
begin
|
16
|
+
require 'ruby_gntp'
|
17
|
+
|
18
|
+
@growl = GNTP.new("ios-box")
|
19
|
+
@growl.register({:notifications => [
|
20
|
+
{:name => "Deployment Succeeded", :enabled => true},
|
21
|
+
{:name => "Deployment Failed", :enabled => true},
|
22
|
+
]})
|
23
|
+
rescue LoadError
|
24
|
+
iosbox.config.growl = false
|
25
|
+
puts "Please install ruby_gntp gem if you want to enable Growl notifications."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def notify(opts)
|
31
|
+
return unless iosbox.config.growl
|
32
|
+
|
33
|
+
@growl.notify(
|
34
|
+
:name => opts[:name],
|
35
|
+
:title => opts[:title],
|
36
|
+
:text => opts[:text] || opts[:error]
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_ipa(path)
|
41
|
+
puts "Creating IPA from path #{path}..."
|
42
|
+
|
43
|
+
# Check for missing plist
|
44
|
+
raise "Archive info.plist is missing" unless File.exists?(File.join(path, "Info.plist"))
|
45
|
+
pl = Plist::parse_xml(File.join(path, "Info.plist"))
|
46
|
+
raise "Invalid XCArchive version" unless pl['ArchiveVersion'] == 1
|
47
|
+
|
48
|
+
app_path = pl['ApplicationProperties']['ApplicationPath']
|
49
|
+
app_name = File.basename(app_path, File.extname(app_path))
|
50
|
+
ipa_name = File.join(path, "Products", "#{app_name}.ipa")
|
51
|
+
puts "Compressing #{app_path}"
|
52
|
+
if File.exists?(ipa_name)
|
53
|
+
File.unlink(ipa_name)
|
54
|
+
end
|
55
|
+
|
56
|
+
Zip::ZipFile.open(ipa_name, Zip::ZipFile::CREATE) do |zip|
|
57
|
+
Dir["#{File.join(path, "Products", app_path)}/**/*"].each do |entry|
|
58
|
+
e_name = entry.gsub(/#{File.join(path, "Products", app_path)}\//, '')
|
59
|
+
e_name = "Payload/#{app_name}.app/#{e_name}"
|
60
|
+
zip.add e_name, entry
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
ipa_name
|
65
|
+
end
|
66
|
+
|
67
|
+
def create_dsym(path)
|
68
|
+
puts "Creating Zipped dSYM from path #{path}..."
|
69
|
+
|
70
|
+
# Check for missing plist
|
71
|
+
raise "Archive info.plist is missing" unless File.exists?(File.join(path, "Info.plist"))
|
72
|
+
pl = Plist::parse_xml(File.join(path, "Info.plist"))
|
73
|
+
raise "Invalid XCArchive version" unless pl['ArchiveVersion'] == 1
|
74
|
+
|
75
|
+
app_path = pl['ApplicationProperties']['ApplicationPath']
|
76
|
+
app_name = File.basename(app_path)
|
77
|
+
dsym_name = File.join(path, "dSYMs", "#{app_name}.dSYM.zip")
|
78
|
+
|
79
|
+
if File.exists?(dsym_name)
|
80
|
+
File.unlink(dsym_name)
|
81
|
+
end
|
82
|
+
|
83
|
+
Zip::ZipFile.open(dsym_name, Zip::ZipFile::CREATE) do |zip|
|
84
|
+
dsym_path = File.join(path, "dSYMs")
|
85
|
+
puts "Path #{dsym_path}"
|
86
|
+
Dir["#{File.join(dsym_path, "#{app_name}.dSYM")}/**/*"].each do |entry|
|
87
|
+
e_name = entry.gsub(/#{dsym_path}\//, '')
|
88
|
+
zip.add e_name, entry
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
dsym_name
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|