Wednesday, February 07, 2007

open source and deployment

I apologize in advance for those of you who don't particularly care to read any code.

But one of the things I'm going to try to do while working on my startup is to post as many code snippets as possible.

Why? Because I'm a believer in open source. My startup is not focussed around building great deployment tools. By posting my code, I'm helping other people who also need to solve similar issues. At the same time, there's a good chance that someone smarter than me will read this and tell me a much simpler or more elegant way of achieving the same result.

If it starts taking up too much of my time, I might post less stuff. If I start to see value in posting code, I might try to do more of it. Either way, I'm running as fast as I can to build an amazing product.

The following script should help me to write common config files and test them out on my mac first and then deploy them with no changes. See the usage function for more info.

Oh - and I'm still a newbie ruby programmer. So if you have code suggestions, or if there's a tool that already does what this does, then let me know. I have no qualms about throwing away my code and using someone else's. Less code = less bugs.


#!/usr/bin/env ruby
require 'yaml'

def usage( msg )
STDERR.puts msg
STDERR.puts <<EOF

USAGE
generate <config_file> <stage> files..

DESCRIPTION
Given the following config.yml:
--
test:
base: path1
user: name1
prod:
base: path2
user: name2
--
and a lighttpd.conf:

--
server.username = "$user$"
server.document-root = "$base$/public_html"
--

% generate config.yml test lighttpd.conf
will create the file lighttpd.conf.generated which looks like:

--
server.username = "name1"
server.document-root = "path1/public_html"
--

EOF
exit
end

usage() if( ARGV.size < 3 )

config_file = ARGV.shift
stage = ARGV.shift

usage( "config file #{config_file} not found. " ) unless File.exists?( config_file )

all_config = YAML.load_file( config_file )

unless( all_config.has_key?( stage ) )
usage("config file #{config_file} doesn't specify stage '#{stage}'.")
end

config = all_config[stage]

# build up the regexp to match all identifiers:
regexp_string = '('
config.each_key { |id|
regexp_string += '\$' + id + '\$|'
}
regexp_string.chop!
regexp_string += ')'
matcher = Regexp.new( regexp_string )


# Iterate through each file
ARGV.each { |filename|
unless( File.exists?( filename ) )
STDERR.puts "file '#{filename}' not found. continuing"
next
end
g_filename = filename + '.generated'
if( File.exists?( g_filename ) )
# TODO: move old file out of the way instead..
STDERR.puts "file '#{g_filename}' exists. OVERWRITING!"
end

generated = File.new( g_filename, "w" )
# check each line
File.open(filename).each_line{ |line|
# replace each instance of a token
line.gsub!( matcher ) { |match|
id = match[1..(match.size - 2)]
# with the value specified in the config file
config[id]
}
generated.puts( line )
}
}

3 comments:

kireet said...

when your startup goes big time, will you still remember the little people? Even if such a person (not saying who), didn't so much as help you but rather mocked you unmercifully?

Kaushik said...

Why not have ruby as the templating language as well using ERB templates?

That will let you parameterize the output on things besides the stage name. The substituted values can even be dynamically computed rather than statically provided values (if that kind of thing makes sense for your use case).

-K

hemantbhanoo said...

Thanks Kaushik. Was just thinking about that last night; it'd give me the ability to do things like

<% unless stage=='test' %>
some stuff
<% end %>

which I can't do right now.

For the moment, I may not need that amount of flexibility; but it's probably a good solution.

Since I'm not gonig to take the time to look into it just yet, do you know how one sets up the environment for an ERB template?