itunes_store_transporter 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +127 -0
- data/bin/itms +243 -0
- data/lib/itunes/store/transporter.rb +236 -0
- data/lib/itunes/store/transporter/command.rb +103 -0
- data/lib/itunes/store/transporter/command/lookup.rb +55 -0
- data/lib/itunes/store/transporter/command/option.rb +21 -0
- data/lib/itunes/store/transporter/command/providers.rb +35 -0
- data/lib/itunes/store/transporter/command/schema.rb +28 -0
- data/lib/itunes/store/transporter/command/status.rb +34 -0
- data/lib/itunes/store/transporter/command/upload.rb +27 -0
- data/lib/itunes/store/transporter/command/verify.rb +37 -0
- data/lib/itunes/store/transporter/command/version.rb +30 -0
- data/lib/itunes/store/transporter/errors.rb +67 -0
- data/lib/itunes/store/transporter/output_parser.rb +83 -0
- data/lib/itunes/store/transporter/shell.rb +94 -0
- data/lib/itunes/store/transporter/version.rb +7 -0
- data/spec/command_spec.rb +511 -0
- data/spec/errors_spec.rb +52 -0
- data/spec/output_parser_spec.rb +71 -0
- data/spec/shell_spec.rb +52 -0
- data/spec/spec_helper.rb +75 -0
- data/spec/transporter_spec.rb +106 -0
- metadata +123 -0
@@ -0,0 +1,103 @@
|
|
1
|
+
require "optout"
|
2
|
+
require "itunes/store/transporter"
|
3
|
+
require "itunes/store/transporter/shell"
|
4
|
+
require "itunes/store/transporter/errors"
|
5
|
+
require "itunes/store/transporter/output_parser"
|
6
|
+
require "itunes/store/transporter/command/option"
|
7
|
+
|
8
|
+
module ITunes
|
9
|
+
module Store
|
10
|
+
class Transporter
|
11
|
+
module Command # :nodoc: all
|
12
|
+
|
13
|
+
class Base
|
14
|
+
include Option
|
15
|
+
|
16
|
+
def initialize(config, default_options = {})
|
17
|
+
@config = config
|
18
|
+
@shell = Shell.new(@config[:path])
|
19
|
+
@default_options = default_options
|
20
|
+
end
|
21
|
+
|
22
|
+
def run(options = {})
|
23
|
+
options = default_options.merge(options)
|
24
|
+
argv = create_transporter_options(options)
|
25
|
+
stdout_lines = []
|
26
|
+
stderr_lines = []
|
27
|
+
|
28
|
+
# TODO: hooks
|
29
|
+
exitcode = @shell.exec(argv) do |line, name|
|
30
|
+
if name == :stdout
|
31
|
+
stdout_lines << line
|
32
|
+
$stdout.puts line if config[:print_stdout]
|
33
|
+
else
|
34
|
+
stderr_lines << line
|
35
|
+
$stderr.puts line if config[:print_stderr]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
if exitcode == 0
|
40
|
+
handle_success(stdout_lines, stderr_lines, options)
|
41
|
+
else
|
42
|
+
handle_error(stdout_lines, stderr_lines, options, exitcode)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
attr :config
|
48
|
+
attr :default_options
|
49
|
+
|
50
|
+
def options
|
51
|
+
@options ||= Optout.options do
|
52
|
+
# On Windows we must include this else the Transporter batch script will call `pause` after the Transporter exits
|
53
|
+
on :windows, "-WONoPause"
|
54
|
+
# Optout can't do this: [a, b, c] => -X a -X b -X c
|
55
|
+
on :jvm, "-X" #, :multiple => true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# TODO: conf[:warnings]
|
60
|
+
def handle_success(stdout_lines, stderr_lines, options)
|
61
|
+
stdout_lines.join
|
62
|
+
end
|
63
|
+
|
64
|
+
def handle_error(stdout_lines, stderr_lines, options, exitcode)
|
65
|
+
parser = Transporter::OutputParser.new(stderr_lines)
|
66
|
+
errors = parser.errors.any? ? parser.errors : [ TransporterMessage.new(stderr_lines.join) ]
|
67
|
+
raise ITunes::Store::Transporter::ExecutionError.new(errors, exitcode)
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_transporter_options(optz)
|
71
|
+
optz[:windows] = "true" if Transporter::Shell.windows?
|
72
|
+
options.argv(optz)
|
73
|
+
rescue Optout::OptionError => e
|
74
|
+
raise ITunes::Store::Transporter::OptionError, e.message
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Mode < Base
|
79
|
+
def initialize(*config)
|
80
|
+
super
|
81
|
+
options.on :log, "-o", Optout::File
|
82
|
+
options.on :verbose, "-v", %w|informational critical detailed eXtreme| # Since log output is critical to determining what's going on we can't include "off"
|
83
|
+
options.on :username, "-u", :required => true
|
84
|
+
options.on :password, "-p", :required => true
|
85
|
+
options.on :summary, "-summaryFile", Optout::File
|
86
|
+
options.on :mode, "-m", /\w+/, :required => true
|
87
|
+
options.on *SHORTNAME
|
88
|
+
end
|
89
|
+
|
90
|
+
def create_transporter_options(optz)
|
91
|
+
optz[:mode] = mode
|
92
|
+
super
|
93
|
+
end
|
94
|
+
|
95
|
+
def mode
|
96
|
+
self.class.to_s.split("::")[-1].gsub(/([a-z])([A-Z])/, "\1_\2").downcase
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "itunes/store/transporter/errors"
|
3
|
+
require "itunes/store/transporter/command"
|
4
|
+
|
5
|
+
module ITunes
|
6
|
+
module Store
|
7
|
+
class Transporter
|
8
|
+
module Command
|
9
|
+
|
10
|
+
##
|
11
|
+
# Retrieve the metadata for a previously delivered package.
|
12
|
+
#
|
13
|
+
class Lookup < Mode
|
14
|
+
def initialize(*config)
|
15
|
+
super
|
16
|
+
# These 2 are mutually exclusive, and one is required.
|
17
|
+
# Optout has no way to denote this
|
18
|
+
options.on *VENDOR_ID
|
19
|
+
options.on *APPLE_ID
|
20
|
+
options.on *DESTINATION
|
21
|
+
end
|
22
|
+
|
23
|
+
def run(options = {})
|
24
|
+
options[:destination] = Dir.mktmpdir
|
25
|
+
super
|
26
|
+
ensure
|
27
|
+
FileUtils.rm_rf(options[:destination])
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
def handle_success(stdout_lines, stderr_lines, options)
|
32
|
+
id = options[:apple_id] || options[:vendor_id]
|
33
|
+
path = File.join(options[:destination], "#{id}.itmsp", "metadata.xml")
|
34
|
+
|
35
|
+
if !File.exists?(path)
|
36
|
+
raise ITunes::Store::Transporter::TransporterError, "No metadata file exists at #{path}"
|
37
|
+
end
|
38
|
+
|
39
|
+
begin
|
40
|
+
metadata = File.read(path)
|
41
|
+
rescue StandardError => e
|
42
|
+
raise ITunes::Store::Transporter::TransporterError, "Failed to read metadata file #{path}: #{e}"
|
43
|
+
end
|
44
|
+
|
45
|
+
metadata
|
46
|
+
end
|
47
|
+
|
48
|
+
def mode
|
49
|
+
"lookupMetadata"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "optout"
|
2
|
+
|
3
|
+
module ITunes
|
4
|
+
module Store
|
5
|
+
class Transporter
|
6
|
+
module Command
|
7
|
+
module Option
|
8
|
+
# Common command options
|
9
|
+
VENDOR_ID = [ :vendor_id, "-vendor_id", /\w/ ]
|
10
|
+
APPLE_ID = [ :apple_id, "-apple_id", /\w/ ]
|
11
|
+
SHORTNAME = [ :shortname, "-s", /\w/ ]
|
12
|
+
TRANSPORT = [ :transport, "-t", %w|Aspera Signiant DAV| ]
|
13
|
+
SUCCESS = [ :success, "-success", Optout::Dir.exists ]
|
14
|
+
FAILURE = [ :failure, "-failure", Optout::Dir.exists ]
|
15
|
+
PACKAGE = [ :package, "-f", Optout::Dir.exists.named(/\.itmsp\z/), { :required => true } ]
|
16
|
+
DESTINATION = [ :destination, "-destination" ]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "itunes/store/transporter/command"
|
2
|
+
|
3
|
+
module ITunes
|
4
|
+
module Store
|
5
|
+
class Transporter
|
6
|
+
module Command
|
7
|
+
|
8
|
+
##
|
9
|
+
# List of Providers for whom your account is authorzed to deliver for.
|
10
|
+
#
|
11
|
+
class Providers < Mode
|
12
|
+
protected
|
13
|
+
def mode
|
14
|
+
"provider"
|
15
|
+
end
|
16
|
+
|
17
|
+
def handle_success(stdout_lines, stderr_lines, options)
|
18
|
+
providers = []
|
19
|
+
stdout_lines.each do |line|
|
20
|
+
line.chomp!
|
21
|
+
if line =~ /\A\d+\s+(.+?)\s+(\w+)\Z/
|
22
|
+
provider = {}
|
23
|
+
provider[:longname] = $1
|
24
|
+
provider[:shortname] = $2
|
25
|
+
providers << provider
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
providers
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "itunes/store/transporter/command"
|
2
|
+
|
3
|
+
module ITunes
|
4
|
+
module Store
|
5
|
+
class Transporter
|
6
|
+
module Command
|
7
|
+
|
8
|
+
##
|
9
|
+
# Download a RelaxNG schema file for a particular metadata specification.
|
10
|
+
#
|
11
|
+
|
12
|
+
class Schema < Mode
|
13
|
+
def initialize(*config)
|
14
|
+
super
|
15
|
+
options.on *DESTINATION
|
16
|
+
options.on :type, "-schemaType", /\A(transitional|strict)\z/i, :required => true
|
17
|
+
options.on :version, "-schema", /\w+/i, :required => true
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
def mode
|
22
|
+
"generateSchema"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "itunes/store/transporter/command"
|
2
|
+
|
3
|
+
module ITunes
|
4
|
+
module Store
|
5
|
+
class Transporter
|
6
|
+
module Command # :nodoc:
|
7
|
+
|
8
|
+
##
|
9
|
+
# Retrieve the status of a previously uploaded package
|
10
|
+
#
|
11
|
+
class Status < Mode
|
12
|
+
def initialize(*config)
|
13
|
+
super
|
14
|
+
options.on *VENDOR_ID
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
def handle_success(stdout_lines, stderr_lines, options)
|
19
|
+
status = {}
|
20
|
+
stdout_lines.each do |line|
|
21
|
+
next unless line =~ /\A\s*\w/
|
22
|
+
key, value = line.split(/:\s+/, 2).map(&:strip)
|
23
|
+
key.gsub!(/\s+/, "_")
|
24
|
+
key.downcase!
|
25
|
+
status[key.to_sym] = value
|
26
|
+
end
|
27
|
+
status
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "optout"
|
2
|
+
require "itunes/store/transporter/command"
|
3
|
+
|
4
|
+
module ITunes
|
5
|
+
module Store
|
6
|
+
class Transporter
|
7
|
+
module Command # :nodoc: all
|
8
|
+
|
9
|
+
##
|
10
|
+
# Upload a package to the iTunes Store
|
11
|
+
#
|
12
|
+
class Upload < Mode
|
13
|
+
def initialize(*config)
|
14
|
+
super
|
15
|
+
options.on *PACKAGE
|
16
|
+
options.on *TRANSPORT
|
17
|
+
options.on *SUCCESS
|
18
|
+
options.on *FAILURE
|
19
|
+
options.on :delete, "-delete", Optout::Boolean
|
20
|
+
options.on :rate, "-k", Integer # Required if TRANSPORT is Aspera or Signiant
|
21
|
+
options.on :log_history, "-loghistory", Optout::Dir.exists
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "optout"
|
2
|
+
require "itunes/store/transporter/errors"
|
3
|
+
require "itunes/store/transporter/command"
|
4
|
+
require "itunes/store/transporter/output_parser"
|
5
|
+
|
6
|
+
module ITunes
|
7
|
+
module Store
|
8
|
+
class Transporter
|
9
|
+
module Command # :nodoc:
|
10
|
+
|
11
|
+
##
|
12
|
+
# Validate the contents of a package's metadata and assets.
|
13
|
+
#
|
14
|
+
|
15
|
+
class Verify < Mode
|
16
|
+
def initialize(*config)
|
17
|
+
super
|
18
|
+
options.on *PACKAGE
|
19
|
+
options.on :verify_assets, "-disableAssetVerification", Optout::Boolean # If false verify MD only no assets
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
# Verify mode returns 0 if there are no packages to verify but will emit an error message about the lack of packages
|
24
|
+
def handle_success(stdout_lines, stderr_lines, options)
|
25
|
+
parser = Transporter::OutputParser.new(stderr_lines)
|
26
|
+
if parser.errors.any?
|
27
|
+
raise ITunes::Store::Transporter::ExecutionError.new(parser.errors, 0)
|
28
|
+
else
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "itunes/store/transporter/command"
|
2
|
+
|
3
|
+
module ITunes
|
4
|
+
module Store
|
5
|
+
class Transporter
|
6
|
+
module Command # :nodoc: all
|
7
|
+
|
8
|
+
##
|
9
|
+
# Return the +iTMSTransporter+ version.
|
10
|
+
#
|
11
|
+
class Version < Base
|
12
|
+
def initialize(*config)
|
13
|
+
super
|
14
|
+
options.on :version, "-version"
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
def create_transporter_options(optz)
|
19
|
+
optz[:version] = true
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def handle_success(stdout_lines, stderr_lines, options)
|
24
|
+
super =~ /version\s+(\d+(?:\.\d+)*)\b/i ? $1 : "Unknown"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
|
2
|
+
module ITunes
|
3
|
+
module Store
|
4
|
+
class Transporter
|
5
|
+
|
6
|
+
class TransporterError < StandardError; end
|
7
|
+
class OptionError < TransporterError; end
|
8
|
+
|
9
|
+
class ExecutionError < TransporterError
|
10
|
+
attr :errors
|
11
|
+
attr :exitstatus
|
12
|
+
|
13
|
+
def initialize(errors, exitstatus = nil)
|
14
|
+
@errors = [ errors ].flatten
|
15
|
+
@exitstatus = exitstatus
|
16
|
+
super @errors.map { |e| e.to_s }.join ", "
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class TransporterMessage
|
21
|
+
attr :code
|
22
|
+
attr :message
|
23
|
+
|
24
|
+
def initialize(message, code = nil)
|
25
|
+
@message = message
|
26
|
+
@code = code
|
27
|
+
end
|
28
|
+
|
29
|
+
# 1000...2000?
|
30
|
+
|
31
|
+
def bad_data?
|
32
|
+
(3000...4000).include?(code)
|
33
|
+
end
|
34
|
+
|
35
|
+
def invalid_data?
|
36
|
+
(4000...5000).include?(code)
|
37
|
+
end
|
38
|
+
|
39
|
+
def missing_data?
|
40
|
+
(5000...6000).include?(code)
|
41
|
+
end
|
42
|
+
|
43
|
+
def unsupported_feature?
|
44
|
+
(6000...7000).include?(code)
|
45
|
+
end
|
46
|
+
|
47
|
+
def schema_error?
|
48
|
+
(8000...9000).include?(code)
|
49
|
+
end
|
50
|
+
|
51
|
+
def asset_error?
|
52
|
+
(9000...10_000).include?(code)
|
53
|
+
end
|
54
|
+
|
55
|
+
def validation_error?
|
56
|
+
(3000...10_000).include?(code)
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
s = message.dup
|
61
|
+
s << " (#{code})" if code
|
62
|
+
s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|