require 'logging' Logging.init :debug, :trace, :verbose, :info, :warn, :error include Logging.globally require 'optparse' require 'erb' require 'yaml' require 'sequel' require 'sequel/extensions/migration' require 'mj/logging' require "build-tool/errors" require 'build-tool/recipe' require 'build-tool/singleton' require 'build-tool/cfg/parser' $noop = false module BuildTool # # The application class # class Application < Singleton attr_reader :application_root attr_reader :cli attr_reader :database attr_reader :local_settings_file_path attr_reader :name def recipe return @recipe if @recipe configuration @recipe end def initialize( name, application_root ) super() @application_root = Pathname.new( application_root ).expand_path @database = nil @name = File.basename( name ).sub( "-build", "" ) @recipe = nil @local_settings_file_path = local_configuration_dir.join("#{@name}.yaml") end # Load the configuration def configuration return @configuration if @configuration # First load the settings file file = local_settings_file_path logger.debug "Loading settings file #{file}" raise ConfigurationError, "Configuration File #{file} does not exists!" if !file.exist? @settings = YAML.load_file( file ) # Then load the recipe logger.debug "Loading configuration for #{name}" @recipe = BuildTool::Recipe.new( @settings['RECIPE'] || name ) @configuration = @recipe.load( name, @settings['SETTINGS'] ) end # Return the local configuration dir. Makes sure the directory exists # before returning it def local_configuration_dir return @local_configuration_dir if @local_configuration_dir @local_configuration_dir = Pathname.new( "~/.build-tool" ).expand_path if !@local_configuration_dir.exist? FileUtils.mkdir_p( @local_configuration_dir ) end @local_configuration_dir end # Open the database # # @return [Object] The database handle def open_database path = local_configuration_dir.join( "#{name}.db" ) logger.debug "Opening the database #{path}." @database = Sequel.connect( "sqlite:///#{path.to_s}" ) @database.logger= MJ::Logging::LoggerAdapter.new( 'db' ) # Try to upgrade the database if necessary Sequel::Migrator.apply( @database, application_root.join( 'db/migrations' ) ) end # Close the database def close_database @database.disconnect path = local_configuration_dir.join( "#{name}.db" ) logger.debug "Closing the database #{path}." end # Get the database handle def database @database end def main( args ) # The configuration is loaded on demand to make it possible to execute command that # don't need a configuration. Help for example. # Initialize the database. open_database() begin # Initialize the shell require 'build-tool/commands' @cli = Commands::Shell.new # Load the commands. This has to be done AFTER the database is opened because # sequel expects that there is a database before a Sequel::Model is created. # # And the commands require all that database related stuff. @cli.load_commands( @application_root.join( "lib/build-tool/commands" ), BuildTool::Commands ) # Execute the given command (or start the cli) @cli.execute( args ) ensure # Make sure the database is closed correctly close_database end end end # class Application end # module BuildTool def BuildTool.main(name, args, root_directory) begin # Setup logging Logging.logger.root.level = :debug # Special case for the configurationParser. It is very verbose if active. Logging.logger['BuildTool::Cfg::Parser'].level = :info Logging.logger.root.appenders = Logging.appenders.stdout( :layout => MJ::Logging::BasicLayout.new(), :level => :info) # Create the application app = BuildTool::Application.new( name, root_directory ) # Execute the application return app.main( args ) rescue Interrupt => e logger.info "User interrupt!" return 0 rescue BuildTool::Error => e logger.error e.message logger.verbose e.backtrace.join("\n") return -1 rescue Exception => e logger.error e return -1 end end # main()