Skip to content

Instantly share code, notes, and snippets.

@laser
Last active November 3, 2022 22:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laser/c83699ccb24ecb320268f3c2864c5f07 to your computer and use it in GitHub Desktop.
Save laser/c83699ccb24ecb320268f3c2864c5f07 to your computer and use it in GitHub Desktop.
A Procfile formatting script, written in Ruby
#!/usr/bin/env ruby
require 'optparse'
def get_procfile_lines
STDIN.read.split("\n")
end
def get_process_type_max_width(lines)
processes = []
lines.each do |line|
processes << line if line.include?(":") && !line.start_with?("#")
end
max = 0
processes.each do |line|
left, _ = line.split(":")
max = left.size if left.size > max
end
max
end
def ignore_line?(options, line)
return true if line.start_with?("#") || line.empty?
parts = line.split(":")
# if the user has requested that we ignore the line, simply write it to stdout
return true if !options[:ignored_process_types].nil? && options[:ignored_process_types].include?(parts[0])
false
end
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: cat ./Procfile.dev | procfile_formatter.rb [options]"
opts.on('-i', '--ignore NAME1,NAME2,NAME3', "ignored process types, e.g. 'web' or 'sidekiq'") { |v| options[:ignored_process_types] = v.split(",") }
end.parse!
lines = get_procfile_lines
process_type_max_width = get_process_type_max_width(lines)
lines.each do |line|
if ignore_line?(options, line)
puts line
next
end
# note that there may be more than one colon in line
parts = line.split(":")
# construct the command portion of the line
command = parts[1..].join(":").strip.gsub(/\s+/, " ")
# an array of environment variable-setting strings, e.g. ["FOO=1", "BAR=abc"]
environment_variables = []
# all other parts of the command, e.g. ["env", "bundle", "exec", "rake"]
others = []
command.split(" ").each do |part|
# if the part isn't obviously an environment variable, append it to 'others'
unless part.include?("=") && part.split("=")[0] == part.split("=")[0].upcase
others << part
next
end
environment_variables << part
end
left = "#{parts[0]}:".ljust(process_type_max_width+1, " ")
right = "#{others.join(" ")} #{environment_variables.sort.join(" ")}"
puts "#{left} #{right}".strip
end

Procfile Formatter

Usage

Given the following Procfile:

# /tmp/Procfile.example

web: bundle exec puma -C ./config/puma.rb

# Resque Scheduler
resque_scheduler: env TERM_CHILD=1 RESQUE_TERM_TIMEOUT=10 bundle exec rake environment resque:scheduler

# RabbitMQ message consumption
foo_processor: env USE_SHARED_DB=true JOB_KLASS=FooJob QUEUE_NAME=foo_jobs bundle exec rake rabbitmq_to_resque_reader BINDING_KEYS='meta.foo.#'
bar_processor: env USE_SHARED_DB=true QUEUE_NAME=bar_jobs JOB_KLASS=BarJob  bundle exec rake rabbitmq_to_resque_reader BINDING_KEYS='meta.bar.#'

# Resque worker processes
foo_worker: env bundle exec rake environment resqutils:work QUEUE=foo_jobs RESQUE_TERM_TIMEOUT=60 TERM_CHILD=1 USE_DATABASE=false
bar_worker:      env bundle exec rake environment resqutils:work QUEUE=bar_jobs TERM_CHILD=1 USE_DATABASE=false RESQUE_TERM_TIMEOUT=60 

When you run the following command:

$ cat /tmp/Procfile.example | ./formatter.rb

...you will see the following output:

# /tmp/Procfile.example

web:              bundle exec puma -C ./config/puma.rb

# Resque Scheduler
resque_scheduler: env bundle exec rake environment resque:scheduler RESQUE_TERM_TIMEOUT=10 TERM_CHILD=1

# RabbitMQ message consumption
foo_processor:    env bundle exec rake rabbitmq_to_resque_reader BINDING_KEYS='meta.foo.#' JOB_KLASS=FooJob QUEUE_NAME=foo_jobs USE_SHARED_DB=true
bar_processor:    env bundle exec rake rabbitmq_to_resque_reader BINDING_KEYS='meta.bar.#' JOB_KLASS=BarJob QUEUE_NAME=bar_jobs USE_SHARED_DB=true

# Resque worker processes
foo_worker:       env bundle exec rake environment resqutils:work QUEUE=foo_jobs RESQUE_TERM_TIMEOUT=60 TERM_CHILD=1 USE_DATABASE=false
bar_worker:       env bundle exec rake environment resqutils:work QUEUE=bar_jobs RESQUE_TERM_TIMEOUT=60 TERM_CHILD=1 USE_DATABASE=false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment