batcave 0.0.1 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/README.md +45 -6
- data/batcave.gemspec +2 -2
- data/lib/batcave/action/add.rb +43 -0
- data/lib/batcave/command/add.rb +9 -48
- data/lib/batcave/command/update.rb +9 -1
- data/lib/batcave/dsl.rb +254 -0
- data/lib/batcave/main.rb +11 -0
- data/lib/batcave/namespace.rb +1 -0
- data/lib/batcave/store.rb +74 -0
- data/lib/batcave/support/envpath.rb +14 -0
- data/lib/batcave/support/git.rb +2 -0
- data/things/dotfiles/self/THING +6 -0
- data/things/go/self/THING +8 -0
- data/things/ruby/self/THING +9 -0
- data/things/ruby/self/{name}.gemspec.erb +20 -0
- data/things/user.sh/THING +7 -0
- metadata +14 -8
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -7,10 +7,22 @@ Wikipedia on "Batcave" uses:
|
|
7
7
|
> ideal to create a stronghold for his war against crime, and has incorporated a
|
8
8
|
> plethora of equipment as well as expanding the cave for specific uses.
|
9
9
|
|
10
|
-
|
11
|
-
tools,
|
10
|
+
Frankly, I write a lot of code in a lot of languages. There's a lot of
|
11
|
+
boilerplate, tools, and scripts I copy from project to project. Debugging,
|
12
|
+
coding, documentation, tests, etc all are quite similar in goal across projects
|
13
|
+
but differ wildly in implementation. I'd like to codify most of this so that,
|
14
|
+
when switching between projects, the cost of context switching isn't as painful.
|
12
15
|
|
13
|
-
|
16
|
+
Essentially, I do roughly the same activities regardless of platform or language.
|
17
|
+
This project is an experiment in solving the above problem.
|
18
|
+
|
19
|
+
Ideally it'd be sweet if this project would also manage my workstation as well
|
20
|
+
as my projects. Dotfiles, packages, etc. I'm tired of setting this shit up or
|
21
|
+
scripting it sideways every few months.
|
22
|
+
|
23
|
+
This project may end up looking a lot like an IDE plus some config management.
|
24
|
+
|
25
|
+
## Example
|
14
26
|
|
15
27
|
Maybe, for example, I'm writing an HTTP library in Ruby, and I want to:
|
16
28
|
|
@@ -18,11 +30,38 @@ Maybe, for example, I'm writing an HTTP library in Ruby, and I want to:
|
|
18
30
|
* run tests whenever I modify the code.
|
19
31
|
* make sure all methods have docstrings (with YARD)
|
20
32
|
* have some useful debugging tools.
|
33
|
+
* generate documentation to publish online
|
21
34
|
|
22
|
-
But new projects should be easier:
|
35
|
+
But new projects should be easier, too:
|
23
36
|
|
24
37
|
* create a 'go' project with a layout that works well with 'goinstall'
|
25
|
-
* create a 'ruby' project with a standard layout, Gemfile, gemspec, etc
|
26
|
-
|
38
|
+
* create a 'ruby' project with a standard layout, Gemfile, gemspec, etc. A bin
|
39
|
+
file that uses clamp with sample flag code. A namespace file generated for
|
40
|
+
me, etc.
|
41
|
+
* automatically create basic tests
|
42
|
+
* include tools for debugging
|
27
43
|
|
28
44
|
Release management should be easier, too.
|
45
|
+
|
46
|
+
## Idea: boilerplate and project creation
|
47
|
+
|
48
|
+
* dk add thing
|
49
|
+
|
50
|
+
a 'thing' is a feature, boilerplate, some debug tool, metrics or testing tool, etc.
|
51
|
+
|
52
|
+
Example:
|
53
|
+
|
54
|
+
* dk add --name example go
|
55
|
+
* dk add ruby
|
56
|
+
|
57
|
+
Both of the above will generate boilerplate projects for their respective
|
58
|
+
languages. It currently uses git to find the root of your project directory
|
59
|
+
and puts files based from that location.
|
60
|
+
|
61
|
+
Boilerplates should include basic tests, etc.
|
62
|
+
|
63
|
+
## Other ideas in command-line form
|
64
|
+
|
65
|
+
* dk release - run tests, bump version, build package, run tests, tag it, publish
|
66
|
+
* dk add travis-ci - add .travis.yml file with language detection
|
67
|
+
* dk debug - run an application inside a debugger (gdb for c/c++, ruby's debugger or pry, etc)
|
data/batcave.gemspec
CHANGED
@@ -2,9 +2,9 @@ Gem::Specification.new do |spec|
|
|
2
2
|
files = %x{git ls-files}.split("\n")
|
3
3
|
|
4
4
|
spec.name = "batcave"
|
5
|
-
spec.version = "0.0.
|
5
|
+
spec.version = "0.0.4"
|
6
6
|
spec.summary = "Experiments in tools, boilerplatery, debugging, etc."
|
7
|
-
spec.
|
7
|
+
spec.description = spec.summary
|
8
8
|
spec.add_dependency("clamp")
|
9
9
|
spec.files = files
|
10
10
|
spec.bindir = "bin"
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "batcave/namespace"
|
2
|
+
require "batcave/support/git"
|
3
|
+
|
4
|
+
class BatCave::Action::Add
|
5
|
+
include BatCave::Support::Git
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
# Add a new thing with some arguments.
|
10
|
+
#
|
11
|
+
# Arguments for each thing are defined in the 'THING' file for each ... thing.
|
12
|
+
def initialize(thing, args)
|
13
|
+
@logger = Cabin::Channel.get("batcave")
|
14
|
+
@thing = thing
|
15
|
+
@args = args
|
16
|
+
end # def initialize
|
17
|
+
|
18
|
+
def find_thing(thing)
|
19
|
+
# look for the 'thing/' or if it's a directory try 'thing/self/'
|
20
|
+
[ @thing, File.join(@thing, "self") ].each do |thing|
|
21
|
+
path = File.join(BatCave::THINGSDIR, thing)
|
22
|
+
config = File.join(path, "THING")
|
23
|
+
return config if File.exists?(config)
|
24
|
+
end
|
25
|
+
|
26
|
+
puts "Could not find any thing '#{@thing}'"
|
27
|
+
return false
|
28
|
+
end # def find_thing
|
29
|
+
|
30
|
+
def execute
|
31
|
+
config = find_thing(@thing)
|
32
|
+
return 1 if config == false
|
33
|
+
dsl = BatCave::DSL.new(config, @thing, @args)
|
34
|
+
dsl.execute
|
35
|
+
|
36
|
+
# TODO(sissel): Record that we've added this thing.
|
37
|
+
puts "Adding #{dsl.environment}/#{@thing}"
|
38
|
+
store = BatCave::Store.new
|
39
|
+
store.store(dsl)
|
40
|
+
end # def execute
|
41
|
+
|
42
|
+
public(:initialize, :execute)
|
43
|
+
end # class BatCave::Action::Add
|
data/lib/batcave/command/add.rb
CHANGED
@@ -1,63 +1,24 @@
|
|
1
1
|
require "clamp"
|
2
2
|
require "batcave/namespace"
|
3
|
-
require "batcave/
|
3
|
+
require "batcave/action/add"
|
4
|
+
require "batcave/dsl"
|
5
|
+
require "batcave/store"
|
4
6
|
require "fileutils"
|
5
7
|
|
8
|
+
# TODO(sissel): Need to track what we've added so we can sync later.
|
9
|
+
|
6
10
|
class BatCave::Command::Add < Clamp::Command
|
7
|
-
include BatCave::Support::Git
|
8
11
|
|
12
|
+
# TODO(sissel): Move this to the 'thing' DSL
|
9
13
|
option ["-n", "--name"], "NAME",
|
10
14
|
"the application or library name", :attribute_name => :name
|
11
15
|
|
12
16
|
parameter "THING",
|
13
17
|
"The thing to add to your batcave", :attribute_name => :thing
|
14
18
|
|
15
|
-
|
16
|
-
# TODO(sissel): Move this stuff into a proper batcave library
|
17
|
-
|
18
|
-
found = false
|
19
|
-
# look for the 'thing/' or if it's a directory try 'thing/self/'
|
20
|
-
[ @thing, File.join(@thing, "self") ].each do |thing|
|
21
|
-
path = File.join(BatCave::THINGSDIR, thing)
|
22
|
-
config = File.join(path, "THING")
|
23
|
-
if File.exists?(config)
|
24
|
-
found = true
|
25
|
-
use(path)
|
26
|
-
break
|
27
|
-
end
|
28
|
-
end
|
19
|
+
parameter "[THINGARGS] ...", "arguments to pass to the thing", :attribute_name => :args
|
29
20
|
|
30
|
-
|
31
|
-
|
32
|
-
end
|
21
|
+
def execute
|
22
|
+
BatCave::Action::Add.new(@thing, @args).execute
|
33
23
|
end # def execute
|
34
|
-
|
35
|
-
def use(dir)
|
36
|
-
config = File.join(dir, "THING")
|
37
|
-
paths = Dir.glob(File.join(dir, "**", "*"))
|
38
|
-
|
39
|
-
paths.each do |path|
|
40
|
-
next if path == config # skip the 'THING' file
|
41
|
-
localpath = File.join(project_root, path[dir.length + 1 .. -1])
|
42
|
-
|
43
|
-
if localpath.include?("{name}")
|
44
|
-
if @name.nil?
|
45
|
-
raise "Path requires '--name' flag to be set: #{localpath.inspect}"
|
46
|
-
end
|
47
|
-
localpath.gsub!("{name}", @name)
|
48
|
-
end
|
49
|
-
|
50
|
-
# Replace '{...}' in localpath
|
51
|
-
|
52
|
-
# TODO(sissel): if this is a directory, create it.
|
53
|
-
# TODO(sissel): if this a file, copy it.
|
54
|
-
if File.directory?(path)
|
55
|
-
FileUtils.mkdir_p(localpath) unless File.directory?(localpath)
|
56
|
-
else
|
57
|
-
localdir = File.dirname(localpath)
|
58
|
-
FileUtils.mkdir_p(localdir) unless File.directory?(localdir)
|
59
|
-
FileUtils.cp(path, localpath)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
24
|
end
|
@@ -1,8 +1,16 @@
|
|
1
1
|
require "clamp"
|
2
2
|
require "batcave/namespace"
|
3
|
+
require "batcave/command/add"
|
3
4
|
|
4
5
|
class BatCave::Command::Update < Clamp::Command
|
6
|
+
parameter "ENVIRONMENT", "The environment to update (user, project, etc)",
|
7
|
+
:attribute_name => :environment
|
8
|
+
|
5
9
|
def execute
|
6
|
-
|
10
|
+
store = BatCave::Store.new
|
11
|
+
store.each(@environment) do |thing, settings|
|
12
|
+
args = settings["args"]
|
13
|
+
BatCave::Action::Add.new(thing, args).execute
|
14
|
+
end
|
7
15
|
end
|
8
16
|
end
|
data/lib/batcave/dsl.rb
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
require "batcave/namespace"
|
2
|
+
require "batcave/support/envpath"
|
3
|
+
require "fileutils" # stdlib
|
4
|
+
require "cabin" # gem 'cabin'
|
5
|
+
require "erb" # stdlib
|
6
|
+
|
7
|
+
# TODO(sissel): DSL is a poor name for this class. Fix it later.
|
8
|
+
# TODO(sissel): Split the 'THING' processor from the 'Thing' instances
|
9
|
+
class BatCave::DSL
|
10
|
+
include BatCave::Support::Git
|
11
|
+
include BatCave::Support::EnvPath
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def initialize(configfile, thing, args)
|
16
|
+
@args = args
|
17
|
+
@thing = thing
|
18
|
+
@configfile = configfile
|
19
|
+
@logger = Cabin::Channel.get("batcave")
|
20
|
+
|
21
|
+
# Default environment is project.
|
22
|
+
@environment = :project
|
23
|
+
# Update all files by default
|
24
|
+
@create_only = []
|
25
|
+
|
26
|
+
@target ||= path(@environment)
|
27
|
+
@sourcedir = File.dirname(@configfile)
|
28
|
+
@sync = true
|
29
|
+
|
30
|
+
# Make this 'thing' argument into a command by
|
31
|
+
# dynamically making a new subclass of Clamp::Command
|
32
|
+
@command = Class.new(Clamp::Command).new("<internal dsl thing>")
|
33
|
+
class << @command
|
34
|
+
def execute
|
35
|
+
# nothing to do, we just want to parse flags.
|
36
|
+
end
|
37
|
+
end
|
38
|
+
binding.eval(File.read(@configfile), @configfile)
|
39
|
+
end # def initialize
|
40
|
+
|
41
|
+
# Declare options for this thing. This block will be executed
|
42
|
+
# in the context of a Clamp::Command, so anything valid in a
|
43
|
+
# Clamp::Command definition is valid here. The options will
|
44
|
+
# be parsed immediately after this block is evaluated.
|
45
|
+
#
|
46
|
+
# Example:
|
47
|
+
#
|
48
|
+
# options do
|
49
|
+
# parameter "SOURCE", "The source to use", :attribute_name => :foo
|
50
|
+
# end
|
51
|
+
def options(&block)
|
52
|
+
@command.class.instance_eval(&block)
|
53
|
+
@command.run(@args)
|
54
|
+
|
55
|
+
# Copy related instance variables from the Clamp::Command to this object.
|
56
|
+
@command.instance_variables.each do |ivar|
|
57
|
+
# Skip ones that are part of the Command normally (not attributes)
|
58
|
+
next if [:@invocation_path, :@context, :@remaining_arguments].include?(ivar)
|
59
|
+
instance_variable_set(ivar, @command.instance_variable_get(ivar))
|
60
|
+
end
|
61
|
+
end # def options
|
62
|
+
|
63
|
+
# Set the source to use for this thing
|
64
|
+
def source(upstream)
|
65
|
+
root = File.join(ENV["HOME"], ".batcave", "upstream")
|
66
|
+
FileUtils.mkdir_p(root)
|
67
|
+
Dir.chdir(root) do
|
68
|
+
if !File.directory?(@thing)
|
69
|
+
cmd = "git clone #{upstream} #{@thing}"
|
70
|
+
else
|
71
|
+
#cmd = "cd #{@thing}; git fetch origin master; git reset --hard origin/master"
|
72
|
+
cmd = "cd #{@thing}; git pull origin master"
|
73
|
+
end
|
74
|
+
|
75
|
+
@logger.info("Running", :command => cmd)
|
76
|
+
system(cmd)
|
77
|
+
if $?.exitstatus != 0
|
78
|
+
# TODO(sissel): Do something better than aborting
|
79
|
+
raise "Command exited #{$?.exitstatus}: #{cmd}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
@sourcedir = File.join(root, @thing)
|
83
|
+
end # def source
|
84
|
+
|
85
|
+
def target(directory)
|
86
|
+
@target = directory
|
87
|
+
end # def target
|
88
|
+
|
89
|
+
# Do path expansion.
|
90
|
+
#
|
91
|
+
# If the path is /foo/bar/{name}
|
92
|
+
# and @names is ["one", "two"], then this will:
|
93
|
+
#
|
94
|
+
# * yield "/foo/bar/one"
|
95
|
+
# * yield "/foo/bar/two"
|
96
|
+
#
|
97
|
+
# A {foo} will check both @foo and @foos
|
98
|
+
def expand(paths, &block)
|
99
|
+
skip_re = /(^|\/)\.(\.|git|svn)/
|
100
|
+
paths.each do |path|
|
101
|
+
next if skip_re.match(path)
|
102
|
+
# find all {...} in the string
|
103
|
+
tokens = path.scan(/{[^}]+}/)
|
104
|
+
if tokens.include?("{name}")
|
105
|
+
names = []
|
106
|
+
names << @name if instance_variable_defined?(:@name)
|
107
|
+
names += @names if instance_variable_defined?(:@names)
|
108
|
+
|
109
|
+
if names.empty?
|
110
|
+
message = [
|
111
|
+
"No {name} known, can't expand #{path}",
|
112
|
+
"maybe add this to your THING file: ",
|
113
|
+
"",
|
114
|
+
"options do",
|
115
|
+
" parameter 'NAME', 'The {name} value', :attribute_name => :name",
|
116
|
+
"end"
|
117
|
+
].join("\n")
|
118
|
+
raise message
|
119
|
+
end
|
120
|
+
|
121
|
+
names.each do |name|
|
122
|
+
yield path, path.gsub("{name}", name)
|
123
|
+
end
|
124
|
+
else
|
125
|
+
yield path, path
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end # def expand
|
129
|
+
|
130
|
+
def create_only(*paths)
|
131
|
+
if paths.first.is_a?(Array)
|
132
|
+
paths = paths.first
|
133
|
+
end
|
134
|
+
|
135
|
+
@create_only = paths
|
136
|
+
end # def create_only
|
137
|
+
|
138
|
+
# Sync two paths
|
139
|
+
def sync
|
140
|
+
if !instance_variable_defined?(:@sourcedir)
|
141
|
+
@logger.error("Can't sync, no source defined")
|
142
|
+
return
|
143
|
+
end
|
144
|
+
|
145
|
+
paths = Dir.glob(File.join(@sourcedir, "**", "*"), File::FNM_DOTMATCH)
|
146
|
+
expand(paths) do |source, target|
|
147
|
+
next if source == @configfile # skip the 'THING' file
|
148
|
+
originalpath = source[@sourcedir.length + 1 .. -1]
|
149
|
+
localpath = File.join(path(@environment), target[@sourcedir.length + 1 .. -1])
|
150
|
+
|
151
|
+
if localpath =~ /\.erb$/
|
152
|
+
localpath = localpath[0...-4]
|
153
|
+
use_erb = true
|
154
|
+
else
|
155
|
+
use_erb = false
|
156
|
+
end
|
157
|
+
|
158
|
+
# TODO(sissel): if this is a directory, create it.
|
159
|
+
# TODO(sissel): if this a file, copy it.
|
160
|
+
if File.directory?(source)
|
161
|
+
FileUtils.mkdir_p(localpath) unless File.directory?(localpath)
|
162
|
+
else
|
163
|
+
localdir = File.dirname(localpath)
|
164
|
+
FileUtils.mkdir_p(localdir) unless File.directory?(localdir)
|
165
|
+
|
166
|
+
if @create_only.include?(originalpath) and File.exists?(localpath)
|
167
|
+
@logger.info("Skipping existing file due to 'create_only': #{localpath}")
|
168
|
+
else
|
169
|
+
if use_erb
|
170
|
+
erb = ERB.new(File.read(source))
|
171
|
+
@logger.info("Generating ", :source => source, :target => localpath)
|
172
|
+
File.open(localpath, "w+") do |fd|
|
173
|
+
# Make all ivars available as names in the templates, so I can just do
|
174
|
+
# <%= name %> instead of <%= @name %>
|
175
|
+
ivar = IvarBinding.new(self)
|
176
|
+
fd.write(erb.result(ivar.binding))
|
177
|
+
end
|
178
|
+
else
|
179
|
+
@logger.info("Copying", :source => source, :target => localpath)
|
180
|
+
FileUtils.cp(source, localpath)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end # def sync
|
186
|
+
|
187
|
+
# Set the environment this thing is operating in.
|
188
|
+
#
|
189
|
+
# Valid environments are: :system, :user, :project, :path
|
190
|
+
#
|
191
|
+
# This helps hint batcave update operations so you can just update your user,
|
192
|
+
# project, etc, instead of everything at once.
|
193
|
+
def environment(env=nil)
|
194
|
+
if !env.nil?
|
195
|
+
@environment = env
|
196
|
+
end
|
197
|
+
return @environment
|
198
|
+
end # def environment
|
199
|
+
|
200
|
+
def thing
|
201
|
+
return @thing
|
202
|
+
end # def thing
|
203
|
+
|
204
|
+
def to_hash
|
205
|
+
return {
|
206
|
+
"args" => @args
|
207
|
+
}
|
208
|
+
end # def to_yaml
|
209
|
+
|
210
|
+
def execute
|
211
|
+
sync if @sync
|
212
|
+
end
|
213
|
+
|
214
|
+
def nosync
|
215
|
+
@sync = false
|
216
|
+
end # def nosync
|
217
|
+
|
218
|
+
# Metaprogramming to provide instance variables from a class as
|
219
|
+
# local-variable-like things in a binding. For use with ERB and such.
|
220
|
+
#
|
221
|
+
# This is a fun hack.
|
222
|
+
class IvarBinding
|
223
|
+
def initialize(object)
|
224
|
+
# Make a new Object subclass
|
225
|
+
klass = Class.new(Object)
|
226
|
+
|
227
|
+
# Find all instance variables in 'object' and make
|
228
|
+
# them methods in our new class. Each method will
|
229
|
+
# simply return that named instance variable.
|
230
|
+
object.instance_variables.each do |ivar|
|
231
|
+
meth = ivar.to_s.gsub("@", "")
|
232
|
+
klass.instance_eval do
|
233
|
+
define_method(meth) do
|
234
|
+
object.instance_variable_get(ivar)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Make a method that returns the binding for this class
|
240
|
+
klass.instance_eval do
|
241
|
+
define_method(:_binding) do
|
242
|
+
return binding
|
243
|
+
end
|
244
|
+
end
|
245
|
+
@instance = klass.new
|
246
|
+
end # def initialize
|
247
|
+
|
248
|
+
def binding
|
249
|
+
return @instance._binding
|
250
|
+
end # def binding
|
251
|
+
end # class IvarBinding
|
252
|
+
|
253
|
+
public(:initialize, :execute, :environment, :thing, :to_hash)
|
254
|
+
end # class BatCave::DSL
|
data/lib/batcave/main.rb
CHANGED
@@ -2,8 +2,19 @@ require "clamp"
|
|
2
2
|
require "batcave/namespace"
|
3
3
|
require "batcave/command/add"
|
4
4
|
require "batcave/command/update"
|
5
|
+
require "cabin"
|
5
6
|
|
6
7
|
class BatCave::Main < Clamp::Command
|
8
|
+
option ["-v", "--verbose"], :flag, "enable verbose logging" do
|
9
|
+
require "logger"
|
10
|
+
logger = Cabin::Channel.get("batcave")
|
11
|
+
p Cabin::Channel.get("batcave").object_id
|
12
|
+
p Cabin::Channel.get("batcave").object_id
|
13
|
+
p Cabin::Channel.get("batcave").object_id
|
14
|
+
logger.subscribe(Logger.new(STDOUT))
|
15
|
+
logger.level = :info
|
16
|
+
end
|
17
|
+
|
7
18
|
# Add something to your bat cave.
|
8
19
|
subcommand "add", "Add something to your batcave", BatCave::Command::Add
|
9
20
|
|
data/lib/batcave/namespace.rb
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
require "batcave/namespace"
|
2
|
+
require "batcave/support/git"
|
3
|
+
require "batcave/support/envpath"
|
4
|
+
require "yaml"
|
5
|
+
require "fileutils"
|
6
|
+
|
7
|
+
class BatCave::Store
|
8
|
+
include BatCave::Support::Git
|
9
|
+
include BatCave::Support::EnvPath
|
10
|
+
|
11
|
+
def lock(path, &block)
|
12
|
+
lockfile = "#{path}.lock"
|
13
|
+
File.open(lockfile, "a+") do |lockfd|
|
14
|
+
locked = lockfd.flock(File::LOCK_EX | File::LOCK_NB)
|
15
|
+
if !locked
|
16
|
+
info = lockfd.read
|
17
|
+
raise "Store is currently locked, cannot write to it. (info: #{info})"
|
18
|
+
end
|
19
|
+
lockfd.rewind
|
20
|
+
lockfd.write("pid:$$")
|
21
|
+
lockfd.flush
|
22
|
+
|
23
|
+
block.call
|
24
|
+
end
|
25
|
+
|
26
|
+
# This step is not required for flock(2) to work, but it should help to
|
27
|
+
# keep users from being confused.
|
28
|
+
File.delete(lockfile)
|
29
|
+
end # def lock
|
30
|
+
|
31
|
+
def store(dsl)
|
32
|
+
basedir = path(dsl.environment)
|
33
|
+
manifest_path = File.join(basedir, ".batcave", "manifest")
|
34
|
+
FileUtils.mkdir_p(File.join(basedir, ".batcave"))
|
35
|
+
|
36
|
+
lock(manifest_path) do
|
37
|
+
manifest = load(manifest_path)
|
38
|
+
# Store this thing.
|
39
|
+
manifest["things"][dsl.thing] = dsl.to_hash
|
40
|
+
|
41
|
+
# Write the manifest to a tmpfile and rename it.
|
42
|
+
tmpfile = manifest_path + ".tmp"
|
43
|
+
File.open(tmpfile, "w") do |tmp|
|
44
|
+
tmp.write(manifest.to_yaml)
|
45
|
+
tmp.flush
|
46
|
+
end
|
47
|
+
File.rename(tmpfile, manifest_path)
|
48
|
+
end
|
49
|
+
end # def store
|
50
|
+
|
51
|
+
def each(environment, &block)
|
52
|
+
basedir = path(environment)
|
53
|
+
manifest_path = File.join(basedir, ".batcave", "manifest")
|
54
|
+
manifest = load(manifest_path)
|
55
|
+
manifest["things"].each do |thing|
|
56
|
+
yield thing
|
57
|
+
end
|
58
|
+
end # def each
|
59
|
+
|
60
|
+
def load(manifest_path)
|
61
|
+
manifest = {}
|
62
|
+
if File.exists?(manifest_path)
|
63
|
+
fd = File.new(manifest_path, "a+")
|
64
|
+
# Load the current manifest so we can modify it
|
65
|
+
manifest = YAML.load(fd.read)
|
66
|
+
manifest = {} if !manifest
|
67
|
+
fd.close
|
68
|
+
end
|
69
|
+
|
70
|
+
# Handle empty manifest. (YAML.load returns false for empty files)
|
71
|
+
manifest["things"] ||= {}
|
72
|
+
return manifest
|
73
|
+
end # def manifest
|
74
|
+
end # class BatCave::Store
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "batcave/namespace"
|
2
|
+
|
3
|
+
module BatCave::Support::EnvPath
|
4
|
+
def path(environment)
|
5
|
+
case environment.to_sym
|
6
|
+
when :user
|
7
|
+
return ENV["HOME"]
|
8
|
+
when :project
|
9
|
+
return project_root
|
10
|
+
else
|
11
|
+
raise "Unsupported environment '#{environment}'"
|
12
|
+
end
|
13
|
+
end # def path
|
14
|
+
end
|
data/lib/batcave/support/git.rb
CHANGED
@@ -2,10 +2,12 @@ require "batcave/namespace"
|
|
2
2
|
|
3
3
|
module BatCave::Support::Git
|
4
4
|
def project_root
|
5
|
+
return @project_root if instance_variable_defined?(:@project_root)
|
5
6
|
root = %x{git rev-parse --show-toplevel}.chomp
|
6
7
|
if $?.exitstatus != 0
|
7
8
|
raise "'git rev-parse --show-toplevel' failed. No project root found. Is this in a git clone?"
|
8
9
|
end
|
10
|
+
@project_root = root
|
9
11
|
return root
|
10
12
|
end # def project_root
|
11
13
|
end # class BatCave::Support::Git
|
data/things/go/self/THING
CHANGED
data/things/ruby/self/THING
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
files = %x{git ls-files}
|
3
|
+
|
4
|
+
spec.name = "<%= name %>"
|
5
|
+
spec.version = "0.0.1"
|
6
|
+
spec.summary = "<%= name %>"
|
7
|
+
spec.description = "<%= name %>"
|
8
|
+
spec.license = "none chosen yet"
|
9
|
+
|
10
|
+
# Note: You should set the version explicitly.
|
11
|
+
spec.add_dependency "cabin", ">0" # for logging. apache 2 license
|
12
|
+
spec.files = files
|
13
|
+
spec.require_paths << "lib"
|
14
|
+
spec.bindir = "bin"
|
15
|
+
|
16
|
+
spec.authors = ["<%= %x{git config --global user.name}.chomp %>"]
|
17
|
+
spec.email = ["<%= %x{git config --global user.email}.chomp %>"]
|
18
|
+
#spec.homepage = "..."
|
19
|
+
end
|
20
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: batcave
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-02-
|
12
|
+
date: 2012-02-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: clamp
|
16
|
-
requirement: &
|
16
|
+
requirement: &4815200 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,8 +21,8 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
-
description:
|
24
|
+
version_requirements: *4815200
|
25
|
+
description: Experiments in tools, boilerplatery, debugging, etc.
|
26
26
|
email: jls@semicomplete.com
|
27
27
|
executables:
|
28
28
|
- dk
|
@@ -35,11 +35,16 @@ files:
|
|
35
35
|
- README.md
|
36
36
|
- batcave.gemspec
|
37
37
|
- bin/dk
|
38
|
+
- lib/batcave/action/add.rb
|
38
39
|
- lib/batcave/command/add.rb
|
39
40
|
- lib/batcave/command/update.rb
|
41
|
+
- lib/batcave/dsl.rb
|
40
42
|
- lib/batcave/main.rb
|
41
43
|
- lib/batcave/namespace.rb
|
44
|
+
- lib/batcave/store.rb
|
45
|
+
- lib/batcave/support/envpath.rb
|
42
46
|
- lib/batcave/support/git.rb
|
47
|
+
- things/dotfiles/self/THING
|
43
48
|
- things/go/self/THING
|
44
49
|
- things/go/self/src/{name}/{name}.go
|
45
50
|
- things/ruby/self/Gemfile
|
@@ -47,6 +52,8 @@ files:
|
|
47
52
|
- things/ruby/self/test/all.rb
|
48
53
|
- things/ruby/self/test/docs.rb
|
49
54
|
- things/ruby/self/test/testing.rb
|
55
|
+
- things/ruby/self/{name}.gemspec.erb
|
56
|
+
- things/user.sh/THING
|
50
57
|
homepage: https://github.com/jordansissel/batcave
|
51
58
|
licenses: []
|
52
59
|
post_install_message:
|
@@ -67,9 +74,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
74
|
version: '0'
|
68
75
|
requirements: []
|
69
76
|
rubyforge_project:
|
70
|
-
rubygems_version: 1.8.
|
77
|
+
rubygems_version: 1.8.16
|
71
78
|
signing_key:
|
72
79
|
specification_version: 3
|
73
|
-
summary:
|
80
|
+
summary: Experiments in tools, boilerplatery, debugging, etc.
|
74
81
|
test_files: []
|
75
|
-
has_rdoc:
|