publisherios 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 24b3238455b43371a19457d451568efd437aae82
4
+ data.tar.gz: 42460027d13891c1d3612df0e66a5121199dc328
5
+ SHA512:
6
+ metadata.gz: 2d5a2b528b4caa31214b421bffc34a71f3d9d2b0652be45379b3a1bfd8bede4efe412eede69271fbade6db3dce7cca639efe62fa598398b04341112e1e7e33f6
7
+ data.tar.gz: 4c5185c1e55234780f9c7ef8ca0b229cb8e8357fad73b863ae56ab840ed15a39865e57abac7adc11e6d70594c36131b0e86d2ba0afdde18607af0eade3637537
@@ -0,0 +1,11 @@
1
+ ## 0.1.3 (2015-10-28)
2
+
3
+ - Bugfix: Removed explicit binary format option for parsing Info.plist to make it work with XML plists.
4
+
5
+ ## 0.1.2 (2015-09-11)
6
+
7
+ ## 0.1.1 (yanked)
8
+
9
+ ## 0.1.0 (2015-09-09)
10
+
11
+ - Initial version.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in publisherios.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Tobias Kremer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
File without changes
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
Binary file
Binary file
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'antenna/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "publisherios"
8
+ spec.version = Antenna::VERSION
9
+ spec.authors = ["Generic"]
10
+ spec.email = ["generic"]
11
+ spec.summary = %q{Painless iOS over-the-air enterprise distribution}
12
+ spec.description = %q{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.}
13
+ spec.homepage = "https://www.github.com/soulchild/antenna"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ spec.add_dependency "aws-sdk", '~> 2.0', '>= 2.0.0'
26
+ spec.add_dependency "commander", "~> 4.3"
27
+ spec.add_dependency "highline", '~> 1.7', '>= 1.7.0'
28
+ spec.add_dependency "CFPropertyList", '~> 2.3', '>= 2.3.0'
29
+ spec.add_dependency "rubyzip", '~> 1.0', '>= 1.0.0'
30
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "awesome"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'commander/import'
4
+ require 'antenna'
5
+
6
+ program :name, 'Antenna'
7
+ program :version, Antenna::VERSION
8
+ program :description, 'Painless iOS over-the-air enterprise distribution'
9
+
10
+ program :help, 'Author', 'Jáder Nunes <tobias@funkreich.de>'
11
+ program :help, 'Website', 'https://www.github.com/soulchild/antenna'
12
+ program :help_formatter, :compact
13
+
14
+ # global_option('--verbose') { $verbose = true }
15
+
16
+ default_command :help
17
+
18
+ require 'antenna/commands'
@@ -0,0 +1 @@
1
+ require "antenna/version"
@@ -0,0 +1,74 @@
1
+ require 'distributor'
2
+ require 'distributor/s3'
3
+
4
+ command :s3 do |c|
5
+ c.name = "s3"
6
+ c.syntax = "antenna s3 [options]"
7
+ c.summary = "Distribute .ipa file over Amazon S3"
8
+
9
+ c.example 'Distribute "awesome.ipa" to S3 bucket "bucket_name"', 'antenna s3 --file ./awesome.ipa -a access_key_id -s secret_access_key --create -b bucket_name'
10
+
11
+ c.option '-f', '--file FILE', '.ipa file to distribute (searches current directory for .ipa files if not specified)'
12
+ c.option '-a', '--access-key-id ACCESS_KEY_ID', 'S3 access key ID'
13
+ c.option '-s', '--secret-access-key SECRET_ACCESS_KEY', 'S3 secret access key'
14
+ c.option '-b', '--bucket BUCKET', 'S3 bucket name'
15
+ c.option '--[no-]create', "(Don't) create bucket if it doesn't already exist"
16
+ c.option '-r', '--region REGION', "AWS region (optional, defaults to us-east-1)"
17
+ c.option '-e', '--endpoint ENDPOINT', "S3 endpoint (optional, e.g. https://mys3.example.com)"
18
+ c.option '-x', '--expires EXPIRES', "Expiration of URLs in seconds (optional, e.g. 86400 = one day)"
19
+ c.option '-i', '--base BASE', "Base filename (optional, defaults to IPA filename without .ipa extension)"
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)"
22
+
23
+ c.action do |args, options|
24
+ determine_file! unless @file = options.file
25
+ say_error "Missing .ipa file" and abort unless @file and File.exist?(@file)
26
+
27
+ determine_access_key_id! unless @access_key_id = options.access_key_id
28
+ say_error "Missing S3 access key ID" and abort unless @access_key_id
29
+
30
+ determine_secret_access_key! unless @secret_access_key = options.secret_access_key
31
+ say_error "Missing S3 secret access key" and abort unless @secret_access_key
32
+
33
+ determine_bucket! unless @bucket = options.bucket
34
+ say_error "Missing S3 bucket name" and abort unless @bucket
35
+
36
+ @endpoint = options.endpoint
37
+ unless @endpoint
38
+ determine_region! unless @region = options.region
39
+ say_error "Missing either S3 region or endpoint" and abort unless @region
40
+ end
41
+
42
+ s3 = Antenna::Distributor::S3.new(@access_key_id, @secret_access_key, @region, @endpoint)
43
+ distributor = Antenna::Distributor.new(s3)
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
+ }
52
+ end
53
+
54
+ private
55
+
56
+ def determine_access_key_id!
57
+ @access_key_id ||= ENV['AWS_ACCESS_KEY_ID']
58
+ @access_key_id ||= ask "S3 access key ID:"
59
+ end
60
+
61
+ def determine_secret_access_key!
62
+ @secret_access_key ||= ENV['AWS_SECRET_ACCESS_KEY']
63
+ @secret_access_key ||= ask "S3 secret access key:"
64
+ end
65
+
66
+ def determine_bucket!
67
+ @bucket ||= ENV['AWS_BUCKET']
68
+ @bucket ||= ask "S3 bucket name:"
69
+ end
70
+
71
+ def determine_region!
72
+ @region ||= ENV['AWS_REGION']
73
+ end
74
+ end
@@ -0,0 +1,16 @@
1
+ $:.push File.expand_path('../', __FILE__)
2
+
3
+ require 'command/s3'
4
+
5
+ private
6
+
7
+ def determine_file!
8
+ files = Dir['*.ipa']
9
+ @file ||= case files.length
10
+ when 0 then nil
11
+ when 1 then files.first
12
+ else
13
+ @file = choose "Select an .ipa File:", *files
14
+ end
15
+ puts "Found ipa file #{@file}" if @file
16
+ end
@@ -0,0 +1,74 @@
1
+ require 'antenna/ipa'
2
+ require 'antenna/manifest'
3
+ require 'antenna/html'
4
+
5
+ module Antenna
6
+ class Distributor
7
+ def initialize(distributor)
8
+ @distributor = distributor
9
+ end
10
+
11
+ def distribute(ipa_file, options = {})
12
+ base_filename = options[:base] || File.basename(ipa_file, ".ipa")
13
+
14
+ # Let distributor set things up (if necessary)
15
+ @distributor.setup(ipa_file, options) if @distributor.respond_to?(:setup)
16
+
17
+ # Distribute IPA
18
+ ipa = process_ipa(ipa_file)
19
+ ipa_url = @distributor.distribute(
20
+ ipa.input_stream.read,
21
+ "#{base_filename}.ipa",
22
+ "application/octet-stream",
23
+ )
24
+
25
+ # Distribute App Icon
26
+ if app_icon = process_app_icon(ipa)
27
+ app_icon_url = @distributor.distribute(
28
+ app_icon,
29
+ "#{base_filename}.png",
30
+ "image/png",
31
+ )
32
+ end
33
+
34
+ # Distribute Manifest
35
+ manifest = build_manifest(ipa, ipa_url, app_icon_url)
36
+ manifest_url = @distributor.distribute(
37
+ manifest.to_s,
38
+ "#{base_filename}.plist",
39
+ "text/xml",
40
+ )
41
+
42
+ # Distribute HTML
43
+ html = build_html(ipa, manifest_url, app_icon_url)
44
+ html_url = @distributor.distribute(
45
+ html.to_s,
46
+ "#{base_filename}.html",
47
+ "text/html",
48
+ )
49
+
50
+ # Let distributor clean things up (if necessary)
51
+ @distributor.teardown if @distributor.respond_to?(:teardown)
52
+
53
+ return html_url
54
+ end
55
+
56
+ private
57
+
58
+ def process_ipa(ipa_file)
59
+ Antenna::IPA.new(ipa_file)
60
+ end
61
+
62
+ def process_app_icon(ipa)
63
+ ipa.bundle_icon(57, 2) || ipa.bundle_icon(57, 1) || ipa.bundle_icon(60, 2) || ipa.bundle_icon(60, 1)
64
+ end
65
+
66
+ def build_manifest(ipa, ipa_url, app_icon_url)
67
+ Antenna::Manifest.new(ipa_url, ipa.info_plist, app_icon_url)
68
+ end
69
+
70
+ def build_html(ipa, manifest_url, app_icon_url)
71
+ Antenna::HTML.new(ipa.info_plist, manifest_url, app_icon_url)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,48 @@
1
+ require 'aws-sdk'
2
+
3
+ module Antenna
4
+ class Distributor::S3
5
+ def initialize(access_key_id, secret_access_key, region, endpoint = nil)
6
+ options = {
7
+ :access_key_id => access_key_id,
8
+ :secret_access_key => secret_access_key,
9
+ :region => region || "us-east-1",
10
+ :force_path_style => true
11
+ }
12
+ options[:endpoint] = endpoint if endpoint
13
+ @s3 = Aws::S3::Resource.new(options)
14
+ end
15
+
16
+ def setup(ipa_file, options = {})
17
+ @options = options
18
+ @options[:expire] = @options[:expire] ? @options[:expire].to_i : 86400
19
+ @options[:acl] ||= "private"
20
+
21
+ if @options[:create]
22
+ puts "Creating bucket #{@options[:bucket]} with ACL #{@options[:acl]}..."
23
+
24
+ @s3.create_bucket({
25
+ :bucket => @options[:bucket],
26
+ :acl => @options[:acl],
27
+ })
28
+ end
29
+ end
30
+
31
+ def distribute(data, filename, content_type)
32
+ puts "Distributing #{filename} ..."
33
+
34
+ object = @s3.bucket(@options[:bucket]).put_object({
35
+ :key => filename,
36
+ :content_type => content_type,
37
+ :body => data,
38
+ :acl => @options[:acl]
39
+ })
40
+
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
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,181 @@
1
+ require 'erb'
2
+
3
+ module Antenna
4
+ class HTML
5
+ include ERB::Util
6
+
7
+ attr_accessor :info_plist, :manifest_url, :display_image_url, :need_shine
8
+
9
+ def initialize(info_plist, manifest_url, display_image_url)
10
+ @info_plist, @manifest_url, @display_image_url = info_plist, manifest_url, display_image_url
11
+ end
12
+
13
+ def template
14
+ <<-EOF
15
+ <!doctype html>
16
+ <html>
17
+ <head>
18
+ <title><%= @info_plist.bundle_display_name %> v<%= @info_plist.bundle_short_version %> (<%= @info_plist.bundle_version %>)</title>
19
+ <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
20
+ <meta charset="UTF-8">
21
+ <meta http-equiv="cache-control" content="no-cache" />
22
+ <meta http-equiv="Pragma" content="no-cache" />
23
+ <meta http-equiv="Expires" content="-1" />
24
+ <style type="text/css">
25
+ body { font-family: Helvetica, Arial, sans-serif; text-align: center; color: #444; font-size: 18px; }
26
+ img { display: block; margin: 1em auto; border: none; width: 120px; height: 120px; border-radius: 20px; }
27
+ .btn, a.btn, a.btn:visited, a.btn:hover, a.btn:link {
28
+ display: inline-block;
29
+ border-radius: 3px;
30
+ background-color: #0095c8;
31
+ color: white;
32
+ padding: .8em 1em;
33
+ text-decoration: none;
34
+ }
35
+ a.btn:hover {
36
+ background-color: #00bbfb;
37
+ color: white;
38
+ }
39
+ .disabled {
40
+ background-color: gray !important;
41
+ cursor: default;
42
+ }
43
+ p {
44
+ color: #999
45
+ }
46
+ </style>
47
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
48
+ </head>
49
+ <body>
50
+ <h1><%= @info_plist.bundle_display_name %></h1>
51
+ <h2><%= @info_plist.bundle_short_version %> (<%= @info_plist.bundle_version %>)</h2>
52
+ <% if @display_image_url %>
53
+ <a href="itms-services://?action=download-manifest&amp;url=<%= u(@manifest_url) %>">
54
+ <img src="<%= @display_image_url %>">
55
+ </a>
56
+ <% end %>
57
+ <div align="center">
58
+ <table style="width: 95%">
59
+ <tr>
60
+ <td align="center" style="height: 50pt"><input id="checkTerm" type="checkbox" onchange="validateButton()"/>
61
+ <label >&nbspCheck to be able to download</label></td>
62
+ </tr>
63
+ <tr>
64
+ <td align="center">&nbsp</td>
65
+ </tr>
66
+ <tr>
67
+ <td align="center"><a id="buttonDownload" onclick="" class="btn disabled">Tap to install</a></td>
68
+ </tr>
69
+ <tr>
70
+ <td align="center">&nbsp</td>
71
+ </tr>
72
+ <tr >
73
+ <td align="center" >
74
+ <div >
75
+ <h2>Cheesecake Certificate</h2>
76
+ </div>
77
+ </td>
78
+ </tr>
79
+ <tr >
80
+ <td align="center" >
81
+ <div >
82
+ <label><font size="3">After install the app, follow the instructions below</font></label>
83
+ </div>
84
+ </td>
85
+ </tr>
86
+ <tr>
87
+ <td align="center">&nbsp</td>
88
+ </tr>
89
+ <tr>
90
+ <td align="center"><b>1 - Access Settings</b></td>
91
+ </tr>
92
+ <tr>
93
+ <td align="center"><img src="Passo_1.jpeg" style="border-radius:0; width:250px; height:450px"></td>
94
+ </tr>
95
+ <tr>
96
+ <td align="center">&nbsp</td>
97
+ </tr>
98
+ <tr>
99
+ <td align="center"><b>2 - Click on menu General</b></td>
100
+ </tr>
101
+ <tr>
102
+ <td align="center"><img src="Passo_2.jpeg" style="border-radius:0; width:250px; height:450px"></td>
103
+ </tr>
104
+ <tr>
105
+ <td align="center">&nbsp</td>
106
+ </tr>
107
+ <tr>
108
+ <td align="center"><b>3 - Click on Profiles & Device Management</b></td>
109
+ </tr>
110
+ <tr>
111
+ <td align="center"><img src="Passo_3.jpeg" style="border-radius:0; width:250px; height:450px"></td>
112
+ </tr>
113
+ <tr>
114
+ <td align="center">&nbsp</td>
115
+ </tr>
116
+ <tr>
117
+ <td align="center"><b>4 - Click on Cheesecake Labs Software S A</b></td>
118
+ </tr>
119
+ <tr>
120
+ <td align="center"><img src="Passo_4.jpeg" style="border-radius:0; width:250px; height:450px"></td>
121
+ </tr>
122
+ <tr>
123
+ <td align="center">&nbsp</td>
124
+ </tr>
125
+ <tr>
126
+ <td align="center"><b>5 - Click on: Trust "Cheesecake Labs Software S A"</b></td>
127
+ </tr>
128
+ <tr>
129
+ <td align="center"><img src="Passo_5.jpeg" style="border-radius:0; width:250px; height:450px"></td>
130
+ </tr>
131
+ <tr>
132
+ <td align="center">&nbsp</td>
133
+ </tr>
134
+ <tr>
135
+ <td align="center"><b>6 - Click on: Trust<b/></td>
136
+ </tr>
137
+ <tr>
138
+ <td align="center"><img src="Passo_6.jpeg" style="border-radius:0; width:250px; height:450px"></td>
139
+ </tr>
140
+ <tr>
141
+ <td align="center"><b>Done, now you can use the app :) <b/></td>
142
+ </tr>
143
+ <tr>
144
+ <td align="center">&nbsp</td>
145
+ </tr>
146
+ </table>
147
+ </div>
148
+ <% if @info_plist.bundle_minimum_os_version %>
149
+ <p class="comment">
150
+ This app requires iOS <%= @info_plist.bundle_minimum_os_version %> or higher.
151
+ </p>
152
+ <% end %>
153
+ </body>
154
+ </html>
155
+ <script>
156
+ function validateButton() {
157
+ if(document.getElementById('checkTerm').checked == true){
158
+ $('#buttonDownload').removeClass('disabled');
159
+ } else {
160
+ $('#buttonDownload').addClass('disabled');
161
+ }
162
+ }
163
+
164
+ $('#buttonDownload').click(function(e){
165
+ window.location.href = 'itms-services://?action=download-manifest&amp;url=<%= u(@manifest_url) %>'
166
+ })
167
+
168
+ $('.disabled').click(function(e){
169
+ e.preventDefault();
170
+ })
171
+
172
+
173
+ </script>
174
+ EOF
175
+ end
176
+
177
+ def to_s
178
+ ERB.new(template).result(binding)
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,50 @@
1
+ require "cfpropertylist"
2
+
3
+ module Antenna
4
+ class InfoPlist
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
27
+
28
+ def initialize(data)
29
+ infoplist = CFPropertyList::List.new(:data => data)
30
+ infoplist_data = CFPropertyList.native_types(infoplist.value)
31
+
32
+ @bundle_display_name = infoplist_data["CFBundleDisplayName"] || infoplist_data["CFBundleName"]
33
+ @bundle_identifier = infoplist_data["CFBundleIdentifier"]
34
+ @bundle_short_version = infoplist_data["CFBundleShortVersionString"]
35
+ @bundle_version = infoplist_data["CFBundleVersion"]
36
+ @bundle_minimum_os_version = infoplist_data["MinimumOSVersion"]
37
+ @bundle_icons = {}
38
+
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
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,48 @@
1
+ require "zip"
2
+ require "zip/filesystem"
3
+ require "antenna/infoplist"
4
+
5
+ module Antenna
6
+ class IPA
7
+ attr_accessor :filename, :app_name, :info_plist, :bundle_icon_files
8
+
9
+ def initialize(filename)
10
+ @filename = filename
11
+ @bundle_icon_files = {}
12
+
13
+ Zip::File.open(filename) do |zipfile|
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
19
+
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
27
+
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
37
+ end
38
+ end
39
+ end
40
+ end
41
+ icon_data
42
+ end
43
+
44
+ def input_stream
45
+ File.open(@filename, "r")
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,60 @@
1
+ require 'erb'
2
+
3
+ module Antenna
4
+ class Manifest
5
+ include ERB::Util
6
+
7
+ attr_accessor :info_plist, :ipa_url, :display_image_url, :need_shine
8
+
9
+ def initialize(ipa_url, info_plist, display_image_url)
10
+ @ipa_url, @info_plist, @display_image_url = ipa_url, info_plist, display_image_url
11
+ end
12
+
13
+ def template
14
+ <<-EOF
15
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
16
+ <plist version="1.0">
17
+ <dict>
18
+ <key>items</key>
19
+ <array>
20
+ <dict>
21
+ <key>assets</key>
22
+ <array>
23
+ <dict>
24
+ <key>kind</key>
25
+ <string>software-package</string>
26
+ <key>url</key>
27
+ <string><%= h(@ipa_url) %></string>
28
+ </dict>
29
+ <dict>
30
+ <key>kind</key>
31
+ <string>display-image</string>
32
+ <key>needs-shine</key>
33
+ <false/>
34
+ <key>url</key>
35
+ <string><%= h(@display_image_url) %></string>
36
+ </dict>
37
+ </array>
38
+ <key>metadata</key>
39
+ <dict>
40
+ <key>bundle-identifier</key>
41
+ <string><%= @info_plist.bundle_identifier %></string>
42
+ <key>bundle-version</key>
43
+ <string><%= @info_plist.bundle_short_version %></string>
44
+ <key>kind</key>
45
+ <string>software</string>
46
+ <key>title</key>
47
+ <string><%= @info_plist.bundle_display_name %></string>
48
+ </dict>
49
+ </dict>
50
+ </array>
51
+ </dict>
52
+ </plist>
53
+ EOF
54
+ end
55
+
56
+ def to_s
57
+ ERB.new(template).result(binding)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,3 @@
1
+ module Antenna
2
+ VERSION = "0.1.7"
3
+ end
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: publisherios
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.7
5
+ platform: ruby
6
+ authors:
7
+ - Generic
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-08-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
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'
55
+ - !ruby/object:Gem::Dependency
56
+ name: aws-sdk
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 2.0.0
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '2.0'
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 2.0.0
75
+ - !ruby/object:Gem::Dependency
76
+ name: commander
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '4.3'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '4.3'
89
+ - !ruby/object:Gem::Dependency
90
+ name: highline
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.7'
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: 1.7.0
99
+ type: :runtime
100
+ prerelease: false
101
+ version_requirements: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '1.7'
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 1.7.0
109
+ - !ruby/object:Gem::Dependency
110
+ name: CFPropertyList
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '2.3'
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: 2.3.0
119
+ type: :runtime
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '2.3'
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: 2.3.0
129
+ - !ruby/object:Gem::Dependency
130
+ name: rubyzip
131
+ requirement: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: '1.0'
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 1.0.0
139
+ type: :runtime
140
+ prerelease: false
141
+ version_requirements: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.0'
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: 1.0.0
149
+ description: Antenna aims to take the pain out of creating and distributing all the
150
+ necessary files for Enterprise iOS over-the-air distribution. It generates the mandatory
151
+ XML manifest, app icons and an HTML file, automatically extracting all the needed
152
+ information from the specified .ipa file.
153
+ email:
154
+ - generic
155
+ executables:
156
+ - antenna
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - CHANGELOG.md
161
+ - Gemfile
162
+ - LICENSE.txt
163
+ - README.md
164
+ - Rakefile
165
+ - antenna-ota-0.1.4.gem
166
+ - antenna-ota-0.1.6.gem
167
+ - antenna.gemspec
168
+ - assets/example-installation.png
169
+ - bin/console
170
+ - bin/setup
171
+ - exe/antenna
172
+ - lib/antenna.rb
173
+ - lib/antenna/command/s3.rb
174
+ - lib/antenna/commands.rb
175
+ - lib/antenna/distributor.rb
176
+ - lib/antenna/distributor/s3.rb
177
+ - lib/antenna/html.rb
178
+ - lib/antenna/infoplist.rb
179
+ - lib/antenna/ipa.rb
180
+ - lib/antenna/manifest.rb
181
+ - lib/antenna/version.rb
182
+ homepage: https://www.github.com/soulchild/antenna
183
+ licenses:
184
+ - MIT
185
+ metadata: {}
186
+ post_install_message:
187
+ rdoc_options: []
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ requirements: []
201
+ rubyforge_project:
202
+ rubygems_version: 2.5.2.3
203
+ signing_key:
204
+ specification_version: 4
205
+ summary: Painless iOS over-the-air enterprise distribution
206
+ test_files: []