module Oye

Constants

DEFAULT_ENVIRONMENTS
DEFAULT_WATCH_INTERVAL
LOG_FORMAT
VERSION

Public Class Methods

build_app(dir) click to toggle source
# File lib/oye.rb, line 179
def build_app(dir)
  Dir.chdir(dir) do
    %x(bundle)

    if rails_app?(dir)
      %x(RAILS_ENV=#{@environment} rails db:migrate)

      if @environment == 'production'
        %x(RAILS_ENV=production rails assets:{clean,precompile})
      end
    elsif jekyll_app?(dir)
      %x{jekyll build}
    end
  end

  log(dir, status: :info, message: "Built app")
rescue => e
  log(dir, status: :warn, message: "#{__method__.to_s} (#{e.message})")
end
config_oye() click to toggle source

print oye config files

# File lib/oye.rb, line 250
def config_oye
  puts "Config file: #{oye_config}"
  puts "Log file: #{oye_logfile}"
  puts "PID file: #{oye_pidfile}"
  exit
end
help() click to toggle source
# File lib/oye.rb, line 86
    def help
      puts <<~eos
      -c, --config            print config, log and pid files
      -e, --environment ENV   environment for the app server [development|production|test] (default production)
      -h, --help              print this message
      -i, --info [PATTERN]    print info of repos matching PATTERN (default .*)
      -l, --list              print monitored repos
      -p, --port PORT         port for app server
      -r, --restart           restart oye
      -s, --stop              stop oye
      -t, --time SECS         time interval for repo monitoring (default 5)
      -v, --version           print oye version
      eos
      exit
    end
info() click to toggle source

print watched repos

# File lib/oye.rb, line 258
def info
  not_found = []
  @repos.keys.each do |repo|
    next unless repo.match?(@pattern)

    unless File.exists?(repo)
      not_found << repo
      next
    end

    ftime=File.stat(repo).ctime

    # use same format than "ls -l"
    time_format =
      if ftime.year == Time.now.year
        "%b %d %H:%M"
      else
        "%b %d  %Y"
      end

    puts "#{ftime.strftime(time_format)} #{repo}"
  end

  unless not_found.empty?
    puts "\nCould not find repos:", not_found
  end

  exit
end
list() click to toggle source
# File lib/oye.rb, line 241
def list
  @repos.each do |origin, clones|
    puts origin
    clones.values.flatten.map {|clone| puts clone.prepend "  - "}
  end
  exit
end
log(repo, options = {}) click to toggle source
# File lib/oye.rb, line 166
def log(repo, options = {})
  log_message = [
                  Time.now.strftime(LOG_FORMAT),
                  repo,
                  "[#{options[:status].to_s.upcase}]",
                  "\"#{options[:message]}\""
                ].join(' ')

  File.open(oye_logfile, 'a') do |f|
    f.puts log_message
  end
end
monitor() click to toggle source

main method

# File lib/oye.rb, line 103
def monitor
  %w(TERM INT).each do |signal|
    trap(signal) do
      stop_oye
      exit
    end
  end

  # get change-times of origin repos
  @repos.keys.filter_map do |origin|
    next unless File.exists?(origin)
    @repos[origin]['stat'] = File.stat(origin).ctime
  end

  pid = fork do
    begin
      # initial build and start of apps
      @repos.values.flatten.each do |app|
        app['clones'].each do |clone|
          if !File.exists?(clone)
            log(clone, status: :warn, message: "Could not find repo")
            next
          end

          build_app(clone)

          start_app(clone)
        end
      end

      # loop that watches for changes in origin repos
      loop do
        repos_dup = @repos
        repos_dup.keys.each do |origin|
          unless File.exists?(origin)
            log(origin, {status: :warn, message: "Could not find repo"})
            next
          end

          repos_dup[origin]['clones'].each do |clone|
            unless File.exists?(clone)
              log(clone, {status: :warn, message: "Could not find repo"})
              next
            end

            unless @repos[origin]['stat'] == File.stat(origin).ctime
              @repos[origin]['stat'] = File.stat(origin).ctime
              update_app(clone)
              build_app(clone)
              restart_app(clone)
            end
          end
        end
        sleep @interval
      end
    end
  end

  File.open(oye_pidfile, 'w') { |f| f.puts pid }

  ::Process.detach pid
end
read_config() click to toggle source
# File lib/oye.rb, line 80
def read_config
  YAML.load(File.open(oye_config)).each do |origin, clones|
    @repos[origin] = {'clones' => clones}
  end
end
restart_app(clone) click to toggle source
# File lib/oye.rb, line 304
def restart_app(clone)
  stop_app(clone)
  start_app(clone)
end
restart_oye() click to toggle source

TODO implement

# File lib/oye.rb, line 301
def restart_oye
end
start(args) click to toggle source
# File lib/oye.rb, line 17
def start(args)
  help if(args.include?('-h') or args.include?('--help'))
  version if(args.include?('-v') or args.include?('--version'))
  restart_oye if(args.include?('-r') or args.include?('--restart'))
  config_oye if(args.include?('-c') or args.include?('--config'))

  if(args.include?('-s') or args.include?('--stop'))
    stop_oye
    exit
  end

  FileUtils.mkdir_p(oyedir)

  @repos = {}
  read_config

  list if(args.include?('-l') or args.include?('--list'))

  if(args.include?('-i') or args.include?('--info'))
    args.map! {|a| a == '--info' ? '-i' : a}
    _, @pattern = args.slice(args.index('-i'),2)
    @pattern = @pattern.nil? ? /.*/ : Regexp.new(@pattern)
    info
  end

  @interval = DEFAULT_WATCH_INTERVAL
  if(args.include?('-t') or args.include?('--time'))
    args.map! {|a| a == '--time' ? '-t' : a}
    _, @interval = args.slice(args.index('-t'),2)
    @interval = @interval.to_i
    unless @interval > 0
      puts "Interval must a positive integer"
      exit
    end
  end

  if(args.include?('-p') or args.include?('--port'))
    args.map! {|a| a == '--port' ? '-p' : a}
    _, @port = args.slice(args.index('-p'),2)
    @port = @port.to_i
    unless @port > 0
      puts "Port must a positive integer"
      exit
    end
  end

  @environment = 'production'
  if(args.include?('-e') or args.include?('--environment'))
    args.map! {|a| a == '--environment' ? '-e' : a}
    _, @environment = args.slice(args.index('-e'),2)
    unless DEFAULT_ENVIRONMENTS.include?(@environment)
      puts "Specify a supported environment for -e option"
      exit
    end
  end

  @default_unicorn_options = "-E #{@environment} -D"
  @default_unicorn_options << " -l #{@port}" if @port
  @default_jekyll_options = "-B"

  monitor
end
start_app(dir) click to toggle source
# File lib/oye.rb, line 213
def start_app(dir)
  Dir.chdir(dir) do
    if rails_app?(dir)
      unicorn_options = @default_unicorn_options << " -c #{unicorn_file(dir)}"

      system("unicorn_rails #{unicorn_options}", [:out, :err] => File::NULL)
    elsif jekyll_app?(dir)
      if @environment == 'development'
        jekyll_options = @default_jekyll_options << " -s #{dir} -d #{dir}/_site"

        system("jekyll serve #{jekyll_options}", [:out, :err] => File::NULL)
      end
    end
  end

  log(dir, status: :info, message: "Started app")
rescue => e
  log(dir, status: :warn, message: "#{__method__.to_s} (#{e.message})")
end
stop_app(dir) click to toggle source
# File lib/oye.rb, line 199
def stop_app(dir)
  if rails_app?(dir)
    if File.exists?(app_pid_file(dir))
      Process.kill 'TERM', app_pid(dir)
    end
  elsif jekyll_app?(dir)
    if @environment == 'development'
      %x(pkill -f jekyll)
    end
  end
rescue => e
  log(dir, status: :warn, message: "#{__method__.to_s} (#{e.message})")
end
stop_oye() click to toggle source
# File lib/oye.rb, line 293
def stop_oye
  Process.kill 'TERM', oye_pid
  FileUtils.rm_f(oye_pidfile)
  log(oye_pidfile, status: :info, message: "Stopped oye")
rescue Errno::ENOENT
end
update_app(dir) click to toggle source
# File lib/oye.rb, line 233
def update_app(dir)
  system("git -C #{dir} pull", [:out, :err] => File::NULL)

  log(dir, status: :info, message: "Pulled from origin")
rescue => e
  log(dir, status: :warn, message: "#{__method__.to_s} (#{e.message})")
end
version() click to toggle source
# File lib/oye.rb, line 288
def version
  puts "oye #{VERSION}"
  exit
end

Private Class Methods

gemfile(dir) click to toggle source
# File lib/oye.rb, line 323
def gemfile(dir)
  File.join(dir, "Gemfile.lock")
end
oye_config() click to toggle source

file with the name of repos that oye should watch

# File lib/oye.rb, line 349
def oye_config
  "#{oyedir}/oye.yml"
end
oye_logfile() click to toggle source

log oye actions

# File lib/oye.rb, line 354
def oye_logfile
  "#{oyedir}/oye.log"
end
oyedir() click to toggle source
# File lib/oye.rb, line 344
def oyedir
  "#{ENV['HOME']}/.oye"
end
port_open?(port) click to toggle source

return true if the port is open else false

# File lib/oye.rb, line 328
def port_open?(port)
  begin
    Timeout::timeout(1) do
      begin
        s = TCPServer.new(port)
        s.close
        return true
      rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::EADDRINUSE
        return false
      end
    end
  rescue Timeout::Error
  end
  return false
end