summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Parri <simonparri@ganzeria.com>2023-05-01 14:54:29 -0500
committerSimon Parri <simonparri@ganzeria.com>2023-05-01 14:54:29 -0500
commit57cf3f022a636dbb9ef7143435ebe16d4ed34366 (patch)
tree182179d0a8635496eda780e172fbb66aee3d4c72
downloadsteeplejack-master.tar.gz
steeplejack-master.zip
Initialize repositoryHEADmaster
-rw-r--r--.gitignore1
-rw-r--r--README.org56
-rwxr-xr-xsteeplejack94
3 files changed, 151 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b25c15b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*~
diff --git a/README.org b/README.org
new file mode 100644
index 0000000..e71b44a
--- /dev/null
+++ b/README.org
@@ -0,0 +1,56 @@
+* Steeplejack
+A generic scaffolding system.
+** About
+#+begin_quote
+A steeplejack is a craftsman who scales buildings, chimneys, and
+church steeples to carry out repairs or maintenance.
+
+Steeplejacks erect ladders on church spires, industrial chimneys,
+cooling towers, bell towers, clock towers, or any other high
+structure.
+#+end_quote
+
+Like a steeplejack, ~steeplejack~ is a diligent worker that can erect
+scaffolding for any project you need.
+** Usage
+~steeplejack~ can be taught how to erect scaffolding by setting the
+=STEEPLEJACK_DIR= (defaults to ~~/.steeplejack~)environment variable and
+putting template directories there. A template directory contains a
+~_scaffold.yml~ and various ~.erb~ files. ~_scaffold.yml~ should at least
+contain a =params= or =parameters= property. It should be a list of
+property names. Each parameter will be read when the scaffold is
+erected. ~_scaffold.yml~ can also contain an =erb_options= parameter
+that, when present, is used to set the =trim_mode= for ERB. See the [[https://docs.ruby-lang.org/en/master/ERB.html][ERB
+documentation]] for details.
+
+When the scaffold is erected, each ~.erb~ file in the scaffold’s
+directory is evaluated, and the output is written to the corresponding
+file in the target directory. For example, let’s take a scaffold
+directory ~scaf~. We will deploy it to ~targ~. Let’s say that ~scaf~ has
+three files in it:
++ ~scaf/_scaffold.yml~
++ ~scaf/a.erb~
++ ~scaf/b.erb~
+Let’s also say that ~_scaffold.yml~ contains the text
+#+begin_src yaml
+ properties:
+ - foo
+ - bar
+#+end_src
+When deploying ~scaf~ to ~targ~, you will be prompted for =foo= and =bar=.
+Then ~scaf/a.erb~ and ~scaf/b.erb~ will be evaluated, and the results
+written to ~targ/a~ and ~targ/b~.
+** Configuration
+When ~steeplejack~ starts up, it loads ~$STEEPLEJACK_DIR/init.rb~. The
+most notable thing to have ~init.rb~ is ~add_alias~. An example:
+#+begin_src ruby
+ add_alias sc: :scaffold,
+ ls: :list,
+ i: :info
+#+end_src
+
+Note that ~add_alias~ is not the same as ~alias~. ~alias~ is a built-in
+Ruby feature, whereas ~add_alias~ is ~steeplejack~-specific; using ~alias~
+will not make the alias available as a ~steeplejack~ subcommand.
+** License
+~steeplejack~ is published under the [[http://gnu.org/licenses/gpl-3.0.html][GPLv3]] (or any later version).
diff --git a/steeplejack b/steeplejack
new file mode 100755
index 0000000..fde9259
--- /dev/null
+++ b/steeplejack
@@ -0,0 +1,94 @@
+#!/usr/bin/ruby
+
+%w[erb yaml readline fileutils]
+ .map &(method :require)
+
+STEEPLEJACK_DIR = File.expand_path(ENV["STEEPLEJACK_DIR"]&.chomp("/") || "~/.steeplejack")
+STEEPLEJACK_RC = "#{STEEPLEJACK_DIR}/init.rb"
+
+def erb(from:, to:, params:, options:)
+ File.write to, ERB.new(File.read(from), trim_mode: options).result_with_hash(params)
+end
+
+def get_params(params)
+ Hash[params.zip params.map {|p| Readline.readline p+": "}]
+end
+
+$commands = {
+ scaffold: "Deploy a scaffold",
+ list: "List available scaffolds",
+ info: "Show info on scaffold",
+ help: "Show this help"
+}
+
+def add_alias(hash)
+ hash.each do |from, to|
+ Kernel.alias_method from, to
+ $commands[from] = "Alias for #{to}"
+ end
+end
+
+def scaffold(name, dest)
+ puts "### Deploying \"#{name}\" to \"#{dest}\" ###"
+
+ orig = "#{STEEPLEJACK_DIR}/#{name}"
+ yaml = "#{orig}/_scaffold.yml"
+ recipe = YAML.load_file yaml
+
+ files = Dir.chdir(orig) do
+ Dir["**/**.erb"].map {|s| s.chomp ".erb" }
+ end
+
+ # We want to check the files as soon as possible
+ files.each do |file|
+ raise "File exists: #{dest}/#{file}" if File.exist? "#{dest}/#{file}"
+ end
+
+ params = get_params (recipe["parameters"] || recipe["params"])
+ options = recipe["erb_options"]
+
+ FileUtils.mkdir_p dest
+
+ files.each do |file|
+ puts "Writing \"#{file}\""
+ erb from: "#{orig}/#{file}.erb", to: "#{dest}/#{file}",
+ params: params, options: options
+ end
+
+ puts "\"#{name}\" deployed successfully"
+end
+
+def list
+ if File.exist? STEEPLEJACK_DIR
+ (Dir.children(STEEPLEJACK_DIR)
+ .filter &(File.method :directory?))
+ .map {|p| p.chomp("/").split("/")[-1] }
+ .each &(method :puts)
+ end
+end
+
+def info(name)
+ puts File.read "#{STEEPLEJACK_DIR}/#{name}/_scaffold.yml"
+end
+
+def help
+ puts "#{$0}: generic scaffolding"
+ puts "usage: #{$0} SUBCOMMAND ARGS"
+ puts
+ puts "Available subcommands:"
+ $commands.each {|n, d| puts " #{n}: #{d || "Not documented"}" }
+end
+
+def main
+ load STEEPLEJACK_RC if File.exist? STEEPLEJACK_RC
+ sub = ARGV[0]
+ if ["--help", "-h", nil].include? sub
+ help
+ elsif $commands.keys.include? sub.to_sym
+ send sub, *ARGV[1..-1]
+ else
+ raise "No subcommand \"#{sub}\""
+ end
+end
+
+main if $0 == __FILE__