itunes_store_transporter 0.1.3 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 603cbf0b2fe54965a1fcd25315fc2434f9987188
4
- data.tar.gz: 4c197138304f80f540f508c8a8f09910532ceef6
3
+ metadata.gz: 1706c7254e4158ee42930c52f11f48bd22ddb409
4
+ data.tar.gz: fb2b7760daa79915b5628f3631b622a4d978b375
5
5
  SHA512:
6
- metadata.gz: 57b4de9c4cc1571829c9151ec91e8b0773d094299454252ff40d348faed32100d65e242a1dcd322c014347d4167e5c9a3f93a6373e3ba7f7e8578d1ebda769af
7
- data.tar.gz: 812887659a510890d27f5e4a497b96463dca4ffe371e281e9be9b50a613e0bff3d2cd4a2df53eae8b82bd395ec4990f5240a6639b22b986efe58d975fb829809
6
+ metadata.gz: 2fb763cde86787bb12b99fdf3e1cebf2e7213d0458cccef43f9c17edbf8903ca32244829d403672ce813f5027c93882b8635a6288f5fb8bdf89645152c934a37
7
+ data.tar.gz: b3b59d68150bb088732c2c9a6f0822b59e843cdebd7e13a7439c54070baa855bb0e3da3e88fe4a43e39f8215afef7204b8b44253eedaca821132be88b414dd47
data/Changes CHANGED
@@ -1,7 +1,27 @@
1
+ v0.2.0 2017-02-07
2
+ --------------------
3
+ Changes:
4
+ * Drop support for Ruby 1.8.7
5
+ * Status command returns an Array of Hashes
6
+ * Upload command returns true on success, not the command's output
7
+
8
+ Enhancements:
9
+ * Support for statusAll mode
10
+ * Status command XML parser
11
+
12
+ Bug Fixes:
13
+ * Status command only returned a single component status
14
+
15
+ This release was brought to you by press9 media solutions berlin - http://press9.de.
16
+ Thanks press9!
17
+
1
18
  v0.1.3 2015-03-31
2
19
  --------------------
3
20
  Enhancements:
4
- * Add the :batch option to support batch operations (thanks to press9 media solutions berlin - press9.de)
21
+ * Add the :batch option to support batch operations
22
+
23
+ This release was brought to you by press9 media solutions berlin - http://press9.de.
24
+ Thanks press9!
5
25
 
6
26
  v0.1.2 2015-03-07
7
27
  --------------------
@@ -1,6 +1,7 @@
1
1
  = iTunes::Store::Transporter
2
2
 
