kmc 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +22 -0
- data/README.md +141 -0
- data/Rakefile +2 -0
- data/bin/kmc +20 -0
- data/kmc.gemspec +26 -0
- data/lib/kmc.rb +27 -0
- data/lib/kmc/configuration.rb +67 -0
- data/lib/kmc/download_url.rb +80 -0
- data/lib/kmc/git_adapter.rb +71 -0
- data/lib/kmc/package.rb +142 -0
- data/lib/kmc/package_attrs.rb +69 -0
- data/lib/kmc/package_downloads.rb +122 -0
- data/lib/kmc/package_dsl.rb +13 -0
- data/lib/kmc/package_utils.rb +29 -0
- data/lib/kmc/packages/lack_luster_labs.rb +22 -0
- data/lib/kmc/page_fetcher.js +47 -0
- data/lib/kmc/post_processors/module_manager_resolver.rb +32 -0
- data/lib/kmc/refresher.rb +36 -0
- data/lib/kmc/user_interface.rb +195 -0
- data/lib/kmc/util.rb +11 -0
- data/lib/kmc/version.rb +3 -0
- data/lib/kmc/versioner.rb +70 -0
- data/lib/kmc/web/app.rb +42 -0
- data/lib/kmc/web/public/css/main.css +69 -0
- data/lib/kmc/web/public/css/normalize.css +527 -0
- data/lib/kmc/web/public/index.html +107 -0
- data/lib/kmc/web/public/js/main.js +125 -0
- data/lib/kmc/web/public/js/plugins.js +24 -0
- data/lib/kmc/web/public/js/vendor/jquery-1.10.2.min.js +6 -0
- data/lib/kmc/web/public/js/vendor/modernizr-2.6.2.min.js +4 -0
- data/lib/kmc/web/public/js/vendor/underscore.js +5 -0
- data/package_generator.rb +197 -0
- data/spec/download_url_spec.rb +69 -0
- data/spec/fixtures/example_box.html +165 -0
- data/spec/fixtures/example_dropbox.com.html +114 -0
- data/spec/fixtures/example_mediafire.html +101 -0
- data/spec/package_spec.rb +113 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/versioner_spec.rb +69 -0
- metadata +177 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
class LackLusterLabs < Kosmos::Package
|
2
|
+
title 'Lack Luster Labs'
|
3
|
+
aliases 'LLL' 'Lack Luster Labs Pack'
|
4
|
+
|
5
|
+
url 'https://www.dropbox.com/s/0h82wo8rdxu4jya/LLL-12-2.zip'
|
6
|
+
|
7
|
+
def install
|
8
|
+
merge_directory 'GameData/LLL', into: 'GameData'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class StockExtension < Kosmos::Package
|
13
|
+
title 'Stock eXTension'
|
14
|
+
aliases 'SXT' 'LLL Stock eXTension'
|
15
|
+
|
16
|
+
url 'https://dl.dropboxusercontent.com/u/39086055/SXT-16.5.zip'
|
17
|
+
|
18
|
+
def install
|
19
|
+
merge_directory 'GameData/SXT', into: 'GameData'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
/**
|
2
|
+
* A PhantomJS script to help find where to download a package's zipfile from.
|
3
|
+
* Execute it is:
|
4
|
+
*
|
5
|
+
* phantomjs page_fetcher.js <url>
|
6
|
+
*
|
7
|
+
* This script will "return" a value by sending it to STDOUT.
|
8
|
+
*
|
9
|
+
* There are two possible outputted values for this script.
|
10
|
+
*
|
11
|
+
* 1. If an external resource is received, and this external resource is a zip
|
12
|
+
* file (as determined from the HTTP contentType header), then the URL to
|
13
|
+
* that resource is returned.
|
14
|
+
* 2. If no external resource is found, then the HTML of the url provided,
|
15
|
+
* after all Javascript has been loaded, is outputted and another script
|
16
|
+
* must take care of determining where the URL of interest is found.
|
17
|
+
*/
|
18
|
+
|
19
|
+
var system = require('system');
|
20
|
+
var page = require('webpage').create();
|
21
|
+
|
22
|
+
var target = system.args[1];
|
23
|
+
|
24
|
+
var externalResourceProvidedUrl = false;
|
25
|
+
|
26
|
+
page.onResourceReceived = function(resource) {
|
27
|
+
if (resource.contentType === 'application/x-zip-compressed' &&
|
28
|
+
!externalResourceProvidedUrl) {
|
29
|
+
externalResourceProvidedUrl = true;
|
30
|
+
|
31
|
+
console.log(resource.url);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
page.open(target, function() {
|
36
|
+
// If this is set to true, then an external resource already provided the
|
37
|
+
// desired URL, and nothing more needs to be done -- the URL has already been
|
38
|
+
// sent to STDOUT.
|
39
|
+
//
|
40
|
+
// If done is still false, then the page itself contains the desired URL, so
|
41
|
+
// we output the HTML and let Ruby-side KMC determine the target URL.
|
42
|
+
if (!externalResourceProvidedUrl) {
|
43
|
+
console.log(page.content);
|
44
|
+
}
|
45
|
+
|
46
|
+
phantom.exit();
|
47
|
+
});
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Kmc
|
2
|
+
module PostProcessors
|
3
|
+
class ModuleManagerResolver
|
4
|
+
def self.post_process(ksp_path)
|
5
|
+
module_managers = Dir.chdir(ksp_path) do
|
6
|
+
Dir["GameData/*"].select do |file|
|
7
|
+
File.basename(file).start_with?('ModuleManager')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
most_recent_manager = module_managers.max_by do |file|
|
12
|
+
# Converts a string like this:
|
13
|
+
#
|
14
|
+
# ModuleManager.5.2.3
|
15
|
+
#
|
16
|
+
# Into this:
|
17
|
+
#
|
18
|
+
# [5, 2, 3]
|
19
|
+
File.basename(file).scan(/\d+/).map(&:to_i)
|
20
|
+
end
|
21
|
+
|
22
|
+
(module_managers - [most_recent_manager]).each do |file|
|
23
|
+
file_name = File.basename(file)
|
24
|
+
Util.log "Detected and deleting outdated version of ModuleManager: " +
|
25
|
+
"#{file_name}"
|
26
|
+
|
27
|
+
File.delete(File.join(ksp_path, file))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Kmc
|
2
|
+
module Refresher
|
3
|
+
class << self
|
4
|
+
# Fetch packages from online and delete the old packages, if any.
|
5
|
+
def update_packages!
|
6
|
+
new_packages_dir = File.join(fetch_packages,
|
7
|
+
'packages-master', 'packages')
|
8
|
+
|
9
|
+
kmc_packages_path = Kmc::Configuration.packages_path
|
10
|
+
output_path = File.join(kmc_packages_path, '..')
|
11
|
+
|
12
|
+
remove_old_packages(kmc_packages_path)
|
13
|
+
FileUtils.cp_r(new_packages_dir, output_path)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def fetch_packages
|
19
|
+
zipped_packages = HTTParty.get(Kmc.config.packages_url)
|
20
|
+
tmp_zip = PackageDownloads.download_to_tempdir(
|
21
|
+
'downloaded_packages.zip', zipped_packages)
|
22
|
+
output_path = Pathname.new(tmp_zip.path).parent.to_s
|
23
|
+
|
24
|
+
PackageDownloads.unzip_file(tmp_zip.path, output_path)
|
25
|
+
|
26
|
+
output_path
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_old_packages(kmc_packages_path)
|
30
|
+
Dir["#{kmc_packages_path}/*.rb"].each do |file|
|
31
|
+
File.delete(file)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
class String
|
2
|
+
def undent
|
3
|
+
gsub(/^.{#{slice(/^ +/).length}}/, '')
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
module Kmc
|
8
|
+
module UserInterface
|
9
|
+
class << self
|
10
|
+
def init(args)
|
11
|
+
ksp_path = extract_ksp_path_from_args(args)
|
12
|
+
|
13
|
+
unless ksp_path
|
14
|
+
Util.log <<-EOS.undent
|
15
|
+
Error: You did not specify what folder you keep KSP in.
|
16
|
+
|
17
|
+
Before doing anything else, please execute the command:
|
18
|
+
|
19
|
+
kmc init ksp-folder
|
20
|
+
|
21
|
+
Where "ksp-folder" is the name of the folder where you keep KSP.
|
22
|
+
EOS
|
23
|
+
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
Util.log "Initializing KMC into #{ksp_path} (This will take a sec) ..."
|
28
|
+
|
29
|
+
Kmc::Versioner.init_repo(ksp_path)
|
30
|
+
Kmc::Configuration.save_ksp_path(ksp_path)
|
31
|
+
|
32
|
+
Util.log <<-EOS.undent
|
33
|
+
Done! You're ready to begin installing mods.
|
34
|
+
|
35
|
+
Install your first mod by running the command:
|
36
|
+
|
37
|
+
kmc install [name-of-the-mod]
|
38
|
+
EOS
|
39
|
+
end
|
40
|
+
|
41
|
+
def install(args)
|
42
|
+
return unless check_initialized!
|
43
|
+
|
44
|
+
ksp_path = Kmc::Configuration.load_ksp_path
|
45
|
+
|
46
|
+
packages = load_packages(args)
|
47
|
+
return unless packages
|
48
|
+
|
49
|
+
return unless check_installed_packages(ksp_path, packages)
|
50
|
+
|
51
|
+
Util.log "KMC is about to install #{packages.count} package(s):"
|
52
|
+
pretty_print_list(packages.map(&:title))
|
53
|
+
|
54
|
+
Package.install_packages!(ksp_path, packages)
|
55
|
+
end
|
56
|
+
|
57
|
+
def uninstall(args)
|
58
|
+
return unless check_initialized!
|
59
|
+
|
60
|
+
ksp_path = Kmc::Configuration.load_ksp_path
|
61
|
+
|
62
|
+
package_name = args.shift
|
63
|
+
unless package_name
|
64
|
+
Util.log <<-EOS.undent
|
65
|
+
Error: You need to specify what package to uninstall. Example:
|
66
|
+
kmc uninstall name-of-the-mod"
|
67
|
+
EOS
|
68
|
+
|
69
|
+
return
|
70
|
+
end
|
71
|
+
|
72
|
+
package = Kmc::Package.find(package_name)
|
73
|
+
installed_packages = Kmc::Versioner.installed_packages(ksp_path)
|
74
|
+
|
75
|
+
if !package
|
76
|
+
Util.log "Error: KMC couldn't find any packages with the name #{package_name.inspect}."
|
77
|
+
elsif !installed_packages.include?(package.title)
|
78
|
+
Util.log <<-EOS.undent
|
79
|
+
Error: #{package.title} is not currently installed.
|
80
|
+
|
81
|
+
There are three reasons you may get this error:
|
82
|
+
|
83
|
+
1. You have already uninstalled #{package.title}.
|
84
|
+
2. You have never previously installed #{package.title}.
|
85
|
+
3. You did not use KMC to install #{package.title}.
|
86
|
+
EOS
|
87
|
+
else
|
88
|
+
Util.log "Preparing to uninstall #{package.title} ..."
|
89
|
+
Kmc::Versioner.uninstall_package(ksp_path, package)
|
90
|
+
Util.log "Done! Just uninstalled: #{package.title}."
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def list(args)
|
95
|
+
return unless check_initialized!
|
96
|
+
|
97
|
+
ksp_path = Kmc::Configuration.load_ksp_path
|
98
|
+
|
99
|
+
packages = Kmc::Versioner.installed_packages(ksp_path)
|
100
|
+
Util.log "You have installed #{packages.length} mod(s) using KMC:"
|
101
|
+
pretty_print_list(packages.map(&:title))
|
102
|
+
end
|
103
|
+
|
104
|
+
def refresh(args)
|
105
|
+
Util.log "Getting the most up-to-date packages for KMC ..."
|
106
|
+
Kmc::Refresher.update_packages!
|
107
|
+
Util.log "Done. The KMC packages you have are all up-to-date."
|
108
|
+
end
|
109
|
+
|
110
|
+
def server(args)
|
111
|
+
Web::App.start!
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
# Extracts the path to init to from the user-supplied arguments. If the
|
117
|
+
# user passed arguments via the commandline, then spaces in paths are
|
118
|
+
# auto-handled beacuse they come from ARGV.
|
119
|
+
#
|
120
|
+
# If, however, the user passed arguments from the server, then these
|
121
|
+
# arguments will still be separated by spaces. This method will then re-
|
122
|
+
# join them.
|
123
|
+
def extract_ksp_path_from_args(args)
|
124
|
+
if args
|
125
|
+
path = args.join(' ')
|
126
|
+
|
127
|
+
# Trim leading quotation mark if any
|
128
|
+
path = path[1..-1] if path.start_with?('"')
|
129
|
+
path = path[0..-2] if path.end_with?('"')
|
130
|
+
|
131
|
+
if path.empty?
|
132
|
+
nil
|
133
|
+
else
|
134
|
+
path
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def check_initialized!
|
140
|
+
if Kmc::Configuration.load_ksp_path
|
141
|
+
true
|
142
|
+
else
|
143
|
+
Util.log <<-EOS.undent
|
144
|
+
Error: You have not yet initialized KMC.
|
145
|
+
|
146
|
+
Before doing anything else, please execute the command:
|
147
|
+
|
148
|
+
kmc init ksp-folder
|
149
|
+
|
150
|
+
Where "ksp-folder" is the name of the folder where you keep KSP.
|
151
|
+
EOS
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def pretty_print_list(list)
|
156
|
+
list.each { |value| Util.log " * #{value}" }
|
157
|
+
end
|
158
|
+
|
159
|
+
def load_packages(package_names)
|
160
|
+
packages = Hash[package_names.map do |name|
|
161
|
+
[name, Kmc::Package.find(name)]
|
162
|
+
end]
|
163
|
+
|
164
|
+
unknown_packages = packages.select { |_, package| package.nil? }
|
165
|
+
package_suggestions = unknown_packages.map do |name, _|
|
166
|
+
best_guess = Kmc::Package.search(name).normalized_title
|
167
|
+
"#{name} (Maybe you meant: #{best_guess.inspect}?)"
|
168
|
+
end
|
169
|
+
|
170
|
+
if unknown_packages.any?
|
171
|
+
Util.log "Error: KMC couldn't find any packages with the following names:"
|
172
|
+
pretty_print_list(package_suggestions)
|
173
|
+
|
174
|
+
return false
|
175
|
+
end
|
176
|
+
|
177
|
+
packages.values
|
178
|
+
end
|
179
|
+
|
180
|
+
def check_installed_packages(ksp_path, new_packages)
|
181
|
+
installed_packages = Kmc::Versioner.installed_packages(ksp_path)
|
182
|
+
already_installed = new_packages & installed_packages
|
183
|
+
|
184
|
+
if already_installed.any?
|
185
|
+
Util.log "Error: You have already installed the following packages using KMC:"
|
186
|
+
pretty_print_list(already_installed.map(&:title))
|
187
|
+
|
188
|
+
false
|
189
|
+
else
|
190
|
+
true
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
data/lib/kmc/util.rb
ADDED
data/lib/kmc/version.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module Kmc
|
2
|
+
class Versioner
|
3
|
+
class << self
|
4
|
+
INIT_COMMIT_MESSAGE = "INIT: Initialize KMC"
|
5
|
+
|
6
|
+
def init_repo(path)
|
7
|
+
GitAdapter.init_repo(path)
|
8
|
+
GitAdapter.commit_everything(path, INIT_COMMIT_MESSAGE)
|
9
|
+
end
|
10
|
+
|
11
|
+
def mark_preinstall(path, package)
|
12
|
+
GitAdapter.commit_everything(path, pre_install_message(package))
|
13
|
+
end
|
14
|
+
|
15
|
+
def mark_postinstall(path, package)
|
16
|
+
GitAdapter.commit_everything(path, post_install_message(package))
|
17
|
+
end
|
18
|
+
|
19
|
+
def installed_packages(path)
|
20
|
+
commits = GitAdapter.list_commits(path)
|
21
|
+
|
22
|
+
postinstalls = commits.select(&:post?).map(&:subject)
|
23
|
+
uninstalls = commits.select(&:uninstall?).map(&:subject)
|
24
|
+
|
25
|
+
# The packages that have actually been installed are those that have a
|
26
|
+
# 'post-install' without any corresponding 'uninstall'. We can find
|
27
|
+
# these packages by getting post-installs, and removing the first
|
28
|
+
# instance of each package also found in uninstall.
|
29
|
+
#
|
30
|
+
# Note that we can't use Array#delete because that method will delete
|
31
|
+
# *all* instances of a package, which won't work because it's possible
|
32
|
+
# for a user to install, uninstall, then re-install a package.
|
33
|
+
uninstalls.each do |package|
|
34
|
+
postinstalls.delete_at(postinstalls.index(package))
|
35
|
+
end
|
36
|
+
|
37
|
+
postinstalls.map { |package_title| Package.find(package_title) }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Has this package already been installed?
|
41
|
+
def already_installed?(path, package)
|
42
|
+
installed_packages(path).include?(package)
|
43
|
+
end
|
44
|
+
|
45
|
+
def uninstall_package(path, package)
|
46
|
+
to_revert = GitAdapter.list_commits(path).find do |commit|
|
47
|
+
commit.post? && commit.subject == package.title
|
48
|
+
end
|
49
|
+
|
50
|
+
GitAdapter.revert_commit(path, to_revert)
|
51
|
+
Util.run_post_processors!(path)
|
52
|
+
GitAdapter.commit_everything(path, uninstall_message(package))
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def pre_install_message(package)
|
58
|
+
"PRE: #{package.title}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def post_install_message(package)
|
62
|
+
"POST: #{package.title}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def uninstall_message(package)
|
66
|
+
"UNINSTALL: #{package.title}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/kmc/web/app.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
|
3
|
+
module Kmc
|
4
|
+
module Web
|
5
|
+
class App < Sinatra::Application
|
6
|
+
set server: 'thin', connection: nil
|
7
|
+
|
8
|
+
get '/' do
|
9
|
+
send_file(File.join(settings.public_folder, 'index.html'))
|
10
|
+
end
|
11
|
+
|
12
|
+
get '/stream', provides: 'text/event-stream' do
|
13
|
+
stream :keep_open do |out|
|
14
|
+
settings.connection = out
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
post '/' do
|
19
|
+
Kmc.configure do |config|
|
20
|
+
config.output_method = Proc.new do |str|
|
21
|
+
# Send to STDOUT
|
22
|
+
puts str
|
23
|
+
|
24
|
+
# And to the in-browser UI
|
25
|
+
str.split("\n").each do |line|
|
26
|
+
settings.connection << "data: #{line}\n\n"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
kmc_params = params[:params].split(' ')
|
32
|
+
kmc_command = %w(init install uninstall list).find do |command|
|
33
|
+
command == params[:command]
|
34
|
+
end
|
35
|
+
|
36
|
+
UserInterface.send(kmc_command, kmc_params)
|
37
|
+
|
38
|
+
204
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|