u3d 0.9
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 +7 -0
- data/.gitignore +2 -0
- data/.licenses.json +19 -0
- data/.rspec +1 -0
- data/.rubocop.yml +43 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +98 -0
- data/LICENSE +21 -0
- data/LICENSE.fastlane +22 -0
- data/LOG_RULES.md +170 -0
- data/README.md +72 -0
- data/Rakefile +28 -0
- data/TODO.md +15 -0
- data/build.sh +5 -0
- data/config/log_rules.json +230 -0
- data/examples/Example1/.gitignore +19 -0
- data/examples/Example1/Assets/Editor.meta +9 -0
- data/examples/Example1/Assets/Editor/EditorRun.cs +23 -0
- data/examples/Example1/Assets/Editor/EditorRun.cs.meta +12 -0
- data/examples/Example1/Assets/Editor/FileSystemUtil.cs +26 -0
- data/examples/Example1/Assets/Editor/FileSystemUtil.cs.meta +12 -0
- data/examples/Example1/Assets/Scene1.unity +264 -0
- data/examples/Example1/Assets/Scene1.unity.meta +8 -0
- data/examples/Example1/Gemfile +8 -0
- data/examples/Example1/Gemfile.lock +165 -0
- data/examples/Example1/ProjectSettings/AudioManager.asset +16 -0
- data/examples/Example1/ProjectSettings/ClusterInputManager.asset +6 -0
- data/examples/Example1/ProjectSettings/DynamicsManager.asset +18 -0
- data/examples/Example1/ProjectSettings/EditorBuildSettings.asset +7 -0
- data/examples/Example1/ProjectSettings/EditorSettings.asset +14 -0
- data/examples/Example1/ProjectSettings/GraphicsSettings.asset +61 -0
- data/examples/Example1/ProjectSettings/InputManager.asset +295 -0
- data/examples/Example1/ProjectSettings/NavMeshAreas.asset +89 -0
- data/examples/Example1/ProjectSettings/NetworkManager.asset +8 -0
- data/examples/Example1/ProjectSettings/Physics2DSettings.asset +35 -0
- data/examples/Example1/ProjectSettings/ProjectSettings.asset +591 -0
- data/examples/Example1/ProjectSettings/ProjectVersion.txt +1 -0
- data/examples/Example1/ProjectSettings/QualitySettings.asset +180 -0
- data/examples/Example1/ProjectSettings/TagManager.asset +43 -0
- data/examples/Example1/ProjectSettings/TimeManager.asset +9 -0
- data/examples/Example1/ProjectSettings/UnityConnectSettings.asset +32 -0
- data/examples/Example1/README.md +5 -0
- data/examples/Example1/Rakefile +5 -0
- data/examples/Example1/fastlane/Fastfile +4 -0
- data/examples/Example1/fastlane/Pluginfile +1 -0
- data/examples/Example1/run.sh +1 -0
- data/examples/Example2/.gitignore +20 -0
- data/examples/Example2/Assets/Editor.meta +9 -0
- data/examples/Example2/Assets/Editor/EditorRun.cs +33 -0
- data/examples/Example2/Assets/Editor/EditorRun.cs.meta +12 -0
- data/examples/Example2/Assets/Editor/PostprocessBuildPlayer.cs +92 -0
- data/examples/Example2/Assets/Editor/PostprocessBuildPlayer.cs.meta +8 -0
- data/examples/Example2/Assets/Editor/PostprocessBuildPlayer_log.sh +31 -0
- data/examples/Example2/Assets/Editor/PostprocessBuildPlayer_log.sh.meta +8 -0
- data/examples/Example2/Assets/Editor/SimpleBuildSetup.cs +20 -0
- data/examples/Example2/Assets/Editor/SimpleBuildSetup.cs.meta +12 -0
- data/examples/Example2/Assets/Scene.unity +278 -0
- data/examples/Example2/Assets/Scene.unity.meta +8 -0
- data/examples/Example2/Gemfile +8 -0
- data/examples/Example2/Gemfile.lock +165 -0
- data/examples/Example2/ProjectSettings/AudioManager.asset +17 -0
- data/examples/Example2/ProjectSettings/ClusterInputManager.asset +6 -0
- data/examples/Example2/ProjectSettings/DynamicsManager.asset +19 -0
- data/examples/Example2/ProjectSettings/EditorBuildSettings.asset +10 -0
- data/examples/Example2/ProjectSettings/EditorSettings.asset +14 -0
- data/examples/Example2/ProjectSettings/GraphicsSettings.asset +63 -0
- data/examples/Example2/ProjectSettings/InputManager.asset +295 -0
- data/examples/Example2/ProjectSettings/NavMeshAreas.asset +89 -0
- data/examples/Example2/ProjectSettings/NetworkManager.asset +8 -0
- data/examples/Example2/ProjectSettings/Physics2DSettings.asset +36 -0
- data/examples/Example2/ProjectSettings/ProjectSettings.asset +591 -0
- data/examples/Example2/ProjectSettings/ProjectVersion.txt +1 -0
- data/examples/Example2/ProjectSettings/QualitySettings.asset +193 -0
- data/examples/Example2/ProjectSettings/TagManager.asset +43 -0
- data/examples/Example2/ProjectSettings/TimeManager.asset +9 -0
- data/examples/Example2/ProjectSettings/UnityConnectSettings.asset +34 -0
- data/examples/Example2/README.md +10 -0
- data/examples/Example2/fastlane/Fastfile +4 -0
- data/examples/Example2/fastlane/Pluginfile +1 -0
- data/exe/u3d +7 -0
- data/fastlane-plugin-u3d/.gitignore +10 -0
- data/fastlane-plugin-u3d/.licenses.json +9 -0
- data/fastlane-plugin-u3d/.rspec +3 -0
- data/fastlane-plugin-u3d/.rubocop.yml +253 -0
- data/fastlane-plugin-u3d/.travis.yml +4 -0
- data/fastlane-plugin-u3d/Gemfile +6 -0
- data/fastlane-plugin-u3d/LICENSE +21 -0
- data/fastlane-plugin-u3d/README.md +52 -0
- data/fastlane-plugin-u3d/Rakefile +9 -0
- data/fastlane-plugin-u3d/circle.yml +9 -0
- data/fastlane-plugin-u3d/fastlane-plugin-u3d.gemspec +31 -0
- data/fastlane-plugin-u3d/fastlane/Fastfile +3 -0
- data/fastlane-plugin-u3d/fastlane/Pluginfile +1 -0
- data/fastlane-plugin-u3d/lib/fastlane/plugin/u3d.rb +38 -0
- data/fastlane-plugin-u3d/lib/fastlane/plugin/u3d/actions/u3d_action.rb +80 -0
- data/fastlane-plugin-u3d/lib/fastlane/plugin/u3d/helper/u3d_helper.rb +34 -0
- data/fastlane-plugin-u3d/lib/fastlane/plugin/u3d/version.rb +27 -0
- data/fastlane-plugin-u3d/spec/spec_helper.rb +32 -0
- data/lib/u3d.rb +33 -0
- data/lib/u3d/cache.rb +120 -0
- data/lib/u3d/commands.rb +307 -0
- data/lib/u3d/commands_generator.rb +163 -0
- data/lib/u3d/downloader.rb +363 -0
- data/lib/u3d/iniparser.rb +83 -0
- data/lib/u3d/installer.rb +445 -0
- data/lib/u3d/log_analyzer.rb +221 -0
- data/lib/u3d/unity_version_number.rb +71 -0
- data/lib/u3d/unity_versions.rb +207 -0
- data/lib/u3d/utils.rb +121 -0
- data/lib/u3d/version.rb +31 -0
- data/lib/u3d_core.rb +30 -0
- data/lib/u3d_core/command_executor.rb +134 -0
- data/lib/u3d_core/command_runner.rb +93 -0
- data/lib/u3d_core/credentials.rb +116 -0
- data/lib/u3d_core/globals.rb +84 -0
- data/lib/u3d_core/helper.rb +149 -0
- data/lib/u3d_core/ui/disable_colors.rb +40 -0
- data/lib/u3d_core/ui/implementations/shell.rb +157 -0
- data/lib/u3d_core/ui/interface.rb +182 -0
- data/lib/u3d_core/ui/ui.rb +49 -0
- data/local_gem_install.sh +6 -0
- data/scripts/be +14 -0
- data/u3d.gemspec +41 -0
- metadata +388 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
## --- BEGIN LICENSE BLOCK ---
|
|
2
|
+
# Copyright (c) 2016-present WeWantToKnow AS
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
# copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
# SOFTWARE.
|
|
21
|
+
## --- END LICENSE BLOCK ---
|
|
22
|
+
|
|
23
|
+
require 'json'
|
|
24
|
+
|
|
25
|
+
module U3d
|
|
26
|
+
# Analyzes log by filtering output along a set of rules
|
|
27
|
+
class LogAnalyzer
|
|
28
|
+
RULES_PATH = File.expand_path('../../../config/log_rules.json', __FILE__)
|
|
29
|
+
MEMORY_SIZE = 10
|
|
30
|
+
|
|
31
|
+
def initialize
|
|
32
|
+
@lines_memory = Array.new(MEMORY_SIZE)
|
|
33
|
+
@active_phase = nil
|
|
34
|
+
@active_rule = nil
|
|
35
|
+
@context = {}
|
|
36
|
+
@rule_lines_buffer = []
|
|
37
|
+
@generic_rules, @phases = load_rules
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def load_rules
|
|
41
|
+
data = {}
|
|
42
|
+
generic_rules = {}
|
|
43
|
+
phases = {}
|
|
44
|
+
File.open(RULES_PATH, 'r') do |f|
|
|
45
|
+
data = JSON.parse(f.read)
|
|
46
|
+
end
|
|
47
|
+
if data['GENERAL'] && data['GENERAL']['active']
|
|
48
|
+
data['GENERAL']['rules'].each do |rn, r|
|
|
49
|
+
generic_rules[rn] = r if parse_rule(r)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
data.delete('GENERAL')
|
|
53
|
+
data.each do |name, phase|
|
|
54
|
+
# Phase parsing
|
|
55
|
+
next unless phase['active']
|
|
56
|
+
next if phase['phase_start_pattern'].nil?
|
|
57
|
+
phase['phase_start_pattern'] = Regexp.new phase['phase_start_pattern']
|
|
58
|
+
phase['phase_end_pattern'] = Regexp.new phase['phase_end_pattern'] if phase['phase_end_pattern']
|
|
59
|
+
phase.delete('comment')
|
|
60
|
+
# Rules parsing
|
|
61
|
+
temp_rules = {}
|
|
62
|
+
unless phase['silent'] == true
|
|
63
|
+
phase['rules'].each do |rn, r|
|
|
64
|
+
temp_rules[rn] = r if parse_rule(r)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
phase['rules'] = temp_rules
|
|
68
|
+
phases[name] = phase
|
|
69
|
+
end
|
|
70
|
+
return generic_rules, phases
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def parse_line(line)
|
|
74
|
+
# Insert new line and remove last stored line
|
|
75
|
+
@lines_memory.push(line).shift
|
|
76
|
+
|
|
77
|
+
# Check if phase is changing
|
|
78
|
+
@phases.each do |name, phase|
|
|
79
|
+
next if name == @active_phase
|
|
80
|
+
next unless line =~ phase['phase_start_pattern']
|
|
81
|
+
if @active_rule
|
|
82
|
+
# Active rule should be finished
|
|
83
|
+
# If it is still active during phase change, it means that something went wrong
|
|
84
|
+
UI.error("[#{@active_phase}] Could not finish active rule '#{@active_rule}'. Aborting it.")
|
|
85
|
+
@active_rule = nil
|
|
86
|
+
end
|
|
87
|
+
@active_phase = name
|
|
88
|
+
@context.clear
|
|
89
|
+
@rule_lines_buffer.clear
|
|
90
|
+
UI.verbose("--- Beginning #{name} phase ---")
|
|
91
|
+
break
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
apply_ruleset = lambda do |ruleset, header|
|
|
95
|
+
# Apply the active rule
|
|
96
|
+
if @active_rule && ruleset[@active_rule]
|
|
97
|
+
rule = ruleset[@active_rule]
|
|
98
|
+
pattern = rule['end_pattern']
|
|
99
|
+
|
|
100
|
+
# Is it the end of the rule?
|
|
101
|
+
if line =~ pattern
|
|
102
|
+
unless @rule_lines_buffer.empty?
|
|
103
|
+
@rule_lines_buffer.each do |l|
|
|
104
|
+
UI.send(rule['type'], "[#{header}] " + l)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
if rule['end_message'] != false
|
|
108
|
+
if rule['end_message']
|
|
109
|
+
match = line.match(pattern)
|
|
110
|
+
params = match.names.map(&:to_sym).zip(match.captures).to_h
|
|
111
|
+
message = rule['end_message'] % params.merge(@context)
|
|
112
|
+
else
|
|
113
|
+
message = line.chomp
|
|
114
|
+
end
|
|
115
|
+
message = "[#{header}] " + message
|
|
116
|
+
UI.send(rule['type'], message)
|
|
117
|
+
end
|
|
118
|
+
@active_rule = nil
|
|
119
|
+
@context.clear
|
|
120
|
+
@rule_lines_buffer.clear
|
|
121
|
+
|
|
122
|
+
# It's not the end of the rules, should the line be stored?
|
|
123
|
+
elsif rule['store_lines']
|
|
124
|
+
match = false
|
|
125
|
+
if rule['ignore_lines']
|
|
126
|
+
rule['ignore_lines'].each do |pat|
|
|
127
|
+
if line =~ pat
|
|
128
|
+
match = true
|
|
129
|
+
break
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
@rule_lines_buffer << line.chomp unless match
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# If there is no active rule, try to apply a new one
|
|
138
|
+
if @active_rule.nil?
|
|
139
|
+
ruleset.each do |rn, r|
|
|
140
|
+
pattern = r['start_pattern']
|
|
141
|
+
next unless line =~ pattern
|
|
142
|
+
@active_rule = rn if r['end_pattern']
|
|
143
|
+
match = line.match(pattern)
|
|
144
|
+
@context = match.names.map(&:to_sym).zip(match.captures).to_h
|
|
145
|
+
if r['fetch_line_at_index'] || r['fetch_first_line_not_matching']
|
|
146
|
+
if r['fetch_line_at_index']
|
|
147
|
+
fetched_line = @lines_memory.reverse[r['fetch_line_at_index']]
|
|
148
|
+
else
|
|
149
|
+
fetched_line = nil
|
|
150
|
+
@lines_memory.reverse.each do |l|
|
|
151
|
+
match = false
|
|
152
|
+
r['fetch_first_line_not_matching'].each do |pat|
|
|
153
|
+
next unless l =~ pat
|
|
154
|
+
match = true
|
|
155
|
+
break
|
|
156
|
+
end
|
|
157
|
+
next if match
|
|
158
|
+
fetched_line = l
|
|
159
|
+
break
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
if fetched_line
|
|
163
|
+
if r['fetched_line_pattern']
|
|
164
|
+
match = fetched_line.match(r['fetched_line_pattern'])
|
|
165
|
+
@context.merge!(match.names.map(&:to_sym).zip(match.captures).to_h)
|
|
166
|
+
end
|
|
167
|
+
if r['fetched_line_message'] != false
|
|
168
|
+
message = if r['fetched_line_message']
|
|
169
|
+
r['fetched_line_message'] % @context
|
|
170
|
+
else
|
|
171
|
+
fetched_line.chomp
|
|
172
|
+
end
|
|
173
|
+
message = "[#{header}] " + message
|
|
174
|
+
UI.send(r['type'], message)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
if r['start_message'] != false
|
|
179
|
+
message = if r['start_message']
|
|
180
|
+
r['start_message'] % @context
|
|
181
|
+
else
|
|
182
|
+
line.chomp
|
|
183
|
+
end
|
|
184
|
+
message = "[#{header}] " + message
|
|
185
|
+
UI.send(r['type'], message)
|
|
186
|
+
end
|
|
187
|
+
break
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
apply_ruleset.call(@phases[@active_phase]['rules'], @active_phase) if @active_phase
|
|
193
|
+
apply_ruleset.call(@generic_rules, 'GENERAL')
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
private
|
|
197
|
+
|
|
198
|
+
def parse_rule(r)
|
|
199
|
+
return false unless r['active']
|
|
200
|
+
return false if r['start_pattern'].nil?
|
|
201
|
+
r['start_pattern'] = Regexp.new r['start_pattern']
|
|
202
|
+
r['end_pattern'] = Regexp.new r['end_pattern'] if r['end_pattern']
|
|
203
|
+
if r['fetch_line_at_index']
|
|
204
|
+
r.delete('fetch_line_at_index') if r['fetch_line_at_index'] >= MEMORY_SIZE
|
|
205
|
+
r.delete('fetch_line_at_index') if r['fetch_line_at_index'] <= 0
|
|
206
|
+
elsif r['fetch_first_line_not_matching']
|
|
207
|
+
r['fetch_first_line_not_matching'].map! { |pat| Regexp.new pat }
|
|
208
|
+
end
|
|
209
|
+
if r['fetch_line_at_index'] || r['fetch_first_line_not_matching']
|
|
210
|
+
r['fetched_line_pattern'] = Regexp.new r['fetched_line_pattern'] if r['fetched_line_pattern']
|
|
211
|
+
end
|
|
212
|
+
r['type'] = 'important' if r['type'] == 'warning'
|
|
213
|
+
if r['type'] && r['type'] != 'error' && r['type'] != 'important' && r['type'] != 'success'
|
|
214
|
+
r['type'] = 'message'
|
|
215
|
+
end
|
|
216
|
+
r['type'] ||= 'message'
|
|
217
|
+
r['ignore_lines'].map! { |pat| Regexp.new pat } if r['ignore_lines']
|
|
218
|
+
true
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
## --- BEGIN LICENSE BLOCK ---
|
|
2
|
+
# Copyright (c) 2016-present WeWantToKnow AS
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
# copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
# SOFTWARE.
|
|
21
|
+
## --- END LICENSE BLOCK ---
|
|
22
|
+
|
|
23
|
+
require 'u3d/utils'
|
|
24
|
+
|
|
25
|
+
module U3d
|
|
26
|
+
class UnityVersionNumber
|
|
27
|
+
attr_reader :parts
|
|
28
|
+
|
|
29
|
+
def initialize(version)
|
|
30
|
+
parsed = Utils.parse_unity_version(version)
|
|
31
|
+
parsed.each_with_index do |val, index|
|
|
32
|
+
next if val.nil? || (index == 3)
|
|
33
|
+
parsed[index] = val.to_i
|
|
34
|
+
end
|
|
35
|
+
@parts = parsed
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def to_s
|
|
39
|
+
"#{parts[0]}.#{parts[1]}.#{parts[2]}#{parts[3]}#{parts[4]}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class UnityVersionComparator
|
|
44
|
+
include Comparable
|
|
45
|
+
|
|
46
|
+
RELEASE_LETTER_STRENGTH = { a: 1, b: 2, f: 3, p: 4 }.freeze
|
|
47
|
+
|
|
48
|
+
attr_reader :version
|
|
49
|
+
|
|
50
|
+
def <=>(other)
|
|
51
|
+
comp = @version.parts[0] <=> other.version.parts[0]
|
|
52
|
+
return comp if comp.nonzero?
|
|
53
|
+
comp = @version.parts[1] <=> other.version.parts[1]
|
|
54
|
+
return comp if comp.nonzero?
|
|
55
|
+
comp = @version.parts[2] <=> other.version.parts[2]
|
|
56
|
+
return comp if comp.nonzero?
|
|
57
|
+
comp = RELEASE_LETTER_STRENGTH[@version.parts[3].to_sym] <=> RELEASE_LETTER_STRENGTH[other.version.parts[3].to_sym]
|
|
58
|
+
return comp if comp.nonzero?
|
|
59
|
+
return @version.parts[4] <=> other.version.parts[4]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def initialize(version)
|
|
63
|
+
version = UnityVersionNumber.new(version.to_s)
|
|
64
|
+
@version = version
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def inspect
|
|
68
|
+
@version
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
## --- BEGIN LICENSE BLOCK ---
|
|
2
|
+
# Copyright (c) 2016-present WeWantToKnow AS
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
# copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
# SOFTWARE.
|
|
21
|
+
## --- END LICENSE BLOCK ---
|
|
22
|
+
|
|
23
|
+
require 'u3d/iniparser'
|
|
24
|
+
require 'u3d_core/helper'
|
|
25
|
+
require 'net/http'
|
|
26
|
+
|
|
27
|
+
module U3d
|
|
28
|
+
# Takes care of fectching versions and version list
|
|
29
|
+
module UnityVersions
|
|
30
|
+
#####################################################
|
|
31
|
+
# @!group URLS: Locations to fetch information from
|
|
32
|
+
#####################################################
|
|
33
|
+
# URL for the forum thread listing all the Linux releases
|
|
34
|
+
UNITY_LINUX_DOWNLOADS = 'https://forum.unity3d.com/threads/unity-on-linux-release-notes-and-known-issues.350256/'.freeze
|
|
35
|
+
# URL for the main releases for Windows and Macintosh
|
|
36
|
+
UNITY_DOWNLOADS = 'https://unity3d.com/get-unity/download/archive'.freeze
|
|
37
|
+
# URL for the patch releases for Windows and Macintosh
|
|
38
|
+
UNITY_PATCHES = 'https://unity3d.com/unity/qa/patch-releases'.freeze
|
|
39
|
+
# URL for the beta releases list, they need to be accessed after
|
|
40
|
+
UNITY_BETAS = 'https://unity3d.com/unity/beta/archive'.freeze
|
|
41
|
+
# URL for a specific beta, takes into parameter a version string (%s)
|
|
42
|
+
UNITY_BETA_URL = 'https://unity3d.com/unity/beta/unity%s'.freeze
|
|
43
|
+
|
|
44
|
+
#####################################################
|
|
45
|
+
# @!group REGEX: expressions to interpret data
|
|
46
|
+
#####################################################
|
|
47
|
+
# Captures a version and its base url
|
|
48
|
+
MAC_DOWNLOAD = %r{"(https?://[\w/\.-]+/[0-9a-f]{12}/)MacEditorInstaller/[a-zA-Z0-9/\.]+-(\d+\.\d+\.\d+\w\d+)\.?\w+"}
|
|
49
|
+
WIN_DOWNLOAD = %r{"(https?://[\w/\.-]+/[0-9a-f]{12}/)Windows..EditorInstaller/[a-zA-Z0-9/\.]+-(\d+\.\d+\.\d+\w\d+)\.?\w+"}
|
|
50
|
+
LINUX_DOWNLOAD = %r{"(https?://[\w/\._-]+/unity\-editor\-installer\-(\d+\.\d+\.\d+\w\d+).*\.sh)"}
|
|
51
|
+
# Captures a beta version in html page
|
|
52
|
+
UNITY_BETAVERSION_REGEX = %r{\/unity\/beta\/unity(\d+\.\d+\.\d+\w\d+)"}
|
|
53
|
+
UNITY_EXTRA_DOWNLOAD_REGEX = %r{"(https?:\/\/[\w\/.-]+\.unity3d\.com\/(\w+))\/[a-zA-Z\/.-]+\/download.html"}
|
|
54
|
+
|
|
55
|
+
class << self
|
|
56
|
+
def list_available(os: nil)
|
|
57
|
+
os ||= U3dCore::Helper.operating_system
|
|
58
|
+
|
|
59
|
+
case os
|
|
60
|
+
when :linux
|
|
61
|
+
return U3d::UnityVersions::LinuxVersions.list_available
|
|
62
|
+
when :mac
|
|
63
|
+
return U3d::UnityVersions::MacVersions.list_available
|
|
64
|
+
when :win
|
|
65
|
+
return U3d::UnityVersions::WindowsVersions.list_available
|
|
66
|
+
else
|
|
67
|
+
raise ArgumentError, "Operating system #{os} not supported"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def fetch_version(url, pattern)
|
|
72
|
+
hash = {}
|
|
73
|
+
data = Utils.get_ssl(url)
|
|
74
|
+
results = data.scan(pattern)
|
|
75
|
+
results.each { |capt| hash[capt[1]] = capt[0] }
|
|
76
|
+
return hash
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def fetch_betas(url, pattern)
|
|
80
|
+
hash = {}
|
|
81
|
+
data = Utils.get_ssl(url)
|
|
82
|
+
results = data.scan(UNITY_BETAVERSION_REGEX).uniq
|
|
83
|
+
results.each { |beta| hash.merge!(fetch_version(UNITY_BETA_URL % beta[0], pattern)) }
|
|
84
|
+
hash
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class LinuxVersions
|
|
89
|
+
class << self
|
|
90
|
+
def list_available
|
|
91
|
+
UI.message 'Loading Unity releases'
|
|
92
|
+
request = nil
|
|
93
|
+
response = nil
|
|
94
|
+
data = ''
|
|
95
|
+
uri = URI(UNITY_LINUX_DOWNLOADS)
|
|
96
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
|
|
97
|
+
request = Net::HTTP::Get.new uri
|
|
98
|
+
request['Connection'] = 'keep-alive'
|
|
99
|
+
response = http.request request
|
|
100
|
+
|
|
101
|
+
case response
|
|
102
|
+
when Net::HTTPSuccess then
|
|
103
|
+
# Successfully retrieved forum content
|
|
104
|
+
data = response.body
|
|
105
|
+
when Net::HTTPRedirection then
|
|
106
|
+
# A session must be opened with the server before accessing forum
|
|
107
|
+
res = nil
|
|
108
|
+
cookie_str = ''
|
|
109
|
+
# Store the name and value of the cookies returned by the server
|
|
110
|
+
response['set-cookie'].gsub(/\s+/, '').split(',').each do |c|
|
|
111
|
+
cookie_str << c.split(';', 2)[0] + '; '
|
|
112
|
+
end
|
|
113
|
+
cookie_str.chomp!('; ')
|
|
114
|
+
|
|
115
|
+
# It should be the Unity register API
|
|
116
|
+
uri = URI(response['location'])
|
|
117
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http_api|
|
|
118
|
+
request = Net::HTTP::Get.new uri
|
|
119
|
+
request['Connection'] = 'keep-alive'
|
|
120
|
+
res = http_api.request request
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
raise 'Unexpected result' unless res.is_a? Net::HTTPRedirection
|
|
124
|
+
# It should be a redirection to the forum to perform authentication
|
|
125
|
+
uri = URI(res['location'])
|
|
126
|
+
|
|
127
|
+
request = Net::HTTP::Get.new uri
|
|
128
|
+
request['Connection'] = 'keep-alive'
|
|
129
|
+
request['Cookie'] = cookie_str
|
|
130
|
+
|
|
131
|
+
res = http.request request
|
|
132
|
+
|
|
133
|
+
raise 'Unable to establish a session with Unity forum' unless res.is_a? Net::HTTPRedirection
|
|
134
|
+
|
|
135
|
+
cookie_str << '; ' + res['set-cookie'].gsub(/\s+/, '').split(';', 2)[0]
|
|
136
|
+
|
|
137
|
+
uri = URI(res['location'])
|
|
138
|
+
|
|
139
|
+
request = Net::HTTP::Get.new uri
|
|
140
|
+
request['Connection'] = 'keep-alive'
|
|
141
|
+
request['Cookie'] = cookie_str
|
|
142
|
+
|
|
143
|
+
res = http.request request
|
|
144
|
+
|
|
145
|
+
data = res.body if res.is_a? Net::HTTPSuccess
|
|
146
|
+
else raise "Request failed with status #{response.code}"
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
data.gsub(/[ \t]+/, '').each_line { |l| puts l if /<a href=/ =~ l }
|
|
150
|
+
versions = {}
|
|
151
|
+
results = data.scan(LINUX_DOWNLOAD)
|
|
152
|
+
results.each do |capt|
|
|
153
|
+
versions[capt[1]] = capt[0]
|
|
154
|
+
end
|
|
155
|
+
if versions.count.zero?
|
|
156
|
+
UI.important 'Found no releases'
|
|
157
|
+
else
|
|
158
|
+
UI.success "Found #{versions.count} releases."
|
|
159
|
+
end
|
|
160
|
+
versions
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
class MacVersions
|
|
166
|
+
class << self
|
|
167
|
+
def list_available
|
|
168
|
+
versions = {}
|
|
169
|
+
UI.message 'Loading Unity releases'
|
|
170
|
+
current = UnityVersions.fetch_version(UNITY_DOWNLOADS, MAC_DOWNLOAD)
|
|
171
|
+
UI.success "Found #{current.count} releases." if current.count.nonzero?
|
|
172
|
+
versions = versions.merge(current)
|
|
173
|
+
UI.message 'Loading Unity patch releases'
|
|
174
|
+
current = UnityVersions.fetch_version(UNITY_PATCHES, MAC_DOWNLOAD)
|
|
175
|
+
UI.success "Found #{current.count} patch releases." if current.count.nonzero?
|
|
176
|
+
versions = versions.merge(current)
|
|
177
|
+
UI.message 'Loading Unity beta releases'
|
|
178
|
+
current = UnityVersions.fetch_betas(UNITY_BETAS, MAC_DOWNLOAD)
|
|
179
|
+
UI.success "Found #{current.count} beta releases." if current.count.nonzero?
|
|
180
|
+
versions = versions.merge(current)
|
|
181
|
+
versions
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
class WindowsVersions
|
|
187
|
+
class << self
|
|
188
|
+
def list_available
|
|
189
|
+
versions = {}
|
|
190
|
+
UI.message 'Loading Unity releases'
|
|
191
|
+
current = UnityVersions.fetch_version(UNITY_DOWNLOADS, WIN_DOWNLOAD)
|
|
192
|
+
UI.success "Found #{current.count} releases." if current.count.nonzero?
|
|
193
|
+
versions = versions.merge(current)
|
|
194
|
+
UI.message 'Loading Unity patch releases'
|
|
195
|
+
current = UnityVersions.fetch_version(UNITY_PATCHES, WIN_DOWNLOAD)
|
|
196
|
+
UI.success "Found #{current.count} patch releases." if current.count.nonzero?
|
|
197
|
+
versions = versions.merge(current)
|
|
198
|
+
UI.message 'Loading Unity beta releases'
|
|
199
|
+
current = UnityVersions.fetch_betas(UNITY_BETAS, WIN_DOWNLOAD)
|
|
200
|
+
UI.success "Found #{current.count} beta releases." if current.count.nonzero?
|
|
201
|
+
versions = versions.merge(current)
|
|
202
|
+
versions
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|