3
3
  {<img src="https://secure.travis-ci.org/sshaw/itunes_store_transporter.svg"/>}[http://travis-ci.org/sshaw/itunes_store_transporter]
4
+ {<img src="https://ci.appveyor.com/api/projects/status/k6w6ob5f7s9j8pv8?svg=true"/>}[https://ci.appveyor.com/project/sshaw/itunes-store-transporter]
4
5
  {<img src="https://codeclimate.com/github/sshaw/itunes_store_transporter.svg" />}[https://codeclimate.com/github/sshaw/itunes_store_transporter]
5
6
 
6
7
  Upload and manage your assets in the iTunes Store using the iTunes Store's Transporter (+iTMSTransporter+).
@@ -9,8 +10,8 @@ Upload and manage your assets in the iTunes Store using the iTunes Store's Trans
9
10
 
10
11
  require "itunes/store/transporter"
11
12
 
12
- itms = iTunes::Store::Transporter.new(:username => "CouchCaster",
13
- :shortname => "bigtimer",
13
+ itms = iTunes::Store::Transporter.new(:username => "SomeUser",
14
+ :shortname => "shrt",
14
15
  :password => "w3c@llYoU!")
15
16
 
16
17
  itms.upload("/path/to/yourpackage.itmsp")
@@ -165,14 +166,14 @@ As you can see, command options are turned into template variables.
165
166
 
166
167
  === More Info
167
168
 
168
- * Docs: http://ruby-doc.org/gems/docs/i/itunes_store_transporter-0.1.1/README_rdoc.html
169
+ * Docs: http://www.rubydoc.info/gems/itunes_store_transporter
169
170
  * Bugs: http://github.com/sshaw/itunes_store_transporter/issues
170
171
  * Source Code: http://github.com/sshaw/itunes_store_transporter
171
- * Web Based GUI: http://github.com/sshaw/itunes_store_transporter_web
172
+ * Transporter GUI: http://github.com/sshaw/itunes_store_transporter_web
172
173
 
173
174
  === Author
174
175
 
175
- Skye Shaw [sshaw AT gmail.com]
176
+ Skye Shaw [skye.shaw AT gmail.com]
176
177
 
177
178
  === License
178
179
 
@@ -3,6 +3,7 @@ require "itunes/store/transporter/command/lookup"
3
3
  require "itunes/store/transporter/command/providers"
4
4
  require "itunes/store/transporter/command/schema"
5
5
  require "itunes/store/transporter/command/status"
6
+ require "itunes/store/transporter/command/status_all"
6
7
  require "itunes/store/transporter/command/upload"
7
8
  require "itunes/store/transporter/command/verify"
8
9
  require "itunes/store/transporter/command/version"
@@ -35,6 +35,7 @@ module ITunes
35
35
  end
36
36
  end
37
37
 
38
+ # TODO: problem as some errors exit 0, e.g., account locked
38
39
  if exitcode == 0
39
40
  handle_success(stdout_lines, stderr_lines, options)
40
41
  else
@@ -57,13 +58,14 @@ module ITunes
57
58
  end
58
59
 
59
60
  # TODO: conf[:warnings]
61
+ # TODO: Need to check for errors here too, 0 doesn't always mean success, e.g., account locked
60
62
  def handle_success(stdout_lines, stderr_lines, options)
61
- stdout_lines.join
63
+ stdout_lines.join("\n")
62
64
  end
63
65
 
64
66
  def handle_error(stdout_lines, stderr_lines, options, exitcode)
65
67
  parser = OutputParser.new(stderr_lines)
66
- errors = parser.errors.any? ? parser.errors : [ TransporterMessage.new(stderr_lines.join) ]
68
+ errors = parser.errors.any? ? parser.errors : [ TransporterMessage.new(stderr_lines.join("\n")) ]
67
69
  raise ExecutionError.new(errors, exitcode)
68
70
  end
69
71
 
@@ -10,10 +10,11 @@ module ITunes
10
10
  #
11
11
  class Providers < Mode
12
12
  protected
13
+
13
14
  def mode
14
15
  "provider"
15
16
  end
16
-
17
+
17
18
  def handle_success(stdout_lines, stderr_lines, options)
18
19
  providers = []
19
20
  stdout_lines.each do |line|
@@ -4,7 +4,7 @@ module ITunes
4
4
  module Store
5
5
  module Transporter
6
6
  module Command
7
-
7
+
8
8
  ##
9
9
  # Download a RelaxNG schema file for a particular metadata specification.
10
10
  #
@@ -16,8 +16,9 @@ module ITunes
16
16
  options.on :type, "-schemaType", /\A(transitional|strict)\z/i, :required => true
17
17
  options.on :version, "-schema", /\w+/i, :required => true
18
18
  end
19
-
19
+
20
20
  protected
21
+
21
22
  def mode
22
23
  "generateSchema"
23
24
  end
@@ -1,4 +1,5 @@
1
1
  require "itunes/store/transporter/command"
2
+ require "itunes/store/transporter/xml/status"
2
3
 
3
4
  module ITunes
4
5
  module Store
@@ -6,40 +7,32 @@ module ITunes
6
7
  module Command # :nodoc:
7
8
 
8
9
  ##
9
- # Retrieve the status of a previously uploaded package
10
+ # Retrieve the most recent status of previously uploaded packages
10
11
  #
11
12
  class Status < Mode
12
13
  def initialize(*config)
13
14
  super
14
- options.on *VENDOR_ID
15
+ options.on :vendor_id, "-vendor_ids", /\w/, :multiple => true
16
+ options.on :apple_id, "-apple_ids", /\w/, :multiple => true
17
+ options.on :format, "-outputFormat", %w[xml]
15
18
  end
16
19
 
17
20
  protected
18
- def handle_success(stdout_lines, stderr_lines, options)
19
- status = {}
20
- while line = stdout_lines.shift
21
- next unless line =~ /\S+/
22
- if line =~ /\A--+/
23
- entry = {}
24
- while line = stdout_lines.shift
25
- break unless line =~ /\A\s*\w/
26
- key, value = parse_line(line)
27
- entry[key] = value
28
- end
29
- (status[:status] ||= []) << entry
30
- else
31
- key, value = parse_line(line)
32
- status[key] = value
33
- end
34
- end
35
- status
21
+
22
+ def create_transporter_options(optz)
23
+ optz[:format] = "xml"
24
+ super
36
25
  end
37
26
 
38
- def parse_line(line)
39
- key, value = line.split(/:\s+/, 2).map(&:strip)
40
- key.gsub!(/\s+/, "_")
41
- key.downcase!
42
- [key.to_sym, value]
27
+ def handle_success(stdout_lines, stderr_lines, options)
28
+ # Pre-XML behavior. Not sure if it should be kept.
29
+ return [] if stdout_lines.empty?
30
+
31
+ begin
32
+ XML::Status.new.parse(stdout_lines.join(""))
33
+ rescue ParseError => e
34
+ raise TransporterError, e.message
35
+ end
43
36
  end
44
37
  end
45
38
  end
@@ -0,0 +1,21 @@
1
+ require "itunes/store/transporter/command/status"
2
+
3
+ module ITunes
4
+ module Store
5
+ module Transporter
6
+ module Command # :nodoc:
7
+
8
+ ##
9
+ # Retrieve the full status history of previously uploaded packages
10
+ #
11
+ class StatusAll < Status
12
+ protected
13
+
14
+ def mode
15
+ "statusAll"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -20,6 +20,12 @@ module ITunes
20
20
  options.on :streams, "-numStreams", Integer # Only valid if TRANSPORT is Signiant
21
21
  options.on :log_history, "-loghistory", Optout::Dir.exists
22
22
  end
23
+
24
+ protected
25
+
26
+ def handle_success(stdout_lines, stderr_lines, options)
27
+ true
28
+ end
23
29
  end
24
30
  end
25
31
  end
@@ -18,6 +18,7 @@ module ITunes
18
18
  end
19
19
 
20
20
  protected
21
+
21
22
  def create_transporter_options(optz)
22
23
  # Include the option if false
23
24
  optz[:verify_assets] = !optz[:verify_assets] if optz.include?(:verify_assets)
@@ -1,33 +1,34 @@
1
1
 
2
2
  module ITunes
3
- module Store
3
+ module Store
4
4
  module Transporter
5
5
 
6
6
  class TransporterError < StandardError; end
7
7
  class OptionError < TransporterError; end
8
-
8
+ class ParseError < TransporterError; end
9
+
9
10
  class ExecutionError < TransporterError
10
11
  attr :errors
11
- attr :exitstatus
12
-
12
+ attr :exitstatus
13
+
13
14
  def initialize(errors, exitstatus = nil)
14
15
  @errors = [ errors ].flatten
15
16
  @exitstatus = exitstatus
16
17
  super @errors.map { |e| e.to_s }.join ", "
17
- end
18
+ end
18
19
  end
19
-
20
+
20
21
  class TransporterMessage
21
22
  attr :code
22
23
  attr :message
23
-
24
+
24
25
  def initialize(message, code = nil)
25
26
  @message = message
26
27
  @code = code
27
28
  end
28
29
 
29
30
  # 1000...2000?
30
-
31
+
31
32
  def bad_data?
32
33
  (3000...4000).include?(code)
33
34
  end
@@ -102,7 +102,7 @@ module ITunes
102
102
  ##
103
103
  # :method: status
104
104
  # :call-seq:
105
- # status(options = {})
105
+ # status(options)
106
106
  #
107
107
  # Retrieve the status of a previously uploaded package.
108
108
  #
@@ -200,11 +200,18 @@ module ITunes
200
200
  end
201
201
  end
202
202
 
203
- %w|lookup providers schema status version|.each do |command|
203
+ %w|lookup providers schema version|.each do |command|
204
204
  define_method(command) { |*options| run_command(command, options.shift) }
205
205
  end
206
206
 
207
+ def status(options)
208
+ options = create_options(options)
209
+ command = options.delete(:all) ? Command::StatusAll : Command::Status
210
+ command.new(@config, @defaults).run(options)
211
+ end
212
+
207
213
  private
214
+
208
215
  def run_command(name, options)
209
216
  Command.const_get(name.capitalize).new(@config, @defaults).run(create_options(options))
210
217
  end
@@ -37,6 +37,7 @@ module ITunes
37
37
  end
38
38
 
39
39
  private
40
+
40
41
  def parse_output(output)
41
42
  output.each do |line|
42
43
  if line =~ ERROR_LINE
@@ -11,8 +11,11 @@ module ITunes
11
11
  EXE_NAME = "iTMSTransporter"
12
12
  WINDOWS_EXE = "#{EXE_NAME}.CMD"
13
13
  DEFAULT_UNIX_PATH = "/usr/local/itms/bin/#{EXE_NAME}"
14
- DEFAULT_OSX_PATHS = ["/Developer/Applications/Utilities/Application Loader.app/Contents/MacOS/itms/bin/#{EXE_NAME}",
15
- "/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/bin/#{EXE_NAME}"]
14
+
15
+ OSX_APPLICATION_LOADER_PATHS = [
16
+ "/Applications/Application Loader.app/Contents/MacOS/itms/bin/#{EXE_NAME}",
17
+ "/Developer/Applications/Utilities/Application Loader.app/Contents/MacOS/itms/bin/#{EXE_NAME}"
18
+ ]
16
19
 
17
20
  class << self
18
21
  def windows?
@@ -33,7 +36,17 @@ module ITunes
33
36
  root = ENV["PROGRAMFILES(x86)"] || ENV["PROGRAMFILES"] # Need C:\ in case?
34
37
  File.join(root, "itms", WINDOWS_EXE)
35
38
  when osx?
36
- DEFAULT_OSX_PATHS.find { |path| File.exist?(path) } || DEFAULT_UNIX_PATH
39
+ paths = OSX_APPLICATION_LOADER_PATHS.dup
40
+ root = `xcode-select --print-path`.chomp rescue ""
41
+
42
+ if !root.empty?
43
+ ["/Applications/Application Loader.app/Contents/MacOS/itms/bin/#{EXE_NAME}",
44
+ "/Applications/Application Loader.app/Contents/itms/bin/#{EXE_NAME}"].each do |path|
45
+ paths << File.join(root, "..", path)
46
+ end
47
+ end
48
+
49
+ paths.find { |path| File.exist?(path) } || DEFAULT_UNIX_PATH
37
50
  else
38
51
  DEFAULT_UNIX_PATH
39
52
  end
@@ -76,6 +89,7 @@ module ITunes
76
89
  end
77
90
 
78
91
  private
92
+
79
93
  def poll(stdout, stderr)
80
94
  read = [ stdout, stderr ]
81
95
 
@@ -1,7 +1,7 @@
1
1
  module ITunes
2
2
  module Store
3
3
  module Transporter
4
- VERSION = "0.1.3"
4
+ VERSION = "0.2.0"
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,145 @@
1
+ require "rexml/document"
2
+ require "itunes/store/transporter/errors"
3
+
4
+ module ITunes
5
+ module Store
6
+ module Transporter
7
+ module XML
8
+
9
+ ##
10
+ # XML parser for the status and statusAll commands XML output
11
+ #
12
+ class Status
13
+ NA = "N/A".freeze
14
+
15
+ ##
16
+ #
17
+ # Parse status or statusAll XML
18
+ #
19
+ # === Arguments
20
+ #
21
+ # [xml (String|IO)] The XML
22
+ #
23
+ # === Errors
24
+ #
25
+ # ParseError, ExecutionError
26
+ #
27
+ # An ExecutionError is raised if the XML contains iTMSTransporter error messages.
28
+ #
29
+ # === Returns
30
+ #
31
+ # A Hash representation of the XML output.
32
+ # Hash keys and values are slightly different (better, I hope) than the
33
+ # elements and attributes returned by Apple. See the documentation for
34
+ # ITunes::Store::Transporter::ITMSTransporter#status
35
+ #
36
+ def parse(xml)
37
+ doc = _parse(xml)
38
+ status = []
39
+
40
+ # No elements means there's just text nodes with an error message
41
+ raise self, doc.root.get_text.to_s unless doc.root.has_elements?
42
+
43
+ doc.root.each_element do |e|
44
+ next unless e.node_type == :element
45
+ status << upload_status(e)
46
+ end
47
+
48
+ status
49
+ end
50
+
51
+ private
52
+
53
+ def _parse(xml)
54
+ begin
55
+ doc = REXML::Document.new(xml)
56
+ rescue REXML::ParseException => e
57
+ raise ParseError, sprintf("%s, caused by line %s: %s",
58
+ "XML is not well-formed", e.line, e.source.buffer[0..32])
59
+ # For the other tricks REXML has up its sleeve :)
60
+ rescue => e
61
+ raise ParseError, "XML parsing failed: #{e}"
62
+ end
63
+
64
+ raise ParseError, "Invalid XML document: '#{xml[0..32]}'" unless doc.root
65
+ doc
66
+ end
67
+
68
+ def upload_status(e)
69
+ {
70
+ :apple_id => e.attributes["apple_identifier"],
71
+ :vendor_id => e.attributes["vendor_identifier"],
72
+ :content_status => content_status(e),
73
+ :info => upload_info(e)
74
+ }
75
+ end
76
+
77
+ def upload_info(e)
78
+ e.get_elements("upload_status_info").map do |info|
79
+ info.attributes.each_with_object({}) do |(name, value), hash|
80
+ hash[name.to_sym] = value
81
+ end
82
+ end
83
+ end
84
+
85
+ def content_status(e)
86
+ e = e.get_elements("content_status_info").first
87
+ return unless e
88
+
89
+ {
90
+ :status => e.attributes["content_status"],
91
+ :review_status => e.attributes["content_review_status"],
92
+ :itunes_connect_status => e.attributes["itunes_connect_status"],
93
+ :store_status => store_status(e),
94
+ :video_components => video_components(e)
95
+ }
96
+ end
97
+
98
+ def store_status(e)
99
+ e = e.get_elements("store_status").first
100
+ return unless e
101
+
102
+ e.attributes.each_with_object({}) do |(name, value), status|
103
+ status[name.to_sym] = territory_list(value)
104
+ end
105
+ end
106
+
107
+ def video_components(video)
108
+ video.get_elements("video_components/video_component").map do |e|
109
+ hash = { :name => e.attributes["component_name"] }
110
+
111
+ [:locale, :status, :delivered].each do |name|
112
+ value = e.attributes["component_#{name}"]
113
+ value = nil if value == NA
114
+ hash[name] = value
115
+ end
116
+
117
+ hash
118
+ end
119
+ end
120
+
121
+ def territory_list(value)
122
+ value && value != NA ? value.split(/\s*,\s*/) : []
123
+ end
124
+
125
+ def exception(text)
126
+ # Some overlap here with OutputParser, may want to create ErrorParser
127
+ text.sub!(/^\s*Error Summary\s*/, "")
128
+ text.strip!
129
+
130
+ errors = text.split(/\n\s*/).map do |line|
131
+ message, code = line, nil
132
+ if message =~ /(.+)\((-?\d+)\)\Z/
133
+ message, code = $1, $2.to_i
134
+ end
135
+
136
+ TransporterMessage.new(message.strip, code)
137
+ end
138
+
139
+ ExecutionError.new(errors)
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end