gene_system 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.doxie.json +25 -0
- data/.github/workflows/build.yml +25 -0
- data/.gitignore +20 -0
- data/.rubocop.yml +20 -0
- data/.ruby-version +1 -0
- data/CONTRIBUTING.md +51 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +150 -0
- data/LICENSE +21 -0
- data/README.md +255 -0
- data/Rakefile +19 -0
- data/bin/genesystem +52 -0
- data/data/manifest.jsonnet +87 -0
- data/gene_system.gemspec +40 -0
- data/lib/gene_system.rb +9 -0
- data/lib/gene_system/cli.rb +76 -0
- data/lib/gene_system/cli/commands.rb +109 -0
- data/lib/gene_system/generators.rb +58 -0
- data/lib/gene_system/manifest.rb +127 -0
- data/lib/gene_system/platform.rb +55 -0
- data/lib/gene_system/step.rb +74 -0
- data/lib/gene_system/version.rb +3 -0
- data/spec/gene_system/cli/commands_spec.rb +230 -0
- data/spec/gene_system/cli_spec.rb +83 -0
- data/spec/gene_system/generators_spec.rb +65 -0
- data/spec/gene_system/manifest_spec.rb +239 -0
- data/spec/gene_system/platform_spec.rb +94 -0
- data/spec/gene_system/step_spec.rb +108 -0
- data/spec/spec_helper.rb +27 -0
- metadata +344 -0
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'private_gem/tasks'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'bump/tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
require 'rubocop/rake_task'
|
6
|
+
require 'rubycritic/rake_task'
|
7
|
+
require 'yard'
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
10
|
+
RuboCop::RakeTask.new
|
11
|
+
|
12
|
+
RubyCritic::RakeTask.new do |task|
|
13
|
+
task.paths = FileList['lib/**/*.rb'] - FileList['spec/**/*_spec.rb']
|
14
|
+
task.options = '--no-browser --path ./target/reports/critique'
|
15
|
+
end
|
16
|
+
|
17
|
+
YARD::Rake::YardocTask.new
|
18
|
+
|
19
|
+
task default: %i[spec rubocop rubycritic yard]
|
data/bin/genesystem
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(__dir__ + '/../lib')
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'optparse'
|
7
|
+
require 'highline'
|
8
|
+
|
9
|
+
require 'gene_system'
|
10
|
+
require 'gene_system/cli'
|
11
|
+
|
12
|
+
USAGE = "GeneSystem system configuration tool
|
13
|
+
|
14
|
+
Usage:\tgenesystem <command> <variables> <opts>
|
15
|
+
Options:
|
16
|
+
\s\s\s\s--trace\t\t trace mode on
|
17
|
+
-h, --help\t\t print usage
|
18
|
+
|
19
|
+
Commands:
|
20
|
+
new <rel>\t\t creates a new manifest
|
21
|
+
install <rel>\t\t applies install steps specified in manifest
|
22
|
+
remove <rel>\t\t applies remove steps specified in manifest
|
23
|
+
".freeze
|
24
|
+
|
25
|
+
@trace = false
|
26
|
+
@overwrite = false
|
27
|
+
|
28
|
+
OptionParser.new do |opts|
|
29
|
+
opts.banner = USAGE
|
30
|
+
opts.on('--trace') { @trace = true }
|
31
|
+
|
32
|
+
opts.on_tail('-h', '--help') do
|
33
|
+
GeneSystem::CLI.print_message_and_exit(USAGE)
|
34
|
+
end
|
35
|
+
end.parse!
|
36
|
+
|
37
|
+
begin
|
38
|
+
raise 'ERROR: no command provided' unless ARGV[0]
|
39
|
+
|
40
|
+
cmd = ARGV.shift
|
41
|
+
GeneSystem::CLI::Commands.public_send(
|
42
|
+
cmd.to_sym,
|
43
|
+
ARGV
|
44
|
+
)
|
45
|
+
rescue StandardError => e
|
46
|
+
GeneSystem::CLI.print_message(e)
|
47
|
+
GeneSystem::CLI.print_message(e.backtrace) if @trace
|
48
|
+
GeneSystem::CLI.print_message("\n")
|
49
|
+
GeneSystem::CLI.print_message_and_exit(USAGE)
|
50
|
+
end
|
51
|
+
|
52
|
+
exit(0)
|
@@ -0,0 +1,87 @@
|
|
1
|
+
local conf = {
|
2
|
+
variables: {
|
3
|
+
apps: [
|
4
|
+
{
|
5
|
+
"name": "virtualbox",
|
6
|
+
"version": "6.0.1",
|
7
|
+
},
|
8
|
+
{
|
9
|
+
"name": "vagrant",
|
10
|
+
"version": "9.0.2",
|
11
|
+
},
|
12
|
+
],
|
13
|
+
|
14
|
+
bins: [
|
15
|
+
"libc",
|
16
|
+
"libd",
|
17
|
+
"libe"
|
18
|
+
]
|
19
|
+
}
|
20
|
+
};
|
21
|
+
|
22
|
+
{
|
23
|
+
"name": "macos_ruby_dev",
|
24
|
+
"version": "0.1.1",
|
25
|
+
"platform": "macos",
|
26
|
+
"metadata": {
|
27
|
+
"gene_system": {
|
28
|
+
"version": "0.1.0"
|
29
|
+
},
|
30
|
+
},
|
31
|
+
"steps": [
|
32
|
+
// install apps
|
33
|
+
{
|
34
|
+
"name": "install " + app.name,
|
35
|
+
"exe": {
|
36
|
+
"install": {
|
37
|
+
"skip": "which yolo",
|
38
|
+
"cmd": [
|
39
|
+
"echo install " + app.name + app.version
|
40
|
+
],
|
41
|
+
},
|
42
|
+
"remove": {
|
43
|
+
"cmd": [
|
44
|
+
"echo remove " + app.name + app.version
|
45
|
+
],
|
46
|
+
},
|
47
|
+
},
|
48
|
+
"tags": "app " + app.name
|
49
|
+
} for app in conf.variables.apps
|
50
|
+
] + [
|
51
|
+
// install bins
|
52
|
+
{
|
53
|
+
"name": "install " + bin,
|
54
|
+
"exe": {
|
55
|
+
"install": {
|
56
|
+
"cmd": [
|
57
|
+
"echo install " + bin
|
58
|
+
],
|
59
|
+
},
|
60
|
+
"remove": {
|
61
|
+
"cmd": [
|
62
|
+
"echo remove " + bin
|
63
|
+
],
|
64
|
+
},
|
65
|
+
},
|
66
|
+
"tags": "app " + bin
|
67
|
+
} for bin in conf.variables.bins
|
68
|
+
] + [
|
69
|
+
{
|
70
|
+
"name": "ask for input",
|
71
|
+
"exe": {
|
72
|
+
"install": {
|
73
|
+
"prompts": [
|
74
|
+
{
|
75
|
+
"prompt": "Please enter your name",
|
76
|
+
"var": "name",
|
77
|
+
},
|
78
|
+
],
|
79
|
+
"cmd": [
|
80
|
+
"echo 'Hello {{name}}'"
|
81
|
+
],
|
82
|
+
},
|
83
|
+
},
|
84
|
+
"tags": "input"
|
85
|
+
},
|
86
|
+
],
|
87
|
+
}
|
data/gene_system.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'gene_system/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'gene_system'
|
9
|
+
spec.version = GeneSystem::VERSION
|
10
|
+
spec.authors = ['Andrew Bigger']
|
11
|
+
spec.email = ['andrew.bigger@gmail.com']
|
12
|
+
spec.summary = 'System configuration tool for applying personal settings to a new machine.'
|
13
|
+
spec.homepage = 'https://github.com/andrewbigger/gene_system'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'hashie'
|
22
|
+
spec.add_dependency 'highline', '~> 2.0'
|
23
|
+
spec.add_dependency 'jsonnet'
|
24
|
+
spec.add_dependency 'logger', '~> 1.4'
|
25
|
+
spec.add_dependency 'os'
|
26
|
+
spec.add_dependency 'ruby-handlebars', '~> 0.4'
|
27
|
+
|
28
|
+
spec.add_development_dependency 'bump', '~> 0.6'
|
29
|
+
spec.add_development_dependency 'byebug', '~> 11.0'
|
30
|
+
spec.add_development_dependency 'doxie'
|
31
|
+
spec.add_development_dependency 'private_gem', '~> 1.1'
|
32
|
+
spec.add_development_dependency 'pry', '~> 0.11'
|
33
|
+
spec.add_development_dependency 'puma', '~> 4.3'
|
34
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
35
|
+
spec.add_development_dependency 'rspec', '~> 3.7', '>= 3.7.0'
|
36
|
+
spec.add_development_dependency 'rubocop', '~> 0.58'
|
37
|
+
spec.add_development_dependency 'rubycritic', '~> 3.4', '>= 3.4.0'
|
38
|
+
spec.add_development_dependency 'simplecov', '~> 0.16'
|
39
|
+
spec.add_development_dependency 'yard', '~> 0.9'
|
40
|
+
end
|
data/lib/gene_system.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'gene_system/cli/commands'
|
2
|
+
|
3
|
+
require 'hashie'
|
4
|
+
|
5
|
+
module GeneSystem
|
6
|
+
# Command line interface helpers and actions
|
7
|
+
module CLI
|
8
|
+
# CLI Helpers
|
9
|
+
class <<self
|
10
|
+
##
|
11
|
+
# Creates logger for printing messages
|
12
|
+
#
|
13
|
+
# Sets the formatter to output only the provided message to the
|
14
|
+
# specified IO
|
15
|
+
#
|
16
|
+
# @param [IO] out - default: STDOUT
|
17
|
+
#
|
18
|
+
# @return [Logger]
|
19
|
+
#
|
20
|
+
def logger(out = STDOUT)
|
21
|
+
@logger ||= Logger.new(out)
|
22
|
+
@logger.formatter = proc do |_sev, _time, _prog, msg|
|
23
|
+
"#{msg}\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
@logger
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Prints command line message to STDOUT
|
31
|
+
#
|
32
|
+
# For use when printing info messages for a user to STDOUT
|
33
|
+
#
|
34
|
+
# @param [String] message - message to be printed
|
35
|
+
#
|
36
|
+
def print_message(message)
|
37
|
+
logger.info(message)
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Prints warning to STDOUT
|
42
|
+
#
|
43
|
+
# For use when printing warn messages to STDOUT
|
44
|
+
#
|
45
|
+
# @param [String] message - message to be printed
|
46
|
+
#
|
47
|
+
def print_warning(message)
|
48
|
+
logger.warn(message)
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Prints error to STDERR
|
53
|
+
#
|
54
|
+
# For indicating fatal message to user
|
55
|
+
#
|
56
|
+
# @param [String] message - message to be printed
|
57
|
+
#
|
58
|
+
def print_error(message)
|
59
|
+
logger(STDERR).error(message)
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Prints a message and then exits with given status code
|
64
|
+
#
|
65
|
+
# This will terminate the program with the given status code
|
66
|
+
#
|
67
|
+
# @param [String] message - message to be printed
|
68
|
+
# @param [Integer] exit_code - exit status (default: 1)
|
69
|
+
#
|
70
|
+
def print_message_and_exit(message, exit_code = 1)
|
71
|
+
print_message(message)
|
72
|
+
exit(exit_code)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'highline'
|
2
|
+
|
3
|
+
module GeneSystem
|
4
|
+
module CLI
|
5
|
+
# CLI Actions
|
6
|
+
module Commands
|
7
|
+
class <<self
|
8
|
+
##
|
9
|
+
# Creates a new, blank manifest with at the specified
|
10
|
+
# destination with the given name
|
11
|
+
#
|
12
|
+
# It is expected that the first argument provided to the
|
13
|
+
# command will be the name of the manifest. If this is not
|
14
|
+
# provided then a RuntimeError will be raised.
|
15
|
+
#
|
16
|
+
# @param dest [String]
|
17
|
+
# @param args [Array]
|
18
|
+
#
|
19
|
+
def new(args = [])
|
20
|
+
manifest_name = args.shift
|
21
|
+
|
22
|
+
raise 'no manifest name provided' unless manifest_name
|
23
|
+
|
24
|
+
GeneSystem::Generators.render_empty_manifest(
|
25
|
+
manifest_name,
|
26
|
+
Dir.pwd
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Applies install instructions from a manifest to the host system
|
32
|
+
#
|
33
|
+
# @param args [Array]
|
34
|
+
#
|
35
|
+
def install(args = [])
|
36
|
+
manifest = load_manifest(args)
|
37
|
+
platform = GeneSystem::Platform.new
|
38
|
+
|
39
|
+
manifest.steps.each do |step|
|
40
|
+
next if skip?(step, platform)
|
41
|
+
|
42
|
+
vars = ask(step.install.prompts)
|
43
|
+
platform.execute_commands(step.install.cmd, vars)
|
44
|
+
end
|
45
|
+
|
46
|
+
GeneSystem::CLI.print_message("\nmanifest successfully installed")
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Asks for user input when given prompts
|
51
|
+
#
|
52
|
+
# @param prompts [Array]
|
53
|
+
#
|
54
|
+
# @return Hashie::Mash
|
55
|
+
#
|
56
|
+
def ask(prompts = [])
|
57
|
+
answers = Hashie::Mash.new
|
58
|
+
return answers if prompts.nil?
|
59
|
+
|
60
|
+
cli = HighLine.new
|
61
|
+
|
62
|
+
prompts.each do |prompt|
|
63
|
+
resp = cli.ask(prompt.prompt)
|
64
|
+
answers[prompt.var] = resp
|
65
|
+
end
|
66
|
+
|
67
|
+
answers
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Applies remove instructions from a manifest to the host system
|
72
|
+
#
|
73
|
+
# @param args [Array]
|
74
|
+
#
|
75
|
+
def remove(args = [])
|
76
|
+
manifest = load_manifest(args)
|
77
|
+
platform = GeneSystem::Platform.new
|
78
|
+
|
79
|
+
manifest.steps.each do |step|
|
80
|
+
platform.execute_commands(step.remove.cmd)
|
81
|
+
end
|
82
|
+
|
83
|
+
GeneSystem::CLI.print_message("\nmanifest successfully removed")
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def load_manifest(args)
|
89
|
+
manifest_rel = args.shift
|
90
|
+
raise 'no manifest path provided' unless manifest_rel
|
91
|
+
|
92
|
+
manifest_path = File.join(Dir.pwd, manifest_rel)
|
93
|
+
|
94
|
+
unless File.exist?(manifest_path)
|
95
|
+
raise "cannot find manifest at #{manifest_path}"
|
96
|
+
end
|
97
|
+
|
98
|
+
GeneSystem::Manifest.new_from_file(manifest_path)
|
99
|
+
end
|
100
|
+
|
101
|
+
def skip?(step, platform)
|
102
|
+
return false if step.install.skip.nil?
|
103
|
+
|
104
|
+
platform.execute_command(step.install.skip).zero?
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module GeneSystem
|
4
|
+
# Generators create empty projects for the gene_system gem
|
5
|
+
module Generators
|
6
|
+
# Default sample step
|
7
|
+
DEFAULT_STEP = {
|
8
|
+
'name' => 'say hello',
|
9
|
+
'exe' => {
|
10
|
+
'install' => {
|
11
|
+
'cmd' => [
|
12
|
+
'echo hello'
|
13
|
+
]
|
14
|
+
},
|
15
|
+
'remove' => {
|
16
|
+
'cmd' => [
|
17
|
+
'echo goodbye'
|
18
|
+
]
|
19
|
+
}
|
20
|
+
},
|
21
|
+
'tags' => 'example step'
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
# Function that creates a named, empty manifest
|
25
|
+
TEMPLATE_MANIFEST = lambda do |name|
|
26
|
+
{
|
27
|
+
'name' => name,
|
28
|
+
'version' => '0.0.1',
|
29
|
+
'metadata' => {
|
30
|
+
'gene_system' => {
|
31
|
+
'version' => GeneSystem::VERSION
|
32
|
+
}
|
33
|
+
},
|
34
|
+
'steps' => [
|
35
|
+
DEFAULT_STEP
|
36
|
+
]
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
class <<self
|
41
|
+
##
|
42
|
+
# Renders empty manifest at specified path
|
43
|
+
#
|
44
|
+
# @param [String] name
|
45
|
+
# @param [String] path
|
46
|
+
#
|
47
|
+
def render_empty_manifest(name, path)
|
48
|
+
manifest_path = File.join(path, "#{name}.json")
|
49
|
+
|
50
|
+
File.open(manifest_path, 'w') do |f|
|
51
|
+
f.write(
|
52
|
+
TEMPLATE_MANIFEST.call(name).to_json
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|