kmc 0.0.6
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 +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
|