antenna-ota 0.1.3 → 0.1.4
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.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/.travis.yml +9 -0
- data/README.md +20 -3
- data/Rakefile +5 -0
- data/antenna.gemspec +3 -2
- data/lib/antenna.rb +1 -1
- data/lib/antenna/command/s3.rb +10 -2
- data/lib/antenna/distributor.rb +1 -1
- data/lib/antenna/distributor/s3.rb +7 -1
- data/lib/antenna/infoplist.rb +32 -10
- data/lib/antenna/ipa.rb +22 -26
- data/lib/antenna/manifest.rb +1 -1
- data/lib/antenna/version.rb +1 -1
- metadata +22 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5268c6276e2dd2cdebbd16c1a1df41dc49bab722
|
4
|
+
data.tar.gz: 953bc51904fcb09accd8aa90df9691fb7ba09113
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b26482d957a336ef3c6ecc5b659d0d6f07947ceccd1738687608aa94027f6aee35791f4b4c4821fc06aafe92bbbbbcc906f0fed8581d2e620583e51be42569ed
|
7
|
+
data.tar.gz: 322384d8829dcd4dd5dc5743be1a2d2b52b030250bdda63b59d48eb1ca42fa0bbe4a00a5e6beadb41a12328dc4e4f16d7acff59f1c2da813f1f95340c0c8605c
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[](https://travis-ci.org/soulchild/antenna)
|
2
|
+
|
1
3
|
# Antenna
|
2
4
|
|
3
5
|
Antenna aims to take the pain out of creating and distributing all the necessary files for Enterprise iOS over-the-air distribution. It generates the mandatory XML manifest, app icons and an HTML file, automatically extracting all the needed information from the specified `.ipa` file, and uploads everything via a distribution method of your choice (currently only S3 is supported, but you're encouraged to create other storage backends). The result is a (signed S3) URL, which you may then send to your clients, so they can easily install your app from Mobile Safari with just one tap.
|
@@ -30,9 +32,11 @@ $ antenna
|
|
30
32
|
-t, --trace Display backtrace when an error occurs
|
31
33
|
```
|
32
34
|
|
33
|
-
##
|
35
|
+
## Examples
|
36
|
+
|
37
|
+
### 1. Private, signed URL
|
34
38
|
|
35
|
-
Create a new S3 bucket called `antenna-ota` on Amazon's `eu-central-1` S3 cluster and upload OverTheAir.ipa:
|
39
|
+
Create a new S3 bucket called `antenna-ota` on Amazon's `eu-central-1` S3 cluster and upload OverTheAir.ipa, resulting in a signed URL for distribution:
|
36
40
|
|
37
41
|
```bash
|
38
42
|
$ antenna s3 -a <YOUR-S3-ACCESS-KEY> -s <YOUR-S3-SECRET-KEY> --file OverTheAir.ipa --region eu-central-1 --create --bucket antenna-ota
|
@@ -43,7 +47,20 @@ Distributing OverTheAir.html ...
|
|
43
47
|
https://antenna-ota.s3.eu-central-1.amazonaws.com/OverTheAir.html?<...signing-parameters...>
|
44
48
|
```
|
45
49
|
|
46
|
-
|
50
|
+
### 2. Public, unsigned URL
|
51
|
+
|
52
|
+
Upload OverTheAir.ipa to Amazon's `eu-central-1` S3 cluster, resulting in a publically available, unsigned URL for distribution:
|
53
|
+
|
54
|
+
```bash
|
55
|
+
$ antenna s3 -a <YOUR-S3-ACCESS-KEY> -s <YOUR-S3-SECRET-KEY> --file OverTheAir.ipa --public --acl public-read --region eu-central-1 --bucket antenna-ota
|
56
|
+
Distributing OverTheAir.ipa ...
|
57
|
+
Distributing OverTheAir.png ...
|
58
|
+
Distributing OverTheAir.plist ...
|
59
|
+
Distributing OverTheAir.html ...
|
60
|
+
https://antenna-ota.s3.eu-central-1.amazonaws.com/OverTheAir.html
|
61
|
+
```
|
62
|
+
|
63
|
+
The resulting URLs show an installation page like the following and can be distributed to your users for installation. The meta-data and app-icon is automatically extracted from the given .ipa file:
|
47
64
|
|
48
65
|

|
49
66
|
|
data/Rakefile
CHANGED
data/antenna.gemspec
CHANGED
@@ -18,12 +18,13 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_development_dependency "bundler", "~> 1.
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
22
|
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec"
|
23
24
|
|
24
25
|
spec.add_dependency "aws-sdk", '~> 2.0', '>= 2.0.0'
|
25
26
|
spec.add_dependency "commander", "~> 4.3"
|
26
|
-
spec.add_dependency "highline", '~> 1.7', '>= 1.7.
|
27
|
+
spec.add_dependency "highline", '~> 1.7', '>= 1.7.0'
|
27
28
|
spec.add_dependency "CFPropertyList", '~> 2.3', '>= 2.3.0'
|
28
29
|
spec.add_dependency "rubyzip", '~> 1.0', '>= 1.0.0'
|
29
30
|
end
|
data/lib/antenna.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require "antenna/version"
|
1
|
+
require "antenna/version"
|
data/lib/antenna/command/s3.rb
CHANGED
@@ -17,7 +17,8 @@ command :s3 do |c|
|
|
17
17
|
c.option '-e', '--endpoint ENDPOINT', "S3 endpoint (optional, e.g. https://mys3.example.com)"
|
18
18
|
c.option '-x', '--expires EXPIRES', "Expiration of URLs in seconds (optional, e.g. 86400 = one day)"
|
19
19
|
c.option '-i', '--base BASE', "Base filename (optional, defaults to IPA filename without .ipa extension)"
|
20
|
-
c.option '
|
20
|
+
c.option '-p', '--public', "Use public instead of signed URLs (you'll probably want '--acl public-read' as well)"
|
21
|
+
c.option '--acl ACL', "Permissions for uploaded files. Must be one of: private, public-read, public-read-write, authenticated-read, bucket-owner-read, bucket-owner-full-control (optional, defaults to private)"
|
21
22
|
|
22
23
|
c.action do |args, options|
|
23
24
|
determine_file! unless @file = options.file
|
@@ -40,7 +41,14 @@ command :s3 do |c|
|
|
40
41
|
|
41
42
|
s3 = Antenna::Distributor::S3.new(@access_key_id, @secret_access_key, @region, @endpoint)
|
42
43
|
distributor = Antenna::Distributor.new(s3)
|
43
|
-
puts distributor.distribute @file, {
|
44
|
+
puts distributor.distribute @file, {
|
45
|
+
:bucket => @bucket,
|
46
|
+
:create => !!options.create,
|
47
|
+
:public => !!options.public,
|
48
|
+
:expire => options.expires,
|
49
|
+
:acl => options.acl,
|
50
|
+
:base => options.base
|
51
|
+
}
|
44
52
|
end
|
45
53
|
|
46
54
|
private
|
data/lib/antenna/distributor.rb
CHANGED
@@ -60,7 +60,7 @@ module Antenna
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def process_app_icon(ipa)
|
63
|
-
ipa.
|
63
|
+
ipa.bundle_icon(57, 2) || ipa.bundle_icon(57, 1) || ipa.bundle_icon(60, 2) || ipa.bundle_icon(60, 1)
|
64
64
|
end
|
65
65
|
|
66
66
|
def build_manifest(ipa, ipa_url, app_icon_url)
|
@@ -7,6 +7,7 @@ module Antenna
|
|
7
7
|
:access_key_id => access_key_id,
|
8
8
|
:secret_access_key => secret_access_key,
|
9
9
|
:region => region || "us-east-1",
|
10
|
+
:force_path_style => true
|
10
11
|
}
|
11
12
|
options[:endpoint] = endpoint if endpoint
|
12
13
|
@s3 = Aws::S3::Resource.new(options)
|
@@ -34,9 +35,14 @@ module Antenna
|
|
34
35
|
:key => filename,
|
35
36
|
:content_type => content_type,
|
36
37
|
:body => data,
|
38
|
+
:acl => @options[:acl]
|
37
39
|
})
|
38
40
|
|
39
|
-
|
41
|
+
if @options[:public]
|
42
|
+
URI.parse(object.public_url)
|
43
|
+
else
|
44
|
+
URI.parse(object.presigned_url(:get, { :expires_in => @options[:expire] }))
|
45
|
+
end
|
40
46
|
end
|
41
47
|
end
|
42
48
|
end
|
data/lib/antenna/infoplist.rb
CHANGED
@@ -1,13 +1,32 @@
|
|
1
|
-
require "
|
1
|
+
require "cfpropertylist"
|
2
2
|
|
3
3
|
module Antenna
|
4
4
|
class InfoPlist
|
5
|
-
attr_accessor :bundle_display_name, :bundle_short_version, :bundle_identifier, :bundle_version, :
|
5
|
+
attr_accessor :bundle_display_name, :bundle_short_version, :bundle_identifier, :bundle_version, :bundle_icons, :bundle_minimum_os_version
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# Class method to instantiate object with data from file.
|
9
|
+
def from_file(filename)
|
10
|
+
self.new(File.read(filename))
|
11
|
+
end
|
12
|
+
|
13
|
+
# Heuristically determines available icon sizes and resolutions from icon filenames
|
14
|
+
# and normalizes them into a hash. This hopefully works for most cases out there.
|
15
|
+
# Returns a hash of hashes like the following:
|
16
|
+
# { icon_width => { icon_resolution => icon_filename } }
|
17
|
+
def determine_icons(iconfiles)
|
18
|
+
icons = Hash.new { |h, k| h[k] = { } }
|
19
|
+
iconfiles.each { |file|
|
20
|
+
(width, height, resolution) = file.to_s.scan(/(\d+)?x?(\d+)?@?(\d+)?x?(\.png)?$/).flatten
|
21
|
+
next unless width
|
22
|
+
icons[width.to_i][(resolution || 1).to_i] = File.basename(file, ".*")
|
23
|
+
}
|
24
|
+
icons
|
25
|
+
end
|
26
|
+
end
|
6
27
|
|
7
28
|
def initialize(data)
|
8
|
-
infoplist = CFPropertyList::List.new(
|
9
|
-
:data => data,
|
10
|
-
)
|
29
|
+
infoplist = CFPropertyList::List.new(:data => data)
|
11
30
|
infoplist_data = CFPropertyList.native_types(infoplist.value)
|
12
31
|
|
13
32
|
@bundle_display_name = infoplist_data["CFBundleDisplayName"] || infoplist_data["CFBundleName"]
|
@@ -15,12 +34,15 @@ module Antenna
|
|
15
34
|
@bundle_short_version = infoplist_data["CFBundleShortVersionString"]
|
16
35
|
@bundle_version = infoplist_data["CFBundleVersion"]
|
17
36
|
@bundle_minimum_os_version = infoplist_data["MinimumOSVersion"]
|
37
|
+
@bundle_icons = {}
|
18
38
|
|
19
|
-
icons = infoplist_data["
|
20
|
-
|
21
|
-
|
22
|
-
if
|
23
|
-
|
39
|
+
if icons = infoplist_data["CFBundleIconFiles"]
|
40
|
+
@bundle_icons = self.class.determine_icons(icons)
|
41
|
+
else
|
42
|
+
if icons = infoplist_data["CFBundleIcons"]
|
43
|
+
if primary_icon = icons["CFBundlePrimaryIcon"]
|
44
|
+
@bundle_icons = self.class.determine_icons(primary_icon["CFBundleIconFiles"])
|
45
|
+
end
|
24
46
|
end
|
25
47
|
end
|
26
48
|
end
|
data/lib/antenna/ipa.rb
CHANGED
@@ -11,38 +11,34 @@ module Antenna
|
|
11
11
|
@bundle_icon_files = {}
|
12
12
|
|
13
13
|
Zip::File.open(filename) do |zipfile|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@app_name = entry
|
14
|
+
# Determine app name
|
15
|
+
@app_name = zipfile.dir.entries('Payload').
|
16
|
+
select { |file| file =~ /.app$/ }.
|
17
|
+
first
|
18
|
+
raise "Unable to determine app name from #{filename}" unless @app_name
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
say "Info.plist not found in #{filename}" and abort unless @info_plist
|
20
|
+
# Find and read Info.plist
|
21
|
+
infoplist_entry = zipfile.get_entry("Payload/#{@app_name}/Info.plist")
|
22
|
+
infoplist_data = infoplist_entry.get_input_stream.read
|
23
|
+
@info_plist = Antenna::InfoPlist.new(infoplist_data)
|
24
|
+
raise "Unable to find Info.plist in #{filename}" unless @info_plist
|
25
|
+
end
|
26
|
+
end
|
33
27
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
28
|
+
# Returns icon image data for given pixel size and resolution (defaults to 1).
|
29
|
+
def bundle_icon(size, resolution=1)
|
30
|
+
icon_data = nil
|
31
|
+
if resolutions = @info_plist.bundle_icons[size]
|
32
|
+
if filename = resolutions[resolution]
|
33
|
+
icon_glob = "Payload/#{@app_name}/#{filename}*.png"
|
34
|
+
Zip::File.open(@filename) do |zipfile|
|
35
|
+
zipfile.glob(icon_glob).each do |icon|
|
36
|
+
icon_data = icon.get_input_stream.read
|
42
37
|
end
|
43
38
|
end
|
44
39
|
end
|
45
40
|
end
|
41
|
+
icon_data
|
46
42
|
end
|
47
43
|
|
48
44
|
def input_stream
|
data/lib/antenna/manifest.rb
CHANGED
data/lib/antenna/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: antenna-ota
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobi Kremer
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.7'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.7'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: aws-sdk
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,7 +95,7 @@ dependencies:
|
|
81
95
|
version: '1.7'
|
82
96
|
- - ">="
|
83
97
|
- !ruby/object:Gem::Version
|
84
|
-
version: 1.7.
|
98
|
+
version: 1.7.0
|
85
99
|
type: :runtime
|
86
100
|
prerelease: false
|
87
101
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -91,7 +105,7 @@ dependencies:
|
|
91
105
|
version: '1.7'
|
92
106
|
- - ">="
|
93
107
|
- !ruby/object:Gem::Version
|
94
|
-
version: 1.7.
|
108
|
+
version: 1.7.0
|
95
109
|
- !ruby/object:Gem::Dependency
|
96
110
|
name: CFPropertyList
|
97
111
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,6 +158,8 @@ extensions: []
|
|
144
158
|
extra_rdoc_files: []
|
145
159
|
files:
|
146
160
|
- ".gitignore"
|
161
|
+
- ".rspec"
|
162
|
+
- ".travis.yml"
|
147
163
|
- CHANGELOG.md
|
148
164
|
- Gemfile
|
149
165
|
- LICENSE.txt
|