itunes_store_transporter 0.0.1
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.
- 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
|