wonko_the_sane 0.1.2 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/wonko_the_sane +12 -28
- data/data/sources.json +0 -5
- data/lib/wonko_the_sane.rb +6 -26
- data/lib/wonko_the_sane/input/base_input.rb +5 -10
- data/lib/wonko_the_sane/input/forge_installer_profile_input.rb +29 -47
- data/lib/wonko_the_sane/input/forgefiles_mods_input.rb +4 -7
- data/lib/wonko_the_sane/input/jenkins_input.rb +11 -15
- data/lib/wonko_the_sane/input/mojang_input.rb +52 -61
- data/lib/wonko_the_sane/{reader_writer.rb → new_format.rb} +31 -35
- data/lib/wonko_the_sane/old_format.rb +123 -0
- data/lib/wonko_the_sane/registry.rb +38 -23
- data/lib/wonko_the_sane/rules.rb +6 -6
- data/lib/wonko_the_sane/timestamps.rb +4 -4
- data/lib/wonko_the_sane/tools/update_nem.rb +20 -18
- data/lib/wonko_the_sane/util/configuration.rb +54 -2
- data/lib/wonko_the_sane/util/deep_storage_cache.rb +10 -8
- data/lib/wonko_the_sane/util/extraction_cache.rb +5 -4
- data/lib/wonko_the_sane/util/file_hash_cache.rb +6 -6
- data/lib/wonko_the_sane/util/http_cache.rb +50 -105
- data/lib/wonko_the_sane/util/maven_identifier.rb +8 -14
- data/lib/wonko_the_sane/util/task_stack.rb +7 -6
- data/lib/wonko_the_sane/{version_parser.rb → util/version_parser.rb} +10 -14
- data/lib/wonko_the_sane/version.rb +1 -1
- data/lib/wonko_the_sane/versionlists/base_version_list.rb +22 -15
- data/lib/wonko_the_sane/versionlists/curse_version_list.rb +15 -16
- data/lib/wonko_the_sane/versionlists/forge_version_list.rb +34 -37
- data/lib/wonko_the_sane/versionlists/forgefiles_mods_list.rb +3 -7
- data/lib/wonko_the_sane/versionlists/jenkins_version_list.rb +6 -15
- data/lib/wonko_the_sane/versionlists/liteloader_version_list.rb +18 -28
- data/lib/wonko_the_sane/versionlists/vanilla_legacy_version_list.rb +6 -6
- data/lib/wonko_the_sane/versionlists/vanilla_version_list.rb +6 -8
- data/lib/wonko_the_sane/wonko_version.rb +32 -12
- metadata +71 -15
- data/lib/wonko_the_sane/wonkoweb_uploader.rb +0 -162
@@ -1,11 +1,63 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
1
3
|
module WonkoTheSane
|
2
4
|
module Util
|
3
5
|
class Configuration
|
6
|
+
class Aws
|
7
|
+
attr_accessor :client_id
|
8
|
+
attr_accessor :client_secret
|
9
|
+
attr_accessor :bucket
|
10
|
+
end
|
11
|
+
|
12
|
+
class WonkoWeb
|
13
|
+
attr_accessor :host
|
14
|
+
attr_accessor :email
|
15
|
+
attr_accessor :token
|
16
|
+
attr_accessor :name
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :aws
|
20
|
+
attr_reader :wonkoweb
|
4
21
|
attr_reader :lists
|
5
22
|
attr_accessor :data_path
|
23
|
+
attr_accessor :out_dir
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@lists = []
|
27
|
+
@aws = Aws.new
|
28
|
+
@wonkoweb = WonkoWeb.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_from_env
|
32
|
+
@aws.client_id = ENV['WTS_AWS_CLIENT_ID'] if ENV['WTS_AWS_CLIENT_ID']
|
33
|
+
@aws.client_secret = ENV['WTS_AWS_CLIENT_SECRET'] if ENV['WTS_AWS_CLIENT_SECRET']
|
34
|
+
@aws.bucket = ENV['WTS_AWS_BUCKET'] if ENV['WTS_AWS_BUCKET']
|
35
|
+
|
36
|
+
@wonkoweb.host = ENV['WTS_WONKOWEB_HOST'] if ENV['WTS_WONKOWEB_HOST']
|
37
|
+
@wonkoweb.email = ENV['WTS_WONKOWEB_EMAIL'] if ENV['WTS_WONKOWEB_EMAIL']
|
38
|
+
@wonkoweb.token = ENV['WTS_WONKOWEB_TOKEN'] if ENV['WTS_WONKOWEB_TOKEN']
|
39
|
+
@wonkoweb.token = ENV['WTS_WONKOWEB_NAME'] if ENV['WTS_WONKOWEB_NAME']
|
40
|
+
|
41
|
+
@data_path = ENV['WTS_DATA_PATH'] if ENV['WTS_DATA_PATH']
|
42
|
+
@out_dir = ENV['WTS_OUT_DIR'] if ENV['WTS_OUT_DIR']
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_from_file(filename)
|
46
|
+
raw = YAML.load_file filename
|
47
|
+
@aws.client_id = raw['aws']['client_id']
|
48
|
+
@aws.client_secret = raw['aws']['client_secret']
|
49
|
+
@aws.bucket = raw['aws']['bucket']
|
50
|
+
|
51
|
+
@wonkoweb.host = raw['wonkoweb']['host']
|
52
|
+
@wonkoweb.email = raw['wonkoweb']['email']
|
53
|
+
@wonkoweb.token = raw['wonkoweb']['token']
|
54
|
+
@wonkoweb.name = raw['wonkoweb']['name']
|
55
|
+
|
56
|
+
@data_path = raw['data_path']
|
57
|
+
@out_dir = raw['out_dir']
|
58
|
+
end
|
6
59
|
|
7
60
|
def register_list(list)
|
8
|
-
@lists ||= []
|
9
61
|
case list
|
10
62
|
when String
|
11
63
|
register_list list.to_sym
|
@@ -24,7 +76,7 @@ module WonkoTheSane
|
|
24
76
|
register_list ForgeFilesModsList.new(uid.to_s, urlId)
|
25
77
|
end if sources[:forgefiles]
|
26
78
|
sources[:jenkins].each do |obj|
|
27
|
-
register_list JenkinsVersionList.new(obj[:uid], obj[:url], obj[:artifact], obj[
|
79
|
+
register_list JenkinsVersionList.new(obj[:uid], obj[:url], obj[:artifact], obj[:@file_regex])
|
28
80
|
end if sources[:jenkins]
|
29
81
|
sources[:curse].each do |obj|
|
30
82
|
register_list CurseVersionList.new(obj[:uid], obj[:id], obj[:fileregex])
|
@@ -1,25 +1,27 @@
|
|
1
1
|
require 'aws-sdk-resources'
|
2
2
|
require 'wonko_the_sane/util/http_cache'
|
3
|
+
require 'wonko_the_sane'
|
3
4
|
|
4
5
|
module WonkoTheSane
|
5
6
|
module Util
|
6
7
|
class DeepStorageCache
|
7
8
|
def initialize
|
8
9
|
@resource = Aws::S3::Resource.new region: 'eu-west-1',
|
9
|
-
credentials: Aws::Credentials.new(
|
10
|
-
|
11
|
-
@bucket = @resource.bucket
|
10
|
+
credentials: Aws::Credentials.new(WonkoTheSane.configuration.aws.client_id,
|
11
|
+
WonkoTheSane.configuration.aws.client_secret)
|
12
|
+
@bucket = @resource.bucket WonkoTheSane.configuration.aws.bucket
|
12
13
|
|
13
14
|
@manifest = @bucket.object 'manifest.json'
|
14
15
|
@entries = @manifest.exists? ? JSON.parse(@manifest.get.body.read, symbolize_keys: true) : {}
|
15
16
|
end
|
16
17
|
|
18
|
+
# fetch file (if not available), get info hash, upload metadata and file to S3, return info hash
|
17
19
|
def get_info(url, options = {})
|
18
20
|
return @entries[url] if @entries.key? url
|
19
21
|
|
20
22
|
ctxt = options[:ctxt] || 'DeepStorageCache'
|
21
23
|
|
22
|
-
file = HTTPCache.file url, check_stale: false, ctxt:
|
24
|
+
file = HTTPCache.file url, check_stale: false, ctxt: ctxt
|
23
25
|
info = self.class.info_for_file file, url
|
24
26
|
|
25
27
|
@entries[url] = info
|
@@ -43,7 +45,7 @@ module WonkoTheSane
|
|
43
45
|
object.put body: file,
|
44
46
|
content_md5: md5,
|
45
47
|
content_type: content_type,
|
46
|
-
metadata: Hash[info.map { |k,v| [k.to_s, v.to_s]}]
|
48
|
+
metadata: Hash[info.map { |k, v| [k.to_s, v.to_s] }]
|
47
49
|
Logging.logger[ctxt].debug 'Backup successfully uploaded to S3'
|
48
50
|
end
|
49
51
|
end
|
@@ -52,9 +54,9 @@ module WonkoTheSane
|
|
52
54
|
end
|
53
55
|
|
54
56
|
def self.get_info(url, options = {})
|
55
|
-
if
|
56
|
-
|
57
|
-
|
57
|
+
if WonkoTheSane.configuration.aws.client_id
|
58
|
+
@instance ||= DeepStorageCache.new
|
59
|
+
@instance.get_info url, options
|
58
60
|
else
|
59
61
|
info_for_file HTTPCache.file(url, check_stale: false, ctxt: options[:ctxt]), url
|
60
62
|
end
|
@@ -7,7 +7,7 @@ class ExtractionCache
|
|
7
7
|
def get(archive, type, file)
|
8
8
|
out = path(archive, type, file)
|
9
9
|
FileUtils.mkdir_p File.dirname(out) unless Dir.exist? File.dirname(out)
|
10
|
-
|
10
|
+
unless File.exist? out
|
11
11
|
if type == :zip
|
12
12
|
Zip::File.open archive do |arch|
|
13
13
|
File.write out, arch.glob(file).first.get_input_stream.read
|
@@ -15,15 +15,16 @@ class ExtractionCache
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
|
18
|
+
File.read out
|
19
19
|
end
|
20
20
|
|
21
|
-
@@defaultCache = ExtractionCache.new 'cache/extraction'
|
22
21
|
def self.get(archive, type, file)
|
23
|
-
|
22
|
+
@cache ||= ExtractionCache.new 'cache/extraction'
|
23
|
+
@cache.get archive, type, file
|
24
24
|
end
|
25
25
|
|
26
26
|
private
|
27
|
+
|
27
28
|
def path(archive, type, file)
|
28
29
|
@basedir + '/' + File.basename(archive) + '/' + file
|
29
30
|
end
|
@@ -10,7 +10,7 @@ class FileHashCache
|
|
10
10
|
name = (file.is_a?(File) ? file.path : file).to_sym
|
11
11
|
timestamp = (file.is_a?(File) ? file.mtime : File.mtime(file)).to_i
|
12
12
|
size = file.is_a?(File) ? file.size : File.size(file)
|
13
|
-
if
|
13
|
+
if !@data[name] || !@data[name][:timestamp] == timestamp || !@data[name][:size] == size
|
14
14
|
hash = digest(file.is_a?(File) ? file.read : File.read(file))
|
15
15
|
@data[name] = {
|
16
16
|
timestamp: timestamp,
|
@@ -19,7 +19,7 @@ class FileHashCache
|
|
19
19
|
}
|
20
20
|
File.write @file, JSON.pretty_generate(@data)
|
21
21
|
end
|
22
|
-
|
22
|
+
@data[name][:hash]
|
23
23
|
end
|
24
24
|
|
25
25
|
def digest(data)
|
@@ -30,13 +30,13 @@ class FileHashCache
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
@@defaultCache = FileHashCache.new 'cache/filehashes', :sha256
|
34
33
|
def self.get(file)
|
35
|
-
|
34
|
+
@sha256_cache ||= FileHashCache.new 'cache/filehashes.json', :sha256
|
35
|
+
@sha256_cache.get file
|
36
36
|
end
|
37
37
|
|
38
|
-
@@md5Cache = FileHashCache.new 'cache/filehashes.md5', :md5
|
39
38
|
def self.get_md5(file)
|
40
|
-
|
39
|
+
@md5_cache ||= FileHashCache.new 'cache/filehashes.md5.json', :md5
|
40
|
+
@md5_cache.get file
|
41
41
|
end
|
42
42
|
end
|
@@ -1,17 +1,26 @@
|
|
1
|
-
#
|
2
|
-
#
|
1
|
+
# :cookie_jar
|
2
|
+
# :timer, response.env[:duration]
|
3
|
+
|
4
|
+
require 'faraday'
|
5
|
+
require 'faraday_connection_pool'
|
6
|
+
require 'faraday/http_cache'
|
7
|
+
require 'faraday_middleware'
|
8
|
+
require 'faraday-cookie_jar'
|
9
|
+
require 'active_support/cache'
|
10
|
+
require 'uri'
|
11
|
+
|
3
12
|
class HTTPCache
|
4
13
|
def initialize(basedir)
|
5
14
|
@basedir = basedir
|
6
15
|
FileUtils.mkdir_p @basedir unless Dir.exist? @basedir
|
7
|
-
@
|
8
|
-
@
|
16
|
+
@mutex = Mutex.new
|
17
|
+
@connections = {}
|
18
|
+
@store = ActiveSupport::Cache.lookup_store(:file_store, @basedir + '/faraday_cache/')
|
9
19
|
end
|
10
20
|
|
11
21
|
# HTTP GETs a url if it doesn't exist locally
|
12
22
|
def get(ctxt, url, key, check_stale = true)
|
13
23
|
fetch ctxt, url, key, check_stale
|
14
|
-
IO.read @basedir + '/' + key
|
15
24
|
end
|
16
25
|
|
17
26
|
def file(ctxt, url, key, check_stale = true)
|
@@ -20,124 +29,60 @@ class HTTPCache
|
|
20
29
|
end
|
21
30
|
|
22
31
|
private
|
32
|
+
|
23
33
|
def fetch(ctxt, url, key, check_stale)
|
24
34
|
cached_path = @basedir + '/' + key
|
25
35
|
cached_dir = File.dirname cached_path
|
26
36
|
FileUtils.mkdir_p cached_dir unless Dir.exist? cached_dir
|
27
37
|
|
28
|
-
|
29
|
-
if should_check cached_path, check_stale
|
30
|
-
Logging.logger[ctxt.to_s].debug "DL: #{url}"
|
31
|
-
resp = http_get ctxt.to_s, url, cached_path
|
32
|
-
unless resp == nil
|
33
|
-
File.open(cached_path, 'w') do |f|
|
34
|
-
f.write resp.body
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# get a file, using the local cached file modified timestamp to make sture we don't re-download stuff pointlessly
|
42
|
-
# this also *should* handle redirection properly
|
43
|
-
def http_get(ctxt, url, cached_path, limit = 10, http = nil)
|
44
|
-
# too many redirects...
|
45
|
-
raise ArgumentError, 'too many HTTP redirects' if limit == 0
|
46
|
-
|
47
|
-
uri = url.is_a?(URI) ? url : URI.parse(url)
|
38
|
+
return if File.exists?(cached_path) && !check_stale
|
48
39
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end
|
59
|
-
end
|
60
|
-
def http_get_internal(ctxt, uri, cached_path, limit = 10, http = nil, local_date = nil)
|
61
|
-
existing_etag = @etags[uri]
|
62
|
-
|
63
|
-
# start by doing a HEAD request
|
64
|
-
head_req = Net::HTTP::Head.new uri
|
65
|
-
head_req.add_field 'If-None-Match', existing_etag if existing_etag
|
66
|
-
head_req.add_field 'If-Modified-Since', local_date.httpdate
|
67
|
-
head_resp = http.request head_req
|
68
|
-
|
69
|
-
case head_resp
|
70
|
-
when Net::HTTPSuccess
|
71
|
-
# don't re-check this
|
72
|
-
checked cached_path
|
73
|
-
|
74
|
-
remote_date = head_resp['Last-Modified'] ? Time.httpdate(head_resp['Last-Modified']) : Time.now
|
75
|
-
new_etag = head_resp['ETag']
|
76
|
-
|
77
|
-
# if the remote resource has been modified later than the local file, grab it and return it
|
78
|
-
if remote_date > local_date || existing_etag != new_etag || !file_valid?(head_resp, cached_path)
|
79
|
-
req = Net::HTTP::Get.new(uri)
|
80
|
-
resp = http.request Net::HTTP::Get.new(uri)
|
81
|
-
Logging.logger[ctxt].debug 'GOT FULL FILE'
|
82
|
-
|
83
|
-
@etags[uri] = new_etag if new_etag
|
84
|
-
File.write @basedir + '/etags.json', JSON.pretty_generate(@etags)
|
85
|
-
|
86
|
-
return resp
|
87
|
-
else
|
88
|
-
Logging.logger[ctxt].debug 'CACHE HIT'
|
89
|
-
return nil
|
90
|
-
end
|
91
|
-
when Net::HTTPRedirection
|
92
|
-
if head_resp.code == "304"
|
93
|
-
Logging.logger[ctxt].debug 'CACHE HIT'
|
94
|
-
checked cached_path
|
95
|
-
return nil
|
40
|
+
TaskStack.in_background do
|
41
|
+
uri = URI.parse url
|
42
|
+
host = URI::HTTP.new(uri.scheme, uri.userinfo, uri.host, uri.port, nil, nil, nil, nil, nil).to_s
|
43
|
+
|
44
|
+
connection = nil
|
45
|
+
@mutex.synchronize do
|
46
|
+
connection_id = host + ctxt.to_s
|
47
|
+
@connections[connection_id] ||= create_faraday host, ctxt.to_s
|
48
|
+
connection = @connections[connection_id]
|
96
49
|
end
|
50
|
+
Logging.logger[ctxt.to_s].debug "DL: #{url}"
|
97
51
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
newurl = URI.join uri.to_s, location if newurl.relative?
|
102
|
-
return http_get ctxt, newurl, cached_path, limit - 1, http
|
103
|
-
else
|
104
|
-
Logging.logger[ctxt].warn "#{location} failed: #{head_resp.code}"
|
105
|
-
checked cached_path
|
106
|
-
return nil
|
52
|
+
response = connection.get uri.path
|
53
|
+
File.write cached_path, response.body
|
54
|
+
response.body
|
107
55
|
end
|
108
56
|
end
|
109
57
|
|
110
|
-
def
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
58
|
+
def create_faraday(host, ctxt)
|
59
|
+
Faraday.new url: host do |faraday|
|
60
|
+
faraday.use :cookie_jar
|
61
|
+
faraday.response :raise_error
|
62
|
+
faraday.response :chunked
|
63
|
+
faraday.use :http_cache,
|
64
|
+
logger: Logging.logger[ctxt],
|
65
|
+
shared_cache: true,
|
66
|
+
serializer: Marshal,
|
67
|
+
store: @store
|
68
|
+
faraday.response :follow_redirects
|
69
|
+
faraday.request :retry, max: 2, interval: 0.05, interval_randomness: 0.5, backoff_factor: 2,
|
70
|
+
exceptions: [Faraday::Error::ConnectionFailed]
|
71
|
+
|
72
|
+
faraday.adapter :net_http_pooled
|
116
73
|
end
|
117
|
-
return true
|
118
74
|
end
|
119
75
|
|
120
|
-
|
121
|
-
def should_check(cached_path, check_stale)
|
122
|
-
# if the file doesn't exist locally, or we should check for stale cache
|
123
|
-
if !File.exist? cached_path or check_stale
|
124
|
-
# but only once per run
|
125
|
-
return !@@checked_paths.include?(cached_path)
|
126
|
-
end
|
127
|
-
# otherwise don't check
|
128
|
-
return false
|
129
|
-
end
|
76
|
+
public
|
130
77
|
|
131
|
-
|
132
|
-
|
133
|
-
end
|
78
|
+
class << self; attr_accessor :cache; end
|
79
|
+
self.cache = HTTPCache.new 'cache/network'
|
134
80
|
|
135
|
-
public
|
136
|
-
@@defaultCatcher = HTTPCache.new 'cache/network'
|
137
81
|
def self.get(url, options = {})
|
138
|
-
|
82
|
+
self.cache.get(options[:ctxt] || 'Download', url, (options.key?(:key) ? options[:key] : url), options[:check_stale] || false)
|
139
83
|
end
|
84
|
+
|
140
85
|
def self.file(url, options = {})
|
141
|
-
|
86
|
+
self.cache.file(options[:ctxt] || 'Download', url, (options.key?(:key) ? options[:key] : url), options[:check_stale] || false)
|
142
87
|
end
|
143
88
|
end
|
@@ -13,26 +13,20 @@ module WonkoTheSane
|
|
13
13
|
@artifact = parts[:artifact]
|
14
14
|
@version = parts[:version]
|
15
15
|
@classifier = parts[:classifier]
|
16
|
-
@extension = parts[:extension]
|
16
|
+
@extension = parts[:extension] || 'jar'
|
17
17
|
end
|
18
18
|
|
19
19
|
def to_path
|
20
|
-
path = @group.gsub
|
21
|
-
if @classifier
|
22
|
-
|
23
|
-
end
|
24
|
-
return path + '.' + @extension
|
20
|
+
path = "#{@group.gsub /\./, '/'}/#{@artifact}/#{@version}/#{@artifact}-#{@version}"
|
21
|
+
path = "#{path}-#{@classifier}" if @classifier
|
22
|
+
"#{path}.#{@extension}"
|
25
23
|
end
|
26
24
|
|
27
25
|
def to_name
|
28
|
-
name = @group
|
29
|
-
if @classifier
|
30
|
-
|
31
|
-
|
32
|
-
if @extension != 'jar'
|
33
|
-
name = name + '@' + @extension
|
34
|
-
end
|
35
|
-
return name
|
26
|
+
name = "#{@group}:#{@artifact}:#{@version}"
|
27
|
+
name = "#{name}:#{@classifier}" if @classifier
|
28
|
+
name = "#{name}@#{@extension}" if @extension != 'jar'
|
29
|
+
name
|
36
30
|
end
|
37
31
|
end
|
38
32
|
end
|
@@ -1,21 +1,22 @@
|
|
1
1
|
class TaskStack
|
2
|
-
|
2
|
+
class << self; attr_accessor :queue; end
|
3
|
+
self.queue = []
|
3
4
|
def self.push(task)
|
4
|
-
|
5
|
+
self.queue.push task
|
5
6
|
end
|
6
7
|
def self.push_defered(task)
|
7
|
-
|
8
|
+
self.queue.unshift task
|
8
9
|
end
|
9
10
|
def self.pop
|
10
|
-
task =
|
11
|
+
task = self.queue.pop
|
11
12
|
task.call
|
12
13
|
end
|
13
14
|
def self.pop_all
|
14
|
-
self.pop until
|
15
|
+
self.pop until self.queue.empty?
|
15
16
|
end
|
16
17
|
def self.in_background(&block)
|
17
18
|
thread = Thread.new &block
|
18
19
|
TaskStack.pop_all
|
19
|
-
thread.join
|
20
|
+
thread.join.value
|
20
21
|
end
|
21
22
|
end
|