hawk 0.0.1 → 0.1.0
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.
- data/LICENSE.txt +1 -1
- data/README.md +47 -14
- data/bin/hawk +5 -0
- data/bin/hawkify +5 -0
- data/hawk.gemspec +2 -0
- data/lib/hawk/builder.rb +82 -0
- data/lib/hawk/cli.rb +25 -0
- data/lib/hawk/dsl.rb +26 -0
- data/lib/hawk/hawkifier.rb +33 -0
- data/lib/hawk/notifier.rb +28 -0
- data/lib/hawk/s3_uploader.rb +54 -0
- data/lib/hawk/version.rb +1 -1
- data/lib/hawk.rb +3 -4
- data/templates/Hawkfile.erb +60 -0
- data/templates/manifest.plist.erb +31 -0
- metadata +47 -3
data/LICENSE.txt
CHANGED
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
21
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,29 +1,62 @@
|
|
1
1
|
# Hawk
|
2
2
|
|
3
|
-
|
3
|
+
Hawk is a simple iOS ad-hoc distribution tool. Deployments are lightweight,
|
4
|
+
backed by S3 storage (sourced from your own AWS account), and notifications of
|
5
|
+
new builds happen via email. Beyond a couple of S3 buckets, the only moving
|
6
|
+
parts to hawk are local on your machine. Hawk is a lightweight, low-friction way
|
7
|
+
to make ad-hoc deploys easy.
|
4
8
|
|
5
|
-
##
|
9
|
+
## Status
|
10
|
+
|
11
|
+
Hawk is alpha quality software at this point, although I'm rapidly working
|
12
|
+
towards firming up a 1.0 release in the next few weeks (by mid Feb 2013). Some
|
13
|
+
features that will be coming in the near future include
|
6
14
|
|
7
|
-
|
15
|
+
### Planned for 0.2
|
16
|
+
* Automatic object expiry on S3 for .ipa and plist bundles
|
17
|
+
* Support for Xcode workspace files
|
8
18
|
|
9
|
-
|
19
|
+
### Planned for 0.3
|
20
|
+
* Automatic bucket name selection option
|
21
|
+
* Tighter ACL support on uploaded files
|
10
22
|
|
11
|
-
|
23
|
+
### Planned for 0.4
|
24
|
+
* UDID validation
|
25
|
+
* Internal refactor
|
26
|
+
|
27
|
+
## Installation
|
12
28
|
|
13
|
-
|
29
|
+
Install hawk on your machine by either running `gem install hawk`, or else by
|
30
|
+
including it in your application's `Gemfile`. Once it's installed run
|
14
31
|
|
15
|
-
|
32
|
+
$ hawkify
|
16
33
|
|
17
|
-
|
34
|
+
in your application's top-level directory to create a `Hawkfile` (if one doesn't
|
35
|
+
already exist). Open up `Hawkfile` in your editor of choice and follow the
|
36
|
+
comments therein to set it up to your liking.
|
18
37
|
|
19
38
|
## Usage
|
20
39
|
|
21
|
-
|
40
|
+
Once your `Hawkfile` is customized, you're ready to go. You can create and
|
41
|
+
deploy an ad-hoc build of your app by running
|
42
|
+
|
43
|
+
$ hawk
|
44
|
+
|
45
|
+
from any folder inside your application. This will do the following:
|
46
|
+
|
47
|
+
1. Build your app for distribution (using the `Release` build configuration)
|
48
|
+
2. Sign your app for ad-hoc distribution (according to the `Release` code signing identity in your project)
|
49
|
+
3. Upload your app and some associated metadata to S3, using the credentials you
|
50
|
+
specify in your `Hawkfile`
|
51
|
+
4. Draft an email with a pre-populated body (once again taken from your `Hawkfile`)
|
52
|
+
and open it using your local mail application, ready for you to edit and send at
|
53
|
+
your leisure
|
54
|
+
|
55
|
+
Hawk automatically includes any required provisioning profiles inside the
|
56
|
+
deployed app, so users don't need to do anything beyond clicking on the link
|
57
|
+
that hawk sends out. There's no client-side app to install, no servers to run,
|
58
|
+
no accounts to set up. It just works.
|
22
59
|
|
23
60
|
## Contributing
|
24
61
|
|
25
|
-
|
26
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
-
5. Create new Pull Request
|
62
|
+
Contributions welcome! Fork this repo and submit a pull request (or just open up a ticket and I'll see what I can do).
|
data/bin/hawk
ADDED
data/bin/hawkify
ADDED
data/hawk.gemspec
CHANGED
data/lib/hawk/builder.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'osx/plist'
|
3
|
+
|
4
|
+
module Hawk
|
5
|
+
module Builder
|
6
|
+
module DSL
|
7
|
+
def signing_identity(identity)
|
8
|
+
@signing_identity = identity
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def app_name
|
13
|
+
info_plist_property('CFBundleDisplayName')
|
14
|
+
end
|
15
|
+
|
16
|
+
def app_version
|
17
|
+
info_plist_property('CFBundleVersion')
|
18
|
+
end
|
19
|
+
|
20
|
+
def bundle_identifier
|
21
|
+
info_plist_property('CFBundleIdentifier')
|
22
|
+
end
|
23
|
+
|
24
|
+
def repo_version
|
25
|
+
`git rev-parse --short HEAD`.chomp
|
26
|
+
end
|
27
|
+
|
28
|
+
def ipa_file
|
29
|
+
if (!@ipa_file)
|
30
|
+
output_dir = Dir.tmpdir
|
31
|
+
product_name = project_property('PRODUCT_NAME')
|
32
|
+
@ipa_file = File.join(output_dir, "#{product_name}.ipa")
|
33
|
+
|
34
|
+
the_app_file = app_file # Do this before below so output happens in the right order
|
35
|
+
|
36
|
+
print "Signing app..."
|
37
|
+
output = `/usr/bin/xcrun -sdk iphoneos PackageApplication -v -s "#{@signing_identity || "iPhone Distribution"}" -o #{@ipa_file} #{the_app_file} 2>&1`
|
38
|
+
if $?.to_i != 0
|
39
|
+
puts "error (text follows)"
|
40
|
+
abort output
|
41
|
+
end
|
42
|
+
puts "done"
|
43
|
+
end
|
44
|
+
@ipa_file
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_plist
|
48
|
+
ERB.new(File.read(File.join(File.dirname(__FILE__), '..', '..', 'templates', 'manifest.plist.erb'))).result(binding)
|
49
|
+
end
|
50
|
+
|
51
|
+
def itms_url
|
52
|
+
"itms-services://?action=download-manifest&url=#{plist_url}"
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def app_file
|
58
|
+
if (!@app_file)
|
59
|
+
output_dir = Dir.tmpdir
|
60
|
+
|
61
|
+
print "Building Xcode project..."
|
62
|
+
output = `/usr/bin/xcodebuild CONFIGURATION_BUILD_DIR=#{output_dir} 2>&1`
|
63
|
+
if $?.to_i != 0
|
64
|
+
puts "error (text follows)"
|
65
|
+
abort output
|
66
|
+
end
|
67
|
+
puts "done"
|
68
|
+
@app_file = File.join(output_dir, project_property('FULL_PRODUCT_NAME'))
|
69
|
+
end
|
70
|
+
@app_file
|
71
|
+
end
|
72
|
+
|
73
|
+
def info_plist_property(prop)
|
74
|
+
plist_file = File.join(File.dirname(app_file), project_property('INFOPLIST_PATH'))
|
75
|
+
result = OSX::PropertyList.load_file(plist_file)[prop]
|
76
|
+
end
|
77
|
+
|
78
|
+
def project_property(prop)
|
79
|
+
`/usr/bin/xcodebuild -showBuildSettings | grep "\s#{prop} = "`.gsub!(/.* = /, '').chomp
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/hawk/cli.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Hawk
|
2
|
+
module CLI
|
3
|
+
def self.run(args)
|
4
|
+
hawkfile = closest_hawkfile(Dir.pwd)
|
5
|
+
if (hawkfile)
|
6
|
+
Dir.chdir(File.dirname(hawkfile))
|
7
|
+
Hawk::DSL.load(hawkfile)
|
8
|
+
else
|
9
|
+
puts "Cannot find hawkfile"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.closest_hawkfile(dir)
|
14
|
+
file_name = File.join(dir, 'Hawkfile')
|
15
|
+
if File.exists?(file_name)
|
16
|
+
file_name
|
17
|
+
elsif dir == '/'
|
18
|
+
nil
|
19
|
+
else
|
20
|
+
closest_hawkfile(File.expand_path(File.join(dir, '..')))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
data/lib/hawk/dsl.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'hawk/builder'
|
2
|
+
require 'hawk/s3_uploader'
|
3
|
+
require 'hawk/notifier'
|
4
|
+
|
5
|
+
module Hawk
|
6
|
+
class DSL
|
7
|
+
include Hawk::Builder::DSL
|
8
|
+
include Hawk::S3Uploader::DSL
|
9
|
+
include Hawk::Notifier::DSL
|
10
|
+
|
11
|
+
def self.load(file)
|
12
|
+
instance = self.new
|
13
|
+
instance.instance_eval(File.read(file), file)
|
14
|
+
|
15
|
+
instance.execute
|
16
|
+
end
|
17
|
+
|
18
|
+
def execute
|
19
|
+
extend Hawk::Builder
|
20
|
+
extend Hawk::S3Uploader
|
21
|
+
extend Hawk::Notifier
|
22
|
+
|
23
|
+
notify_users # notify_users will trigger all dependent actions
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Hawk
|
4
|
+
class Hawkifier
|
5
|
+
def initialize(dir)
|
6
|
+
@dir = dir
|
7
|
+
end
|
8
|
+
|
9
|
+
def hawkify
|
10
|
+
files.each do |name, contents|
|
11
|
+
write_file_if_not_exist(name, contents)
|
12
|
+
end
|
13
|
+
puts "hawkify done"
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def files
|
18
|
+
{ 'Hawkfile' => ERB.new(File.read(File.join(File.dirname(__FILE__), '..', '..', 'templates', 'Hawkfile.erb'))).result }
|
19
|
+
end
|
20
|
+
|
21
|
+
def write_file_if_not_exist(name, contents)
|
22
|
+
file = File.join(@dir, name)
|
23
|
+
if (File.exists?(file))
|
24
|
+
puts "hawkify skipping #{name}; file exists"
|
25
|
+
else
|
26
|
+
File.open(file, 'w') do |f|
|
27
|
+
puts "hawkify writing #{name}"
|
28
|
+
f.write(contents)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Hawk
|
4
|
+
module Notifier
|
5
|
+
module DSL
|
6
|
+
def user(user)
|
7
|
+
puts "Warning: UDID check not implemented yet" if user.is_a? Hash
|
8
|
+
user = { user => nil } unless (user.is_a? Hash)
|
9
|
+
@users ||= {}
|
10
|
+
@users.merge!(user)
|
11
|
+
end
|
12
|
+
|
13
|
+
def email_subject(subject)
|
14
|
+
@email_subject = subject
|
15
|
+
end
|
16
|
+
|
17
|
+
def email_body(body)
|
18
|
+
@email_body = body
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def notify_users
|
23
|
+
subject = URI.encode(ERB.new(@email_subject).result(binding)).gsub('?','%3F').gsub('&','%26')
|
24
|
+
body = URI.encode(ERB.new(@email_body).result(binding)).gsub('?','%3F').gsub('&','%26')
|
25
|
+
`open "mailto:?bcc=#{@users.keys.join(',')}&subject=#{subject}&body=#{body}"`
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'aws/s3'
|
2
|
+
|
3
|
+
module Hawk
|
4
|
+
module S3Uploader
|
5
|
+
module DSL
|
6
|
+
def access_key_id(key)
|
7
|
+
@access_key_id = key
|
8
|
+
end
|
9
|
+
|
10
|
+
def secret_access_key(key)
|
11
|
+
@secret_access_key = key
|
12
|
+
end
|
13
|
+
|
14
|
+
def bucket_name(name)
|
15
|
+
@bucket_name = name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def ipa_url
|
20
|
+
if (!@ipa_url)
|
21
|
+
file = ipa_file
|
22
|
+
@ipa_url = object(File.basename(file)) do |obj|
|
23
|
+
print "Uploading #{File.basename(file)} to S3..."
|
24
|
+
obj.write(Pathname.new(file))
|
25
|
+
puts 'done'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@ipa_url
|
29
|
+
end
|
30
|
+
|
31
|
+
def plist_url
|
32
|
+
if (!@plist_url)
|
33
|
+
@plist_url = object('manifest.plist') do |obj|
|
34
|
+
plist_data = build_plist
|
35
|
+
print 'Uploading plist to S3...'
|
36
|
+
obj.write(plist_data, :content_type => 'application/xml')
|
37
|
+
puts 'done'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
@plist_url
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def object(name, &block)
|
46
|
+
s3 = AWS::S3.new(:access_key_id => @access_key_id, :secret_access_key => @secret_access_key)
|
47
|
+
bucket = s3.buckets.create @bucket_name
|
48
|
+
obj = bucket.objects[name]
|
49
|
+
yield obj
|
50
|
+
obj.acl = :public_read
|
51
|
+
obj.public_url
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/hawk/version.rb
CHANGED
data/lib/hawk.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
#
|
2
|
+
# Hawkfile
|
3
|
+
#
|
4
|
+
|
5
|
+
#
|
6
|
+
# XCode project information - used to configure how your app is built and signed
|
7
|
+
#
|
8
|
+
# The signing identity to sign your app with. Defaults to 'iPhone Distribution'
|
9
|
+
#signing_identity 'iPhone Distribution'
|
10
|
+
|
11
|
+
#
|
12
|
+
# AWS Credentials - used to upload your app and associated metadata into S3 so
|
13
|
+
# that users can download and install your app
|
14
|
+
#
|
15
|
+
#access_key_id 'AAAAAAAAAAAAAAAAAAAA'
|
16
|
+
#secret_access_key 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
17
|
+
#bucket_name 'mah-bukkit'
|
18
|
+
|
19
|
+
#
|
20
|
+
# User list - a list of users to send emails to upon successsful upload of a new
|
21
|
+
# version of your app. Can be specified as simple email addresses, or optionally
|
22
|
+
# as a hash with the value being the UDID of their device. Any specified UDIDs
|
23
|
+
# will be checked against the built app, to verify that they are entitled to run
|
24
|
+
# the app. Any failures will result in a warning being displayed
|
25
|
+
#
|
26
|
+
#user 'alice@example.com'
|
27
|
+
#user 'bob@example.com'
|
28
|
+
#user 'claire@example.com' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
29
|
+
|
30
|
+
#
|
31
|
+
# Notification settings - parameters describing the email to be sent out. This
|
32
|
+
# email will be opened locally on this machine by invoking a 'mailto:' url. Your
|
33
|
+
# default mail application will open, allowing you to review and edit the email
|
34
|
+
# before sending it. This is usually a good time to describe changes to the app,
|
35
|
+
# or to suggest areas for testing and review.
|
36
|
+
#
|
37
|
+
# The following tags are allowed for replacement. In addition to these tags, any
|
38
|
+
# ERB compliant tags will be replaced as well.
|
39
|
+
#
|
40
|
+
# <%= app_name %> : The name of the application, taken from the project
|
41
|
+
# <%= app_version %> : The current version of the project, taken from the project's
|
42
|
+
# Info.plist file
|
43
|
+
# <%= repo_version %> : An SCM specific version number taken from the current state
|
44
|
+
# of the project
|
45
|
+
# <%= itms_url %> : The itms-services: url that users will click on to install
|
46
|
+
# the application
|
47
|
+
#
|
48
|
+
|
49
|
+
email_subject "A new update of <%= app_name %> is available for download"
|
50
|
+
|
51
|
+
email_body <<EOF
|
52
|
+
A new build of <%= app_name %> is ready to install. Click the followling link
|
53
|
+
on your iOS device to install it:
|
54
|
+
|
55
|
+
<%= itms_url %>
|
56
|
+
|
57
|
+
Please reference '<%= repo_version %>' in any bug reports to help us out.
|
58
|
+
|
59
|
+
Thanks!
|
60
|
+
EOF
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<dict>
|
5
|
+
<key>items</key>
|
6
|
+
<array>
|
7
|
+
<dict>
|
8
|
+
<key>assets</key>
|
9
|
+
<array>
|
10
|
+
<dict>
|
11
|
+
<key>kind</key>
|
12
|
+
<string>software-package</string>
|
13
|
+
<key>url</key>
|
14
|
+
<string><%= ipa_url %></string>
|
15
|
+
</dict>
|
16
|
+
</array>
|
17
|
+
<key>metadata</key>
|
18
|
+
<dict>
|
19
|
+
<key>bundle-identifier</key>
|
20
|
+
<string><%= bundle_identifier %></string>
|
21
|
+
<key>bundle-version</key>
|
22
|
+
<string><%= app_version %></string>
|
23
|
+
<key>kind</key>
|
24
|
+
<string>software</string>
|
25
|
+
<key>title</key>
|
26
|
+
<string><%= app_name %></string>
|
27
|
+
</dict>
|
28
|
+
</dict>
|
29
|
+
</array>
|
30
|
+
</dict>
|
31
|
+
</plist>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hawk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: aws-sdk
|
@@ -27,10 +27,44 @@ dependencies:
|
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '1.8'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: xcodeproj
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0.4'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0.4'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: osx-plist
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
30
62
|
description: A simple iOS ad-hoc distribution tool
|
31
63
|
email:
|
32
64
|
- mat@geeky.net
|
33
|
-
executables:
|
65
|
+
executables:
|
66
|
+
- hawk
|
67
|
+
- hawkify
|
34
68
|
extensions: []
|
35
69
|
extra_rdoc_files: []
|
36
70
|
files:
|
@@ -39,9 +73,19 @@ files:
|
|
39
73
|
- LICENSE.txt
|
40
74
|
- README.md
|
41
75
|
- Rakefile
|
76
|
+
- bin/hawk
|
77
|
+
- bin/hawkify
|
42
78
|
- hawk.gemspec
|
43
79
|
- lib/hawk.rb
|
80
|
+
- lib/hawk/builder.rb
|
81
|
+
- lib/hawk/cli.rb
|
82
|
+
- lib/hawk/dsl.rb
|
83
|
+
- lib/hawk/hawkifier.rb
|
84
|
+
- lib/hawk/notifier.rb
|
85
|
+
- lib/hawk/s3_uploader.rb
|
44
86
|
- lib/hawk/version.rb
|
87
|
+
- templates/Hawkfile.erb
|
88
|
+
- templates/manifest.plist.erb
|
45
89
|
homepage: http://github.com/mtrudel/hawk
|
46
90
|
licenses: []
|
47
91
|
post_install_message:
|