carthage_remote_cache 0.0.5 → 0.0.10
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 +4 -4
- data/Dockerfile +7 -0
- data/Gemfile.lock +38 -38
- data/Guardfile +2 -2
- data/README.md +87 -5
- data/Rakefile +2 -2
- data/bin/carthagerc +24 -9
- data/carthage_remote_cache.gemspec +5 -5
- data/dev/start_server +1 -1
- data/lib/api.rb +49 -5
- data/lib/carthage_archive.rb +18 -8
- data/lib/carthage_dependency.rb +6 -27
- data/lib/carthage_remote_cache.rb +14 -13
- data/lib/commands.rb +5 -4
- data/lib/commands/download_command.rb +21 -12
- data/lib/commands/server_command.rb +2 -2
- data/lib/commands/upload_command.rb +18 -11
- data/lib/commands/verify_command.rb +18 -0
- data/lib/configuration.rb +34 -17
- data/lib/constants.rb +9 -7
- data/lib/crc32.rb +3 -3
- data/lib/errors.rb +59 -1
- data/lib/log.rb +1 -1
- data/lib/networking.rb +92 -33
- data/lib/server/config.ru +3 -3
- data/lib/server/server_app.rb +28 -9
- data/lib/shell_wrapper.rb +1 -1
- data/lib/table.rb +58 -0
- data/lib/utils.rb +20 -10
- data/lib/version.rb +1 -1
- data/lib/version_file.rb +31 -17
- metadata +27 -20
- data/com.kayak.carthagerc.server.plist +0 -20
data/lib/constants.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
-
CARTHAGE_DIR =
|
2
|
-
CARTHAGE_BUILD_DIR = File.join(CARTHAGE_DIR,
|
3
|
-
CARTFILE_RESOLVED =
|
4
|
-
CARTRCFILE =
|
1
|
+
CARTHAGE_DIR = "Carthage"
|
2
|
+
CARTHAGE_BUILD_DIR = File.join(CARTHAGE_DIR, "Build")
|
3
|
+
CARTFILE_RESOLVED = "Cartfile.resolved"
|
4
|
+
CARTRCFILE = "Cartrcfile"
|
5
5
|
THREAD_POOL_SIZE = 8
|
6
6
|
|
7
7
|
SERVER_DEFAULT_PORT = 9292
|
8
|
-
SERVER_CACHE_DIR = File.join(Dir.home,
|
8
|
+
SERVER_CACHE_DIR = File.join(Dir.home, ".carthagerc_server")
|
9
9
|
|
10
10
|
ARCHIVE_CHECKSUM_HEADER_REST_CLIENT = :archive_checksum
|
11
|
-
ARCHIVE_CHECKSUM_HEADER_SINATRA_IN =
|
12
|
-
ARCHIVE_CHECKSUM_HEADER_SINATRA_OUT =
|
11
|
+
ARCHIVE_CHECKSUM_HEADER_SINATRA_IN = "HTTP_ARCHIVE_CHECKSUM"
|
12
|
+
ARCHIVE_CHECKSUM_HEADER_SINATRA_OUT = "Archive-Checksum"
|
13
|
+
|
14
|
+
PLATFORMS = [:iOS, :macOS, :tvOS, :watchOS]
|
data/lib/crc32.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "digest"
|
2
|
+
require "zlib"
|
3
3
|
|
4
4
|
class Digest::CRC32 < Digest::Class
|
5
5
|
include Digest::Instance
|
@@ -17,6 +17,6 @@ class Digest::CRC32 < Digest::Class
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def finish
|
20
|
-
[@crc32].pack(
|
20
|
+
[@crc32].pack("N")
|
21
21
|
end
|
22
22
|
end
|
data/lib/errors.rb
CHANGED
@@ -19,6 +19,64 @@ class MultipleErrorsError < AppError
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
class
|
22
|
+
class VersionFileDoesNotExistError < AppError; end
|
23
|
+
|
24
|
+
class OutdatedFrameworkBuildError < AppError
|
25
|
+
attr_reader :framework_name, :build_version, :cartfile_resolved_version
|
26
|
+
|
27
|
+
def initialize(framework_name, build_version, cartfile_resolved_version)
|
28
|
+
@framework_name = framework_name
|
29
|
+
@build_version = build_version
|
30
|
+
@cartfile_resolved_version = cartfile_resolved_version
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(o)
|
34
|
+
self.class == o.class && state == o.state
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
"framework name: #{@framework_name}, build version: #{@build_version}, resolved version: #{@cartfile_resolved_version}"
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def state
|
44
|
+
[@framework_name, @build_version, @cartfile_resolved_version]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class FrameworkValidationError < AppError
|
49
|
+
def initialize(errors)
|
50
|
+
@errors = errors
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
header = ["Framework", CARTHAGE_BUILD_DIR, CARTFILE_RESOLVED]
|
55
|
+
rows = @errors.map { |e| [e.framework_name, e.build_version, e.cartfile_resolved_version] }
|
56
|
+
table = Table.new(header, rows)
|
57
|
+
<<~EOS
|
58
|
+
Detected differences between existing frameworks in '#{CARTHAGE_BUILD_DIR}' and entries in '#{CARTFILE_RESOLVED}':
|
59
|
+
|
60
|
+
#{table}
|
61
|
+
|
62
|
+
To resolve the issue:
|
63
|
+
- run `carthagerc download` to fetch missing frameworks from the server.
|
64
|
+
- if the issue persists, run `carthage bootstrap` to build frameworks and `carthagerc upload` to populate the server.
|
65
|
+
EOS
|
66
|
+
end
|
67
|
+
end
|
23
68
|
|
24
69
|
class MissingFrameworkDirectoryError < AppError; end
|
70
|
+
|
71
|
+
class ServerVersionMismatchError < AppError; end
|
72
|
+
|
73
|
+
class PlatformMismatchError < AppError
|
74
|
+
def initialize(platform)
|
75
|
+
@platform = platform
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_s
|
79
|
+
platforms = PLATFORMS.map(&:to_s).join(", ")
|
80
|
+
"Platform '#{@platform}' doesn't match any of: #{platforms}"
|
81
|
+
end
|
82
|
+
end
|
data/lib/log.rb
CHANGED
data/lib/networking.rb
CHANGED
@@ -1,23 +1,48 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "rest-client"
|
2
|
+
require "uri"
|
3
3
|
|
4
4
|
class Networking
|
5
|
-
def initialize(config)
|
5
|
+
def initialize(config, is_retry_enabled)
|
6
6
|
@config = config
|
7
|
+
@is_retry_enabled = is_retry_enabled
|
8
|
+
end
|
9
|
+
|
10
|
+
# Version
|
11
|
+
|
12
|
+
def get_server_version
|
13
|
+
url = new_version_url
|
14
|
+
$LOG.debug("Fetching server version from #{url}")
|
15
|
+
server_version = perform_network_request do
|
16
|
+
RestClient.get(url) do |response, request, result|
|
17
|
+
if response.code == 200
|
18
|
+
response.strip
|
19
|
+
else
|
20
|
+
raise AppError.new, "Failed to read server version from #{url}, response:\n #{response[0...300]}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
server_version
|
7
25
|
end
|
8
26
|
|
9
27
|
# Version Files
|
10
28
|
|
11
29
|
# @return VersionFile or nil
|
12
|
-
def download_version_file(carthage_dependency)
|
30
|
+
def download_version_file(carthage_dependency, platforms)
|
13
31
|
url = new_version_file_url(carthage_dependency)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
32
|
+
params = {}
|
33
|
+
unless platforms.nil?
|
34
|
+
params[:platform] = platforms.map(&:to_s).join(",")
|
35
|
+
end
|
36
|
+
|
37
|
+
version_file = perform_network_request do
|
38
|
+
$LOG.debug("Downloading version file from #{url}, params: #{params}")
|
39
|
+
RestClient.get(url, { params: params }) do |response, request, result|
|
40
|
+
if response.code == 200
|
41
|
+
File.write(carthage_dependency.version_filename, response.to_s)
|
42
|
+
VersionFile.new(carthage_dependency.version_filename)
|
43
|
+
else
|
44
|
+
nil
|
45
|
+
end
|
21
46
|
end
|
22
47
|
end
|
23
48
|
version_file
|
@@ -26,10 +51,12 @@ class Networking
|
|
26
51
|
# @raise AppError on upload failure
|
27
52
|
def upload_version_file(carthage_dependency)
|
28
53
|
url = new_version_file_url(carthage_dependency)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
54
|
+
perform_network_request do
|
55
|
+
$LOG.debug("Uploading #{carthage_dependency.version_filename}")
|
56
|
+
RestClient.post(url, :version_file => File.new(carthage_dependency.version_filepath)) do |response, request, result|
|
57
|
+
unless response.code == 200
|
58
|
+
raise AppError.new, "Version file upload #{carthage_dependency.version_filename} failed, response:\n #{response[0..300]}"
|
59
|
+
end
|
33
60
|
end
|
34
61
|
end
|
35
62
|
end
|
@@ -39,14 +66,16 @@ class Networking
|
|
39
66
|
# @return Hash with CarthageArchive and checksum or nil
|
40
67
|
def download_framework_archive(carthage_dependency, framework_name, platform)
|
41
68
|
url = new_framework_url(carthage_dependency, framework_name, platform)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
69
|
+
archive = perform_network_request do
|
70
|
+
$LOG.debug("Downloading framework from #{url}")
|
71
|
+
RestClient.get(url) do |response, request, result|
|
72
|
+
if response.code == 200
|
73
|
+
archive = CarthageArchive.new(framework_name, platform)
|
74
|
+
File.write(archive.archive_path, response.to_s)
|
75
|
+
{ :archive => archive, :checksum => response.headers[ARCHIVE_CHECKSUM_HEADER_REST_CLIENT] }
|
76
|
+
else
|
77
|
+
nil
|
78
|
+
end
|
50
79
|
end
|
51
80
|
end
|
52
81
|
archive
|
@@ -55,21 +84,27 @@ class Networking
|
|
55
84
|
# @raise AppError when upload fails
|
56
85
|
def upload_framework_archive(zipfile_name, carthage_dependency, framework_name, platform, checksum)
|
57
86
|
url = new_framework_url(carthage_dependency, framework_name, platform)
|
58
|
-
params = {:framework_file => File.new(zipfile_name)}
|
59
|
-
headers = {ARCHIVE_CHECKSUM_HEADER_REST_CLIENT => checksum}
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
87
|
+
params = { :framework_file => File.new(zipfile_name) }
|
88
|
+
headers = { ARCHIVE_CHECKSUM_HEADER_REST_CLIENT => checksum }
|
89
|
+
perform_network_request do
|
90
|
+
$LOG.debug("Uploading framework to #{url}, headers: #{headers}")
|
91
|
+
RestClient.post(url, params, headers) do |response, request, result|
|
92
|
+
unless response.code == 200
|
93
|
+
raise AppError.new, "Framework upload #{zipfile_name} failed, response:\n #{response[0..300]}"
|
94
|
+
end
|
64
95
|
end
|
65
96
|
end
|
66
97
|
end
|
67
98
|
|
68
99
|
private
|
69
100
|
|
101
|
+
def new_version_url
|
102
|
+
new_server_url(["version"])
|
103
|
+
end
|
104
|
+
|
70
105
|
def new_version_file_url(carthage_dependency)
|
71
106
|
new_server_url([
|
72
|
-
|
107
|
+
"versions",
|
73
108
|
@config.xcodebuild_version,
|
74
109
|
@config.swift_version,
|
75
110
|
carthage_dependency.guessed_framework_basename,
|
@@ -80,7 +115,7 @@ class Networking
|
|
80
115
|
|
81
116
|
def new_framework_url(carthage_dependency, framework_name, platform)
|
82
117
|
new_server_url([
|
83
|
-
|
118
|
+
"frameworks",
|
84
119
|
@config.xcodebuild_version,
|
85
120
|
@config.swift_version,
|
86
121
|
carthage_dependency.guessed_framework_basename,
|
@@ -96,13 +131,37 @@ class Networking
|
|
96
131
|
:scheme => @config.server_uri.scheme,
|
97
132
|
:host => @config.server_uri.host,
|
98
133
|
:port => @config.server_uri.port,
|
99
|
-
:path =>
|
134
|
+
:path => "/" + sanitized_path_slices.join("/"),
|
100
135
|
)
|
101
136
|
uri.to_s
|
102
137
|
end
|
103
138
|
|
104
139
|
# Mangle identifiers for URL paths.
|
105
140
|
def sanitized(input)
|
106
|
-
input.gsub(/\//,
|
141
|
+
input.gsub(/\//, "_")
|
142
|
+
end
|
143
|
+
|
144
|
+
def perform_network_request
|
145
|
+
if @is_retry_enabled
|
146
|
+
retries_remaining = 3
|
147
|
+
sleep_time_seconds = 5
|
148
|
+
begin
|
149
|
+
result = yield
|
150
|
+
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
|
151
|
+
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
|
152
|
+
if retries_remaining > 0
|
153
|
+
$LOG.warn("Network request failed - remaining retries: #{retries_remaining}, sleeping for: #{sleep_time_seconds}s, error: #{e.message}")
|
154
|
+
sleep(sleep_time_seconds)
|
155
|
+
retries_remaining -= 1
|
156
|
+
sleep_time_seconds *= 3
|
157
|
+
retry
|
158
|
+
else
|
159
|
+
raise e
|
160
|
+
end
|
161
|
+
end
|
162
|
+
result
|
163
|
+
else
|
164
|
+
yield
|
165
|
+
end
|
107
166
|
end
|
108
167
|
end
|
data/lib/server/config.ru
CHANGED
data/lib/server/server_app.rb
CHANGED
@@ -1,22 +1,41 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "json"
|
2
|
+
require "sinatra"
|
3
|
+
require "fileutils"
|
4
|
+
require "carthage_remote_cache"
|
4
5
|
|
5
|
-
get
|
6
|
+
get "/" do
|
6
7
|
"Welcome to carthage_remote_cache"
|
7
8
|
end
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
version_path = "/version"
|
11
|
+
versions_path = "/versions/:xcodebuild_version/:swift_version/:dependency_name/:version/:version_filename"
|
12
|
+
frameworks_path = "/frameworks/:xcodebuild_version/:swift_version/:dependency_name/:version/:framework_name/:platform"
|
13
|
+
|
14
|
+
get version_path do
|
15
|
+
status(200)
|
16
|
+
VERSION
|
17
|
+
end
|
11
18
|
|
12
19
|
get versions_path do
|
20
|
+
if params.key?(:platform)
|
21
|
+
begin
|
22
|
+
platforms = platform_to_symbols(params[:platform])
|
23
|
+
rescue AppError => e
|
24
|
+
status(400)
|
25
|
+
return JSON.pretty_generate({ "error" => e.message })
|
26
|
+
end
|
27
|
+
else
|
28
|
+
platforms = PLATFORMS
|
29
|
+
end
|
30
|
+
|
13
31
|
dirname = params_to_framework_dir(params)
|
14
32
|
filename = params[:version_filename]
|
15
33
|
filepath = File.join(dirname, filename)
|
16
34
|
|
17
35
|
if File.exist?(filepath)
|
18
36
|
status(200)
|
19
|
-
|
37
|
+
version_file = VersionFile.new(filepath, platforms)
|
38
|
+
JSON.pretty_generate(version_file.json)
|
20
39
|
else
|
21
40
|
status(404)
|
22
41
|
end
|
@@ -33,7 +52,7 @@ post versions_path do
|
|
33
52
|
File.delete(target_filename) if File.exist?(target_filename)
|
34
53
|
|
35
54
|
$LOG.info("Writing: #{target_filename}")
|
36
|
-
File.open(target_filename,
|
55
|
+
File.open(target_filename, "wb") do |target_file|
|
37
56
|
target_file.write(source_file.read)
|
38
57
|
end
|
39
58
|
|
@@ -69,7 +88,7 @@ post frameworks_path do
|
|
69
88
|
File.delete(target_filename) if File.exist?(target_filename)
|
70
89
|
|
71
90
|
$LOG.info("Writing: #{target_filename}")
|
72
|
-
File.open(target_filename,
|
91
|
+
File.open(target_filename, "wb") do |target_file|
|
73
92
|
target_file.write(source_file.read)
|
74
93
|
end
|
75
94
|
|
data/lib/shell_wrapper.rb
CHANGED
data/lib/table.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
class Table
|
2
|
+
def initialize(header, rows)
|
3
|
+
@header = header
|
4
|
+
@rows = rows
|
5
|
+
@column_sizes = calculate_column_sizes
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
lines = [
|
10
|
+
separator_line,
|
11
|
+
header_line,
|
12
|
+
separator_line,
|
13
|
+
@rows.map { |r| row_line(r) },
|
14
|
+
separator_line,
|
15
|
+
]
|
16
|
+
|
17
|
+
lines
|
18
|
+
.flatten
|
19
|
+
.reject { |line| line.empty? }
|
20
|
+
.join("\n")
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def calculate_column_sizes
|
26
|
+
all = [@header] + @rows
|
27
|
+
result = all.transpose.map do |row|
|
28
|
+
lengths = row.map { |r| r.length }
|
29
|
+
lengths.max + 2
|
30
|
+
end
|
31
|
+
result
|
32
|
+
end
|
33
|
+
|
34
|
+
def separator_line
|
35
|
+
dashes = @column_sizes.map { |size| "-" * size }
|
36
|
+
"+" + dashes.join("+") + "+"
|
37
|
+
end
|
38
|
+
|
39
|
+
def header_line
|
40
|
+
columns = @header.each_with_index.map do |column, index|
|
41
|
+
column_size = @column_sizes[index] - 1
|
42
|
+
" %-#{column_size}.#{column_size}s" % column
|
43
|
+
end
|
44
|
+
"|" + columns.join("|") + "|"
|
45
|
+
end
|
46
|
+
|
47
|
+
def row_line(row)
|
48
|
+
columns = row.each_with_index.map do |column, index|
|
49
|
+
column_size = @column_sizes[index] - 1
|
50
|
+
if index == 0
|
51
|
+
" %-#{column_size}.#{column_size}s" % column
|
52
|
+
else
|
53
|
+
"%#{column_size}.#{column_size}s " % column
|
54
|
+
end
|
55
|
+
end
|
56
|
+
"|" + columns.join("|") + "|"
|
57
|
+
end
|
58
|
+
end
|
data/lib/utils.rb
CHANGED
@@ -15,7 +15,7 @@ end
|
|
15
15
|
def quote(input)
|
16
16
|
if input.is_a? String
|
17
17
|
if input.empty?
|
18
|
-
|
18
|
+
""
|
19
19
|
else
|
20
20
|
'"' + input + '"'
|
21
21
|
end
|
@@ -23,7 +23,7 @@ def quote(input)
|
|
23
23
|
input
|
24
24
|
.map { |e| quote(e) }
|
25
25
|
.select { |e| !e.empty? }
|
26
|
-
.join(
|
26
|
+
.join(" ")
|
27
27
|
else
|
28
28
|
raise AppError.new, "Unsupported type #{input}"
|
29
29
|
end
|
@@ -32,13 +32,13 @@ end
|
|
32
32
|
def platform_to_api_string(platform)
|
33
33
|
case platform
|
34
34
|
when :iOS
|
35
|
-
|
35
|
+
"iOS"
|
36
36
|
when :macOS
|
37
|
-
|
37
|
+
"macOS"
|
38
38
|
when :tvOS
|
39
|
-
|
39
|
+
"tvOS"
|
40
40
|
when :watchOS
|
41
|
-
|
41
|
+
"watchOS"
|
42
42
|
else
|
43
43
|
raise AppError.new, "Unrecognized platform #{platform.inspect}"
|
44
44
|
end
|
@@ -47,18 +47,28 @@ end
|
|
47
47
|
def platform_to_carthage_dir_string(platform)
|
48
48
|
case platform
|
49
49
|
when :iOS
|
50
|
-
|
50
|
+
"iOS"
|
51
51
|
when :macOS
|
52
|
-
|
52
|
+
"Mac"
|
53
53
|
when :tvOS
|
54
|
-
|
54
|
+
"tvOS"
|
55
55
|
when :watchOS
|
56
|
-
|
56
|
+
"watchOS"
|
57
57
|
else
|
58
58
|
raise AppError.new, "Unrecognized platform #{platform.inspect}"
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
+
def platform_to_symbols(string)
|
63
|
+
platforms = string.split(",").map(&:to_sym)
|
64
|
+
for platform in platforms
|
65
|
+
if !PLATFORMS.include?(platform)
|
66
|
+
raise PlatformMismatchError.new(platform)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
platforms
|
70
|
+
end
|
71
|
+
|
62
72
|
# @return string in "x.y MB" format
|
63
73
|
def format_file_size(bytes)
|
64
74
|
megabytes = bytes / 1000.0 / 1000.0
|