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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b807db1b06113f1407044abb45251cb713266f43
4
- data.tar.gz: 21078d78892d63282e1664707906e8dfea720bb8
3
+ metadata.gz: 5268c6276e2dd2cdebbd16c1a1df41dc49bab722
4
+ data.tar.gz: 953bc51904fcb09accd8aa90df9691fb7ba09113
5
5
  SHA512:
6
- metadata.gz: eb3a2ecb9baddd85d495d299dcb519927028a726fb9b009b712f2e3dd6daa94afcc45362e44c6da89c839002eb285ca094eecdae3baf8865c75721b12729f9b0
7
- data.tar.gz: a0a59e1048541991ca3e61a6f568e8a039f809c3ba13bca025758811c98192e591f1205aac3ecaa96959970a53d7e7af97ee1fbc6711f0d8be1e3c2f1a7ec814
6
+ metadata.gz: b26482d957a336ef3c6ecc5b659d0d6f07947ceccd1738687608aa94027f6aee35791f4b4c4821fc06aafe92bbbbbcc906f0fed8581d2e620583e51be42569ed
7
+ data.tar.gz: 322384d8829dcd4dd5dc5743be1a2d2b52b030250bdda63b59d48eb1ca42fa0bbe4a00a5e6beadb41a12328dc4e4f16d7acff59f1c2da813f1f95340c0c8605c
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1
6
+ - 2.2
7
+ notifications:
8
+ recipients:
9
+ - ci@funkreich.de
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Build Status](https://travis-ci.org/soulchild/antenna.svg?branch=master)](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
- ## Example
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
- The resulting URL leads to 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.
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
  ![Installation site](https://raw.githubusercontent.com/soulchild/antenna/master/assets/example-installation.png)
49
66
 
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -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.10"
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.2'
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
@@ -1 +1 @@
1
- require "antenna/version"
1
+ require "antenna/version"
@@ -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 '--acl ACL', "Permissions for uploaded files. Must be one of: public_read, private, public_read_write, authenticated_read (optional, defaults to private)"
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, { :bucket => @bucket, :create => !!options.create, :expire => options.expires, :acl => @acl, :base => options.base }
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
@@ -60,7 +60,7 @@ module Antenna
60
60
  end
61
61
 
62
62
  def process_app_icon(ipa)
63
- ipa.bundle_icon_files["57x57@2x"] || ipa.bundle_icon_files["60x60@2x"] || ipa.bundle_icon_files["60x60@3x"]
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
- URI.parse(object.presigned_url(:get, { :expires_in => @options[:expire] }))
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
@@ -1,13 +1,32 @@
1
- require "CFPropertyList"
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, :bundle_icon_filenames, :bundle_minimum_os_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["CFBundleIcons"]
20
- if icons
21
- primary_icon = icons["CFBundlePrimaryIcon"]
22
- if primary_icon
23
- @bundle_icon_filenames = primary_icon["CFBundleIconFiles"]
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
@@ -11,38 +11,34 @@ module Antenna
11
11
  @bundle_icon_files = {}
12
12
 
13
13
  Zip::File.open(filename) do |zipfile|
14
- zipfile.dir.entries("Payload").each do |entry|
15
- # Find app name
16
- if entry =~ /.app$/
17
- app_entry = zipfile.find_entry("Payload/#{entry}")
18
- if app_entry
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
- # Find and parse Info.plist
22
- infoplist_entry = zipfile.find_entry("Payload/#{@app_name}/Info.plist")
23
- if infoplist_entry
24
- infoplist_data = infoplist_entry.get_input_stream.read
25
- @info_plist = Antenna::InfoPlist.new(infoplist_data)
26
- break
27
- end
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
- # Extract main icon files
35
- @info_plist.bundle_icon_filenames.each do |icon|
36
- icon_glob = "Payload/#{@app_name}/#{icon}*.png"
37
- zipfile.glob(icon_glob).each do |entry|
38
- (width, height, resolution) = entry.to_s.scan(/(\d+)x(\d+)@(\d+)x\.png$/).flatten
39
- if width and height and resolution
40
- key = "#{width}x#{height}@#{resolution}x"
41
- @bundle_icon_files[key] = entry.get_input_stream.read
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
@@ -57,4 +57,4 @@ EOF
57
57
  ERB.new(template).result(binding)
58
58
  end
59
59
  end
60
- end
60
+ end
@@ -1,3 +1,3 @@
1
1
  module Antenna
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
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.3
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-10-29 00:00:00.000000000 Z
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.10'
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.10'
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.2
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.2
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