shred 0.0.1
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 +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/bin/shred +7 -0
- data/lib/shred/commands/app.rb +12 -0
- data/lib/shred/commands/base.rb +176 -0
- data/lib/shred/commands/db.rb +28 -0
- data/lib/shred/commands/dotenv.rb +68 -0
- data/lib/shred/commands/platform_deps.rb +133 -0
- data/lib/shred/commands/ruby_deps.rb +16 -0
- data/lib/shred/commands/services.rb +135 -0
- data/lib/shred/commands/test.rb +23 -0
- data/lib/shred/version.rb +3 -0
- data/lib/shred.rb +70 -0
- data/shred.gemspec +25 -0
- data/shred.yml +62 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5082b6bab04c4cf6a6b309058a72abe3eb781e89
|
4
|
+
data.tar.gz: 5d3da579a0e632bf8f0a13b5cb29f85821de8e01
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3d974d78ddce385509d35ca9c93f17ca4ec699a2f7d984651d24229185b9e4bf57de677ee2ea958214def1235ee248d0ba95719044e22b2783ce34672fa07271
|
7
|
+
data.tar.gz: a35e3ab5342d5f19bc561e49a312d2c888eadbd19603058ea68be472a4dcd6df8e451d5a46b0777bfa2d2f128ada982b65319ac10c53d3f251ef4015ebc0cc2f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Brian Moseley
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Shred
|
2
|
+
|
3
|
+
A task runner written in Ruby that does exactly what I want, how I want it.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'shred'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install shred
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/shred/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/shred
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'open3'
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
module Shred
|
6
|
+
module Commands
|
7
|
+
class ShellCommand
|
8
|
+
class CommandLine
|
9
|
+
attr_reader :command_line, :out
|
10
|
+
|
11
|
+
def initialize(command_line: nil, out: nil)
|
12
|
+
@command_line = command_line
|
13
|
+
@out = out
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
Bundler.with_clean_env do
|
18
|
+
if out
|
19
|
+
Open3.popen3(command_line) do |stdin, stdout, stderr, wait_thr|
|
20
|
+
install_signal_handler(wait_thr.pid)
|
21
|
+
|
22
|
+
while out_line = stdout.gets
|
23
|
+
out.write(out_line)
|
24
|
+
end
|
25
|
+
|
26
|
+
while err_line = stderr.gets
|
27
|
+
puts err_line
|
28
|
+
end
|
29
|
+
|
30
|
+
wait_thr.value
|
31
|
+
end
|
32
|
+
else
|
33
|
+
pid = Process.spawn(command_line)
|
34
|
+
install_signal_handler(pid)
|
35
|
+
Process.wait(pid)
|
36
|
+
$?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
command_line.to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def install_signal_handler(pid)
|
47
|
+
# make sure Ctrl-C gets passed on to the child process
|
48
|
+
# http://stackoverflow.com/questions/14635318/having-a-io-popen-command-be-killed-when-the-caller-process-is-killed
|
49
|
+
Signal.trap('INT') do
|
50
|
+
# propagate the signal to the child
|
51
|
+
Process.kill('INT', pid)
|
52
|
+
# send the signal back to this process
|
53
|
+
Signal.trap('INT', 'DEFAULT')
|
54
|
+
Process.kill('INT', 0)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
attr_reader :command_lines, :success_msg, :error_msg, :output, :out
|
60
|
+
|
61
|
+
def initialize(command_lines: nil, success_msg: nil, error_msg: nil, output: nil)
|
62
|
+
@command_lines = Array(command_lines).compact
|
63
|
+
raise ArgumentError, "At least one command line is required" if command_lines.empty?
|
64
|
+
@success_msg = success_msg
|
65
|
+
@error_msg = error_msg
|
66
|
+
@output = output
|
67
|
+
@out = File.open(output, 'w') if output
|
68
|
+
end
|
69
|
+
|
70
|
+
def run(&block)
|
71
|
+
exit_status = nil
|
72
|
+
command_lines.each_with_index do |command_line, i|
|
73
|
+
command_line = CommandLine.new(command_line: command_line, out: out)
|
74
|
+
exit_status = if block_given?
|
75
|
+
yield(command_line)
|
76
|
+
else
|
77
|
+
command_line.run
|
78
|
+
end
|
79
|
+
break unless exit_status.success?
|
80
|
+
end
|
81
|
+
exit_status
|
82
|
+
ensure
|
83
|
+
out.close if out
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class ShellCommandRunner
|
88
|
+
attr_reader :console
|
89
|
+
|
90
|
+
def initialize(console: console)
|
91
|
+
@console = console
|
92
|
+
end
|
93
|
+
|
94
|
+
def run(shell_command)
|
95
|
+
exit_status = shell_command.run do |command_line|
|
96
|
+
console.say_trace(command_line)
|
97
|
+
command_line.run
|
98
|
+
end
|
99
|
+
if exit_status.success?
|
100
|
+
if shell_command.success_msg
|
101
|
+
console.say_ok(shell_command.success_msg)
|
102
|
+
end
|
103
|
+
elsif
|
104
|
+
if shell_command.error_msg
|
105
|
+
console.say_err("#{shell_command.error_msg}: #{exit_status}")
|
106
|
+
else
|
107
|
+
console.say_err(exit_status)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Console
|
114
|
+
attr_reader :thor
|
115
|
+
|
116
|
+
def initialize(thor: nil)
|
117
|
+
@thor = thor
|
118
|
+
end
|
119
|
+
|
120
|
+
def say_trace(msg)
|
121
|
+
thor.say_status(:TRACE, msg, :green)
|
122
|
+
end
|
123
|
+
|
124
|
+
def say_ok(msg)
|
125
|
+
thor.say_status(:OK, msg, :blue)
|
126
|
+
end
|
127
|
+
|
128
|
+
def say_err(msg)
|
129
|
+
thor.say_status(:ERR, msg, :red)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class Base < Thor
|
134
|
+
attr_reader :command_name, :command_config, :console
|
135
|
+
|
136
|
+
def initialize(*args)
|
137
|
+
@command_name = args[2][:invocations][Shred::CLI].last
|
138
|
+
@command_config = Shred::CLI.config['commands'][@command_name]
|
139
|
+
@console = Console.new(thor: self)
|
140
|
+
super
|
141
|
+
configure
|
142
|
+
end
|
143
|
+
|
144
|
+
no_commands do
|
145
|
+
def configure
|
146
|
+
end
|
147
|
+
|
148
|
+
def cfg(key, required: true)
|
149
|
+
base_cfg = command_config
|
150
|
+
sub_keys = key.to_s.split('.')
|
151
|
+
value = nil
|
152
|
+
sub_keys.each_with_index do |sub_key, i|
|
153
|
+
if base_cfg.key?(sub_key)
|
154
|
+
value = base_cfg = base_cfg[sub_key]
|
155
|
+
elsif i < sub_keys.length - 1
|
156
|
+
raise "Missing '#{key}' config for '#{command}'"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
raise "Missing '#{key}' config for '#{command_name}' command" if required && !value
|
160
|
+
value
|
161
|
+
end
|
162
|
+
|
163
|
+
def run_shell_command(command)
|
164
|
+
ShellCommandRunner.new(console: console).run(command)
|
165
|
+
end
|
166
|
+
|
167
|
+
def load_rails
|
168
|
+
unless @rails_loaded
|
169
|
+
require File.expand_path('config/environment.rb')
|
170
|
+
@rails_loaded = true
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'shred/commands/base'
|
2
|
+
|
3
|
+
module Shred
|
4
|
+
module Commands
|
5
|
+
class Db < Base
|
6
|
+
desc 'init', 'Set up the database in development and test environments'
|
7
|
+
long_desc <<-LONGDESC
|
8
|
+
Recreate the database from the structure.sql file.
|
9
|
+
LONGDESC
|
10
|
+
def init
|
11
|
+
run_shell_command(ShellCommand.new(
|
12
|
+
command_lines: 'bin/rake db:create db:structure:load',
|
13
|
+
success_msg: 'Database initialized',
|
14
|
+
error_msg: 'Database could not be initialized'
|
15
|
+
))
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'migrate', 'Apply pending migrations to the database'
|
19
|
+
def migrate
|
20
|
+
run_shell_command(ShellCommand.new(
|
21
|
+
command_lines: 'bin/rake db:migrate',
|
22
|
+
success_msg: 'Migrations applied',
|
23
|
+
error_msg: 'Migrations could not be applied'
|
24
|
+
))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'shred/commands/base'
|
2
|
+
|
3
|
+
module Shred
|
4
|
+
module Commands
|
5
|
+
class Dotenv < Base
|
6
|
+
desc 'heroku', 'Write the environment config file from Heroku config variables'
|
7
|
+
long_desc <<-LONGDESC
|
8
|
+
Writes the environment config file (.env) by reading Heroku config variables.
|
9
|
+
|
10
|
+
Consults the commands.dotenv.heroku.app_name config item to determine which Heroku
|
11
|
+
app to load config vars from.
|
12
|
+
|
13
|
+
Consults the commands.dotenv.heroku.vars config item to determine which Heroku
|
14
|
+
config vars to add to the .env file.
|
15
|
+
LONGDESC
|
16
|
+
def heroku
|
17
|
+
app_name = cfg('heroku.app_name')
|
18
|
+
vars = cfg('heroku.vars')
|
19
|
+
mode = cfg('heroku.mode') == 'a' ? 'a' : 'w'
|
20
|
+
|
21
|
+
authenticate_to_heroku
|
22
|
+
|
23
|
+
run_shell_command(ShellCommand.new(
|
24
|
+
command_lines: "heroku config --app #{app_name} --shell",
|
25
|
+
output: '.heroku.env'
|
26
|
+
))
|
27
|
+
File.open('.heroku.env') do |input|
|
28
|
+
File.open('.env', mode) do |output|
|
29
|
+
input.readlines.each do |line|
|
30
|
+
line.chomp!
|
31
|
+
if line =~ /^([^=]+)=/ && vars.include?($1)
|
32
|
+
output.write("#{line}\n")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
File.unlink('.heroku.env')
|
38
|
+
console.say_ok("Heroku config written to environment config file")
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'custom', 'Write custom config items to the environment config file'
|
42
|
+
long_desc <<-LONGDESC
|
43
|
+
Writes custom config items to the environment config file (.env).
|
44
|
+
|
45
|
+
Consults the commands.dotenv.custom config item to determine the custom config items to
|
46
|
+
add to the .env file.
|
47
|
+
LONGDESC
|
48
|
+
option :append, type: :boolean, default: false
|
49
|
+
def custom
|
50
|
+
custom = cfg('custom.vars')
|
51
|
+
mode = cfg('custom.mode') == 'a' ? 'a' : 'w'
|
52
|
+
|
53
|
+
File.open('.env', mode) do |output|
|
54
|
+
custom.each do |key, value|
|
55
|
+
output.write("#{key}=#{value}\n")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
console.say_ok("Custom config written to environment config file")
|
59
|
+
end
|
60
|
+
|
61
|
+
no_commands do
|
62
|
+
def authenticate_to_heroku
|
63
|
+
run_shell_command(ShellCommand.new(command_lines: 'heroku auth:whoami'))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'shred/commands/base'
|
2
|
+
|
3
|
+
module Shred
|
4
|
+
module Commands
|
5
|
+
class PlatformDeps < Base
|
6
|
+
attr_reader :supported_dependencies
|
7
|
+
|
8
|
+
class Dependency
|
9
|
+
attr_reader :sym
|
10
|
+
|
11
|
+
def initialize(sym: nil)
|
12
|
+
@sym = sym
|
13
|
+
end
|
14
|
+
|
15
|
+
def install(ctx, command_lines)
|
16
|
+
ctx.run_shell_command(ShellCommand.new(
|
17
|
+
command_lines: command_lines,
|
18
|
+
success_msg: "#{sym} installed",
|
19
|
+
error_msg: "#{sym} could not be installed"
|
20
|
+
))
|
21
|
+
end
|
22
|
+
|
23
|
+
def update(ctx, command_lines)
|
24
|
+
ctx.run_shell_command(ShellCommand.new(
|
25
|
+
command_lines: command_lines,
|
26
|
+
success_msg: "#{sym} updated",
|
27
|
+
error_msg: "#{sym} could not be updated"
|
28
|
+
))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class HomebrewDependency < Dependency
|
33
|
+
def install(ctx)
|
34
|
+
super(ctx, "brew install #{sym}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def update(ctx)
|
38
|
+
super(ctx, "brew upgrade #{sym}")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class RubyGemDependency < Dependency
|
43
|
+
def install(ctx)
|
44
|
+
super(ctx, "gem install #{sym}")
|
45
|
+
end
|
46
|
+
|
47
|
+
def update(ctx)
|
48
|
+
super(ctx, "gem update #{sym}")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class ShellCommandDependency < Dependency
|
53
|
+
attr_reader :install_command_lines, :update_command_lines
|
54
|
+
|
55
|
+
def initialize(sym: nil, install_command_lines: nil, update_command_lines: nil)
|
56
|
+
super(sym: sym)
|
57
|
+
@install_command_lines = install_command_lines
|
58
|
+
@update_command_lines = update_command_lines
|
59
|
+
end
|
60
|
+
|
61
|
+
def install(ctx)
|
62
|
+
super(ctx, install_command_lines)
|
63
|
+
end
|
64
|
+
|
65
|
+
def update(ctx)
|
66
|
+
super(ctx, update_command_lines)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
desc 'install [dependencies...]', 'Install some or all platform dependencies'
|
71
|
+
long_desc <<-LONGDESC
|
72
|
+
Installs platform dependencies that the application depends on.
|
73
|
+
|
74
|
+
When no dependencies are specified, all dependencies are installed.
|
75
|
+
|
76
|
+
When one or more dependencies are specified, only those dependencies are installed.
|
77
|
+
LONGDESC
|
78
|
+
def install(*dependencies)
|
79
|
+
invoke_for_dependencies(:install, *dependencies)
|
80
|
+
end
|
81
|
+
|
82
|
+
desc 'update [dependencies...]', 'Install some or all platform dependencies'
|
83
|
+
long_desc <<-LONGDESC
|
84
|
+
Updates platform dependencies that the application depends on.
|
85
|
+
|
86
|
+
When no dependencies are specified, all dependencies are updated.
|
87
|
+
|
88
|
+
When one or more dependencies are specified, only those dependencies are updated.
|
89
|
+
LONGDESC
|
90
|
+
def update(*dependencies)
|
91
|
+
invoke_for_dependencies(:update, *dependencies)
|
92
|
+
end
|
93
|
+
|
94
|
+
no_commands do
|
95
|
+
def configure
|
96
|
+
@supported_dependencies = command_config.each_with_object([]) do |(type, specs), m|
|
97
|
+
case type.to_sym
|
98
|
+
when :homebrew
|
99
|
+
specs.each_with_object(m) { |svc, mm| mm << HomebrewDependency.new(sym: svc.to_sym) }
|
100
|
+
when :rubygems
|
101
|
+
specs.each_with_object(m) { |svc, mm| mm << RubyGemDependency.new(sym: svc.to_sym) }
|
102
|
+
when :shell
|
103
|
+
specs.each_with_object(m) do |(svc, keys), mm|
|
104
|
+
install = keys['install'] or
|
105
|
+
raise "Missing 'install' config for '#{svc}' platform dependency"
|
106
|
+
update = keys['update'] or
|
107
|
+
raise "Missing 'update' config for '#{svc}' platform dependency"
|
108
|
+
mm << ShellCommandDependency.new(
|
109
|
+
sym: svc.to_sym,
|
110
|
+
install_command_lines: install,
|
111
|
+
update_command_lines: update
|
112
|
+
)
|
113
|
+
end
|
114
|
+
else raise "Unknown platform dependency type #{type}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def invoke_for_dependencies(meth, *dependencies)
|
120
|
+
dependencies = supported_dependencies.map { |d| d.sym.to_s} if dependencies.empty?
|
121
|
+
dependencies.each do |dependency|
|
122
|
+
dependency = supported_dependencies.detect { |d| d.sym.to_s == dependency }
|
123
|
+
if dependency
|
124
|
+
dependency.send(meth, self)
|
125
|
+
else
|
126
|
+
say_err("No such dependency #{dependency}")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'shred/commands/base'
|
2
|
+
|
3
|
+
module Shred
|
4
|
+
module Commands
|
5
|
+
class RubyDeps < Base
|
6
|
+
desc 'install', 'Install Ruby dependencies'
|
7
|
+
def install
|
8
|
+
run_shell_command(ShellCommand.new(
|
9
|
+
command_lines: 'bundle install',
|
10
|
+
success_msg: "Ruby dependencies installed",
|
11
|
+
error_msg: "Ruby dependencies could not be installed"
|
12
|
+
))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'shred/commands/base'
|
2
|
+
|
3
|
+
module Shred
|
4
|
+
module Commands
|
5
|
+
class Services < Base
|
6
|
+
attr_reader :supported_services
|
7
|
+
|
8
|
+
class Service
|
9
|
+
attr_reader :sym
|
10
|
+
|
11
|
+
def initialize(sym: nil)
|
12
|
+
@sym = sym
|
13
|
+
end
|
14
|
+
|
15
|
+
def start(ctx, command_lines)
|
16
|
+
ctx.run_shell_command(ShellCommand.new(
|
17
|
+
command_lines: command_lines,
|
18
|
+
success_msg: "#{sym} started",
|
19
|
+
error_msg: "#{sym} could not be started"
|
20
|
+
))
|
21
|
+
end
|
22
|
+
|
23
|
+
def stop(ctx, command_lines)
|
24
|
+
ctx.run_shell_command(ShellCommand.new(
|
25
|
+
command_lines: command_lines,
|
26
|
+
success_msg: "#{sym} stopped",
|
27
|
+
error_msg: "#{sym} could not be stopped"
|
28
|
+
))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class LaunchctlService < Service
|
33
|
+
attr_reader :plist
|
34
|
+
|
35
|
+
def initialize(sym: nil, plist: nil)
|
36
|
+
super(sym: sym)
|
37
|
+
@plist = plist
|
38
|
+
end
|
39
|
+
|
40
|
+
def start(ctx)
|
41
|
+
super(ctx, "launchctl load -w -F #{plist}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def stop(ctx)
|
45
|
+
super(ctx, "launchctl unload #{plist}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class ShellCommandService < Service
|
50
|
+
attr_reader :start_command_lines, :stop_command_lines
|
51
|
+
|
52
|
+
def initialize(sym: nil, start_command_lines: nil, stop_command_lines: nil)
|
53
|
+
super(sym: sym)
|
54
|
+
@start_command_lines = start_command_lines
|
55
|
+
@stop_command_lines = stop_command_lines
|
56
|
+
end
|
57
|
+
|
58
|
+
def start(ctx)
|
59
|
+
super(ctx, start_command_lines)
|
60
|
+
end
|
61
|
+
|
62
|
+
def stop(ctx)
|
63
|
+
super(ctx, stop_command_lines)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
desc 'start [services...]', 'Start some or all platform services'
|
68
|
+
long_desc <<-LONGDESC
|
69
|
+
Starts platform services that the application uses.
|
70
|
+
|
71
|
+
When no services are specified, all services are started.
|
72
|
+
|
73
|
+
When one or more services are specified, only those services are started.
|
74
|
+
LONGDESC
|
75
|
+
def start(*services)
|
76
|
+
invoke_for_services(:start, *services)
|
77
|
+
end
|
78
|
+
|
79
|
+
desc 'stop [services...]', 'Stop some or all platform services'
|
80
|
+
long_desc <<-LONGDESC
|
81
|
+
Stops platform services that the application uses.
|
82
|
+
|
83
|
+
When no services are specified, all services are stopped.
|
84
|
+
|
85
|
+
When one or more services are specified, only those services are stopped.
|
86
|
+
LONGDESC
|
87
|
+
def stop(*services)
|
88
|
+
invoke_for_services(:stop, *services)
|
89
|
+
end
|
90
|
+
|
91
|
+
no_commands do
|
92
|
+
def configure
|
93
|
+
@supported_services = command_config.each_with_object([]) do |(type, specs), m|
|
94
|
+
case type.to_sym
|
95
|
+
when :launchctl
|
96
|
+
specs.each_with_object(m) do |(svc, keys), mm|
|
97
|
+
plist = keys['plist'] or
|
98
|
+
raise "Missing 'plist' config for '#{svc}' platform service"
|
99
|
+
mm << LaunchctlService.new(
|
100
|
+
sym: svc.to_sym,
|
101
|
+
plist: plist
|
102
|
+
)
|
103
|
+
end
|
104
|
+
when :shell
|
105
|
+
specs.each_with_object(m) do |(svc, keys), mm|
|
106
|
+
start = keys['start'] or
|
107
|
+
raise "Missing 'start' config for '#{svc}' platform service"
|
108
|
+
stop = keys['stop'] or
|
109
|
+
raise "Missing 'stop' config for '#{svc}' platform service"
|
110
|
+
mm << ShellCommandService.new(
|
111
|
+
sym: svc.to_sym,
|
112
|
+
start_command_lines: start,
|
113
|
+
stop_command_lines: stop
|
114
|
+
)
|
115
|
+
end
|
116
|
+
else raise "Unknown platform service type #{type}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def invoke_for_services(meth, *services)
|
122
|
+
services = supported_services.map { |d| d.sym.to_s} if services.empty?
|
123
|
+
services.each do |service|
|
124
|
+
service = supported_services.detect { |d| d.sym.to_s == service }
|
125
|
+
if service
|
126
|
+
service.send(meth, self)
|
127
|
+
else
|
128
|
+
say_err("No such service #{service}")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'shred/commands/base'
|
2
|
+
|
3
|
+
module Shred
|
4
|
+
module Commands
|
5
|
+
class Test < Base
|
6
|
+
desc 'all', 'Run all tests'
|
7
|
+
def all
|
8
|
+
invoke(:server) if cfg('server', required: false)
|
9
|
+
invoke(:client) if cfg('client', required: false)
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'server', 'Run only server tests'
|
13
|
+
def server
|
14
|
+
run_shell_command(ShellCommand.new(command_lines: cfg('server')))
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'client', 'Run only client tests'
|
18
|
+
def client
|
19
|
+
run_shell_command(ShellCommand.new(command_lines: cfg('client')))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/shred.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'shred/commands/app'
|
2
|
+
require 'shred/commands/db'
|
3
|
+
require 'shred/commands/dotenv'
|
4
|
+
require 'shred/commands/platform_deps'
|
5
|
+
require 'shred/commands/ruby_deps'
|
6
|
+
require 'shred/commands/services'
|
7
|
+
require 'shred/commands/test'
|
8
|
+
require 'shred/version'
|
9
|
+
require 'thor'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
module Shred
|
13
|
+
class CLI < Thor
|
14
|
+
def self.start(*)
|
15
|
+
load_config
|
16
|
+
set_up_commands
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.load_config
|
21
|
+
@config = YAML.load(File.new('shred.yml'))
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.set_up_commands
|
25
|
+
if config.key?('commands')
|
26
|
+
commands = config['commands']
|
27
|
+
if commands.key?('platform_deps')
|
28
|
+
desc 'platform_deps SUBCOMMAND ...ARGS', 'Manage platform dependencies'
|
29
|
+
subcommand 'platform_deps', Commands::PlatformDeps
|
30
|
+
end
|
31
|
+
if commands.key?('services')
|
32
|
+
desc 'services SUBCOMMAND ...ARGS', 'Control platform services'
|
33
|
+
subcommand 'services', Commands::Services
|
34
|
+
end
|
35
|
+
if commands.key?('ruby_deps')
|
36
|
+
desc 'ruby_deps SUBCOMMAND ...ARGS', 'Manage Ruby dependencies'
|
37
|
+
subcommand 'ruby_deps', Commands::RubyDeps
|
38
|
+
end
|
39
|
+
if commands.key?('db')
|
40
|
+
desc 'db SUBCOMMAND ...ARGS', 'Manage the database'
|
41
|
+
subcommand 'db', Commands::Db
|
42
|
+
end
|
43
|
+
if commands.key?('dotenv')
|
44
|
+
desc 'dotenv SUBCOMMAND ...ARGS', 'Manage the environmental configuration'
|
45
|
+
subcommand 'dotenv', Commands::Dotenv
|
46
|
+
end
|
47
|
+
if commands.key?('test')
|
48
|
+
desc 'test SUBCOMMAND ...ARGS', 'Run tests'
|
49
|
+
subcommand 'test', Commands::Test
|
50
|
+
end
|
51
|
+
if commands.key?('app')
|
52
|
+
desc 'app SUBCOMMAND ...ARGS', 'Control the application'
|
53
|
+
subcommand 'app', Commands::App
|
54
|
+
end
|
55
|
+
if commands.key?('setup')
|
56
|
+
desc 'setup', 'First-time application setup'
|
57
|
+
def setup
|
58
|
+
self.class.config['commands']['setup'].each do |cmd, subcmds|
|
59
|
+
invoke(cmd.to_sym, subcmds.map(&:to_sym))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class << self
|
67
|
+
attr_reader :config
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/shred.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'shred/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'shred'
|
8
|
+
spec.version = Shred::VERSION
|
9
|
+
spec.authors = %w(Brian Moseley)
|
10
|
+
spec.email = %w(bcm@maz.org)
|
11
|
+
spec.summary = 'Opinionated Ruby task runner'
|
12
|
+
spec.description = 'A task runner written in Ruby that does exactly what I want, how I want it'
|
13
|
+
spec.homepage = 'http://github.com/bcm/shred'
|
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 = %w(lib)
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
|
24
|
+
spec.add_dependency 'thor'
|
25
|
+
end
|
data/shred.yml
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
commands:
|
2
|
+
app:
|
3
|
+
start:
|
4
|
+
- foreman start
|
5
|
+
db:
|
6
|
+
dotenv:
|
7
|
+
heroku:
|
8
|
+
app_name: my-app
|
9
|
+
vars:
|
10
|
+
- AIRBRAKE_API_KEY
|
11
|
+
mode: w
|
12
|
+
custom:
|
13
|
+
vars:
|
14
|
+
RACK_ENV: none
|
15
|
+
RAILS_ENV: development
|
16
|
+
WEB_CONCURRENCY: 1
|
17
|
+
mode: a
|
18
|
+
platform_deps:
|
19
|
+
homebrew:
|
20
|
+
- postgres
|
21
|
+
- redis
|
22
|
+
- heroku
|
23
|
+
rubygems:
|
24
|
+
- bundler
|
25
|
+
shell:
|
26
|
+
mailcatcher:
|
27
|
+
install:
|
28
|
+
- rvm default@mailcatcher --create do gem install mailcatcher
|
29
|
+
- rvm wrapper default@mailcatcher --no-prefix mailcatcher catchmail
|
30
|
+
# https://github.com/sj26/mailcatcher/issues/155
|
31
|
+
- rvm default@mailcatcher do gem install i18n -v 0.6.11
|
32
|
+
# - rvm default@mailcatcher do gem uninstall i18n -Ix --version '>0.6.11'
|
33
|
+
update:
|
34
|
+
- rvm default@mailcatcher do gem update mailcatcher
|
35
|
+
ruby_deps:
|
36
|
+
services:
|
37
|
+
launchctl:
|
38
|
+
postgres:
|
39
|
+
plist: /usr/local/opt/postgresql/homebrew.mxcl.postgresql.plist
|
40
|
+
redis:
|
41
|
+
plist: /usr/local/opt/redis/homebrew.mxcl.redis.plist
|
42
|
+
shell:
|
43
|
+
mailcatcher:
|
44
|
+
start: mailcatcher
|
45
|
+
stop: killall mailcatcher
|
46
|
+
setup:
|
47
|
+
platform_deps:
|
48
|
+
- install
|
49
|
+
services:
|
50
|
+
- start
|
51
|
+
ruby_deps:
|
52
|
+
- install
|
53
|
+
pg:
|
54
|
+
- init
|
55
|
+
dotenv:
|
56
|
+
- heroku
|
57
|
+
- custom
|
58
|
+
test:
|
59
|
+
server:
|
60
|
+
- bin/rake spec
|
61
|
+
client:
|
62
|
+
- bin/rake konacha:run
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shred
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian
|
8
|
+
- Moseley
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-10-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ~>
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.7'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.7'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '10.0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '10.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: thor
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
description: A task runner written in Ruby that does exactly what I want, how I want
|
57
|
+
it
|
58
|
+
email:
|
59
|
+
- bcm@maz.org
|
60
|
+
executables:
|
61
|
+
- shred
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files: []
|
64
|
+
files:
|
65
|
+
- .gitignore
|
66
|
+
- Gemfile
|
67
|
+
- LICENSE.txt
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- bin/shred
|
71
|
+
- lib/shred.rb
|
72
|
+
- lib/shred/commands/app.rb
|
73
|
+
- lib/shred/commands/base.rb
|
74
|
+
- lib/shred/commands/db.rb
|
75
|
+
- lib/shred/commands/dotenv.rb
|
76
|
+
- lib/shred/commands/platform_deps.rb
|
77
|
+
- lib/shred/commands/ruby_deps.rb
|
78
|
+
- lib/shred/commands/services.rb
|
79
|
+
- lib/shred/commands/test.rb
|
80
|
+
- lib/shred/version.rb
|
81
|
+
- shred.gemspec
|
82
|
+
- shred.yml
|
83
|
+
homepage: http://github.com/bcm/shred
|
84
|
+
licenses:
|
85
|
+
- MIT
|
86
|
+
metadata: {}
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 2.2.2
|
104
|
+
signing_key:
|
105
|
+
specification_version: 4
|
106
|
+
summary: Opinionated Ruby task runner
|
107
|
+
test_files: []
|