class Externals::Ext

Attributes

path_calculator[RW]

Public Class Methods

new(options = {}) click to toggle source
Calls superclass method
# File lib/externals/ext.rb, line 311
    def initialize options = {}
      super()

      @configuration = nil
      @projects = nil

      scm = configuration['.']
      scm = scm['scm'] if scm
      scm ||= options[:scm]

      type = configuration['.']
      type = type['type'] if type

      type ||= options[:type]

      if type
        install_project_type type
      else
        possible_project_types = self.class.project_types.select do |project_type|
          self.class.project_type_detector(project_type).detected?
        end

        if possible_project_types.size > 1
          raise "We found multiple project types that this could be: #{possible_project_types.join(',')}
Please use
 the --type option to tell ext which to use."
        else
          possible_project_types.each do |project_type|
            install_project_type project_type
          end
        end
      end
    end
new_opts(main_options, sub_options) click to toggle source
# File lib/externals/ext.rb, line 126
def self.new_opts main_options, sub_options
  opts = OptionParser.new(
    "ext [OPTIONS] <command> [repository] [-b <branch>] [path]"
  )
  opts.summary_indent = '  '
  opts.summary_width = 24
  summary_width = 53

  project_classes.each do |project_class|
    project_class.fill_in_opts(opts, main_options, sub_options,
      :summary_width => summary_width)
  end

  opts.on("--type TYPE", "-t TYPE",
    String,
    *"The type of project the main project is.
      For example, 'rails'.".lines_by_width(summary_width)
  ) {|type| sub_options[:type] = main_options[:type] = type}
  opts.on("--scm SCM", "-s SCM",
    String,
    *"The SCM used to manage the main project.  For example, '--scm svn'.".lines_by_width(summary_width)
  ) {|scm| sub_options[:scm] = main_options[:scm] = scm}
  opts.on("--branch BRANCH", "-b BRANCH",
    String,
    *"The branch you want the
    subproject to checkout when doing 'ext install'".lines_by_width(summary_width)
  ) {|branch| sub_options[:branch] = main_options[:branch] = branch}
  opts.on("--revision REVISION", "-r REVISION",
    String,
    *"The revision you want the
    subproject to checkout when doing 'ext install'".lines_by_width(summary_width)
  ) {|revision| sub_options[:revision] = main_options[:revision] = revision}
  opts.on("--force_removal", "-f",
    String,
    *"When doing an uninstall of a subproject,
    remove it's files and subfolders, too.".lines_by_width(summary_width)
  ) {|branch| sub_options[:force_removal] = true}
  opts.on("--workdir DIR", "-w DIR", String, *"The working directory to execute commands from.  Use this if for some reason you
    cannot execute ext from the main project's directory (or if it's just inconvenient, such as in a script
    or in a Capistrano task)".lines_by_width(summary_width)) {|dir|
    raise "No such directory: #{dir}" unless File.exist?(dir) && File.directory?(dir)
    main_options[:workdir] = dir
  }
  opts.on(
    "--help", *"does the same as 'ext help'  If you use this with a command
    it will ignore the command and run help instead.".lines_by_width(summary_width)
  ) {main_options[:help] = true}
  opts.on("--version", *"Displays the version number of externals and then exits.
    Same as 'ext version'".lines_by_width(summary_width)) {
    main_options[:version] = true
  }
  opts
end
project_class(scm) click to toggle source
# File lib/externals/ext.rb, line 345
def self.project_class(scm)
  Externals.module_eval("#{scm.to_s.cap_first}Project", __FILE__, __LINE__)
end
project_classes() click to toggle source
# File lib/externals/ext.rb, line 349
def self.project_classes
  retval = []
  registered_scms.each do |scm|
    retval << project_class(scm)
  end

  retval
end
project_type_detector(name) click to toggle source
# File lib/externals/ext.rb, line 762
def self.project_type_detector name
  Externals.module_eval("#{name.classify}Detector", __FILE__, __LINE__)
end
project_type_files() click to toggle source
# File lib/externals/ext.rb, line 116
def self.project_type_files
  project_types.map do |project_type|
    "#{File.join(PROJECT_TYPES_DIRECTORY, project_type)}.rb"
  end
end
project_types() click to toggle source
# File lib/externals/ext.rb, line 106
def self.project_types
  types = Dir.entries(PROJECT_TYPES_DIRECTORY).select do |file|
    file =~ /\.rb$/
  end

  types.map do |type|
    /^(.*)\.rb$/.match(type)[1]
  end
end
registered_scms() click to toggle source
# File lib/externals/ext.rb, line 241
def self.registered_scms
  return @registered_scms if @registered_scms
  @registered_scms ||= []

  scmdir = File.join(File.dirname(__FILE__), 'scms')

  Dir.entries(scmdir).each do |file|
    if file =~ /^(.*)_project\.rb$/
      @registered_scms << $1
    end
  end

  @registered_scms
end
run(*arguments) click to toggle source
# File lib/externals/ext.rb, line 180
def self.run *arguments
  main_options = {}
  sub_options = {}

  opts = new_opts main_options, sub_options

  args = opts.parse(arguments)

  unless args.nil? || args.empty?
    command = args[0]
    args = args[1..(args.size - 1)] || []
  end

  command &&= command.to_sym

  command = :help if main_options[:help]
  command = :version if main_options[:version]

  if !command || command.to_s == ''
    puts "hey... you didn't tell me what you want to do."
    puts "Try 'ext help' for a list of commands"
    exit
  end

  unless COMMANDS.index command
    puts "unknown command: #{command}"
    puts "for a list of commands try 'ext help'"
    exit
  end


  Dir.chdir(main_options[:workdir] || ".") do
    self.new(main_options).send(command, args, sub_options)
  end
end

Public Instance Methods

checkout(args, options) click to toggle source
# File lib/externals/ext.rb, line 663
def checkout args, options
  options ||= {}

  repository = args[0]
  path = args[1] || "."

  main_project = do_checkout_or_export repository, path, options, :checkout

  if path == "."
    path = main_project.name
  end

  Dir.chdir path do
    self.class.new({}).co [], {} #args, options
  end
end
configuration() click to toggle source
# File lib/externals/ext.rb, line 293
def configuration
  return @configuration if @configuration

  file_string = ''
  if File.exist?('.externals')
    open('.externals', 'r') do |f|
      file_string = f.read
    end
  end
  @configuration = Configuration::Configuration.new(file_string)
end
export(args, options) click to toggle source
# File lib/externals/ext.rb, line 680
def export args, options
  options ||= {}

  repository = args[0]
  path = args[1] || "."

  main_project = do_checkout_or_export repository, path, options, :export

  if path == "."
    path = main_project.name
  end

  Dir.chdir path do
    self.class.new({}).ex [], {} #args, options
  end
end
freeze(args, options) click to toggle source
# File lib/externals/ext.rb, line 380
def freeze args, options
  project = subproject_by_name_or_path(args[0])

  raise "No such project named #{args[0]}" unless project

  revision = args[1] || project.current_revision

  section = configuration[project.path]

  if section[:branch]
    branch = project.current_branch
    if branch
      section[:branch] = branch
    else
      section.rm_setting :branch
    end
  end
  section[:revision] = revision
  configuration.write '.externals'
  reload_configuration

  subproject_by_name_or_path(args[0]).up
end
help(args, options) click to toggle source
# File lib/externals/ext.rb, line 223
def help(args, options)
  puts "There's a tutorial available at http://nopugs.com/ext-tutorial\n\n"
  puts "#{self.class.new_opts({},{}).to_s}\n\n"

  puts "\nCommands that apply to the main project or the .externals file:"
  puts "#{MAIN_COMMANDS.join(', ')}\n\n"
  print_commands(MAIN_COMMANDS_HASH)

  puts "\nCommands that apply to the main project and all subprojects:"
  puts "#{FULL_COMMANDS.join(', ')}\n\n"
  print_commands(FULL_COMMANDS_HASH)

  puts "\nCommands that only apply to the subprojects:"
  puts "#{SHORT_COMMANDS.join(', ')}\n\n"
  print_commands(SHORT_COMMANDS_HASH)
end
init(args, options = {}) click to toggle source
# File lib/externals/ext.rb, line 697
    def init args, options = {}
      raise ".externals already exists" if File.exist?('.externals')

      scm = options[:scm]
      type = options[:type]

      if !scm
        possible_project_classes = self.class.project_classes.select do |project_class|
          project_class.detected?
        end

        raise "Could not determine this project's scm" if  possible_project_classes.empty?
        if possible_project_classes.size > 1
          raise "This project appears to be managed by multiple SCMs: #{
          possible_project_classes.map(&:to_s).join(',')}
Please explicitly declare the SCM (using --git or --svn, or, by creating .externals manually"
        end

        scm = possible_project_classes.first.scm
      end

      if !type
        possible_project_types = self.class.project_types.select do |project_type|
          self.class.project_type_detector(project_type).detected?
        end

        if possible_project_types.size > 1
          raise "We found multiple project types that this could be: #{possible_project_types.join(',')}
Please use the --type option to tell ext which to use."
        elsif possible_project_types.size == 0
          puts "WARNING: We could not automatically determine the project type.
          Be sure to specify paths when adding subprojects to your .externals file"
        else
          type = possible_project_types.first
        end
      end

      config = Configuration::Configuration.new_empty
      raise ".externals already exists" if File.exist?('.externals')

      config.add_empty_section '.'

      # TODO: If we are using subversion, we should warn about not setting a branch
      if scm == "svn"
        if options[:branch]
          config['.'][:repository] = SvnProject.extract_repository(
            SvnProject.info_url,
            options[:branch]
          )
        elsif args[0]
          config['.'][:repository] = args[0].strip
        end
      end

      config['.'][:scm] = scm
      config['.'][:type] = type if type

      config.write '.externals'
      reload_configuration
    end
install(args, options) click to toggle source
# File lib/externals/ext.rb, line 423
    def install args, options
      if !File.exist?('.externals')
        STDERR.puts "This project does not appear to be managed by externals.  Try 'ext init' first"
        exit NO_EXTERNALS_FILE
      end
      repository = args[0]
      path = args[1]

      orig_options = options.dup

      scm = options[:scm]

      scm ||= infer_scm(repository)

      unless scm
        STDERR.puts "Unable to determine SCM from the repository name.
You need to either specify the scm used to manage the subproject
that you are installing. Use an option to specify it
(such as --git or --svn)"
        exit COULD_NOT_DETERMINE_SCM
      end

      project = self.class.project_class(scm).new(:repository => repository,
        :path => path || path_calculator.new, :scm => scm)
      path = project.path

      raise "no path" unless path

      raise "already exists" if configuration[path]

      project.branch = options[:branch] if options[:branch]
      project.revision = options[:revision] if options[:revision]

      attributes = project.attributes.dup
      attributes.delete(:path)
      configuration[path] = project.attributes
      configuration.write '.externals'
      reload_configuration

      project.co

      update_ignore args, orig_options
    end
install_project_type(name) click to toggle source
# File lib/externals/ext.rb, line 766
def install_project_type name
  self.path_calculator = Externals.module_eval("#{name.classify}ProjectType::DefaultPathCalculator", __FILE__, __LINE__)
end
main_project() click to toggle source
# File lib/externals/ext.rb, line 289
def main_project
  projects.detect {|p| p.main_project?}
end
print_commands(commands) click to toggle source
projects() click to toggle source
# File lib/externals/ext.rb, line 256
def projects
  return @projects if @projects
  @projects = []
  configuration.sections.each do |section|
    @projects << Ext.project_class(section[:scm]||infer_scm(section[:repository])).new(
      section.attributes.merge(:path => section.title))
  end
  #let's set the parents of these projects
  main = main_project
  subprojects.each {|subproject| subproject.parent = main}
  @projects
end
reload_configuration() click to toggle source
# File lib/externals/ext.rb, line 305
def reload_configuration
  @configuration = nil
  @projects = nil
  configuration
end
status(args, options) click to toggle source
# File lib/externals/ext.rb, line 541
    def status args, options
      options ||= {}
      scm = options[:scm]

      if !scm
        scm ||= configuration['.']
        scm &&= scm['scm']
      end

      if !scm
        possible_project_classes = self.class.project_classes.select do |project_class|
          project_class.detected?
        end

        raise "Could not determine this projects scm" if  possible_project_classes.empty?
        if possible_project_classes.size > 1
          raise "This project appears to be managed by multiple SCMs: #{
          possible_project_classes.map(&:to_s).join(',')}
Please explicitly declare the SCM (by using --git or --svn, or,
by creating the .externals file manually"
        end

        scm = possible_project_classes.first.scm
      end

      unless scm
        raise "You need to either specify the scm as the first line in .externals, or use an option to specify it
          (such as --git or --svn)"
      end

      project = main_project
      project.scm ||= scm
      project.st

      self.class.new({}).st [], {} #args, options
    end
subproject(name)
subproject_by_name_or_path(name) click to toggle source
# File lib/externals/ext.rb, line 269
def subproject_by_name_or_path name
  name = name.strip
  project = subprojects.detect {|p| p.path.strip == name}
  project ||= subprojects.detect do |p|
    File.split(p.path).last.strip == name
  end
  project ||= subprojects.detect do |p|
    p.name == name
  end
end
Also aliased as: subproject
subprojects() click to toggle source
# File lib/externals/ext.rb, line 281
def subprojects
  s = []
  projects.each do |project|
    s << project unless project.main_project?
  end
  s
end
switch(args, options) click to toggle source
# File lib/externals/ext.rb, line 578
    def switch args, options
      branch = args[0]

      options ||= {}
      scm = options[:scm]

      if !scm
        scm ||= configuration['.']
        scm &&= scm['scm']
      end

      if !scm
        possible_project_classes = self.class.project_classes.select do |project_class|
          project_class.detected?
        end

        raise "Could not determine this projects scm" if  possible_project_classes.empty?
        if possible_project_classes.size > 1
          raise "This project appears to be managed by multiple SCMs: #{
          possible_project_classes.map(&:to_s).join(',')}
Please explicitly declare the SCM (by using --git or --svn, or,
by creating the .externals file manually"
        end

        scm = possible_project_classes.first.scm
      end

      unless scm
        raise "You need to either specify the scm as the first line in .externals, or use an option to specify it
          (such as --git or --svn)"
      end

      old_config = configuration
      project = main_project
      project.scm ||= scm

      if project.current_branch == branch
        puts "Already on branch #{branch}"
      else
        project.switch branch, options
        project.up

        reload_configuration

        #update subprojects
        self.class.new({}).up [], {} #args, options

        removed_project_paths = old_config.removed_project_paths(
          configuration
        ).select{|path| File.exist?(path)}

        if !removed_project_paths.empty?
          puts "WARNING: The following subprojects are no longer being maintained in the
.externals file.  You might want to remove them.  You can copy and paste the
commands below if you actually wish to delete them."
          removed_project_paths.each do |path|
            if File.exist?(path)
              puts "  rm -r #{path}"
            end
          end
        end
      end
    end
touch_emptydirs(args, options) click to toggle source
# File lib/externals/ext.rb, line 510
def touch_emptydirs args, options
  require 'find'

  excludes = ['.','..','.svn', '.git']

  excludes.dup.each do |exclude|
    excludes << "./#{exclude}"
  end

  paths = []

  Find.find('.') do |f|
    if File.directory?(f)
      excluded = false
      File.split(f).each do |part|
        exclude ||= excludes.index(part)
      end

      if !excluded && ((Dir.entries(f) - excludes).size == 0)
        paths << f
      end
    end
  end

  paths.each do |p|
    open(File.join(p,".emptydir"), "w").close
  end

end
unfreeze(args, options) click to toggle source
# File lib/externals/ext.rb, line 404
def unfreeze args, options
  project = subproject_by_name_or_path(args[0])

  raise "No such project named #{args[0]}" unless project

  section = configuration[project.path]

  unless section[:revision]
    puts "Uhh... #{project.name} wasn't frozen, so I can't unfreeze it."
    exit
  end

  section.rm_setting :revision
  configuration.write '.externals'
  reload_configuration

  subproject_by_name_or_path(args[0]).up
end
uninstall(args, options) click to toggle source
# File lib/externals/ext.rb, line 467
def uninstall args, options
  unless File.exist?('.externals')
    raise "Hmm... there's no .externals file in this directory."
  end

  project = subproject_by_name_or_path(args[0])

  raise "No such project named #{args[0]}" unless project

  main_project.drop_from_ignore project.path


  configuration.remove_section(project.path)
  configuration.write '.externals'
  reload_configuration

  if options[:force_removal]
    Dir.chdir File.dirname(project.path) do
      rm_rf File.basename(project.path)
    end
  end
end
update(args, options) click to toggle source
# File lib/externals/ext.rb, line 642
def update args, options
  options ||= {}
  scm = options[:scm]

  if !scm
    scm ||= configuration['.']
    scm &&= scm['scm']
  end

  unless scm
    raise "You need to either specify the scm as the first line in .externals, or use an option to specify it
      (such as --git or --svn)"
  end

  project = main_project
  project.scm ||= scm
  project.up

  self.class.new({}).up [], {} #args, options
end
update_ignore(args, options) click to toggle source
# File lib/externals/ext.rb, line 490
def update_ignore args, options
  scm = configuration['.']
  scm = scm['scm'] if scm

  scm ||= options[:scm]

  unless scm
    raise "You need to either specify the scm as the first line in .externals (for example, scm = git), or use an option to specify it
      (such as --git or --svn)"
  end

  project = self.class.project_class(scm).new(:path => ".")

  raise "only makes sense for main project" unless project.main_project?

  subprojects.each do |subproject|
    project.update_ignore subproject.path
  end
end
version(args, options) click to toggle source
# File lib/externals/ext.rb, line 758
def version(args, options)
  puts Externals::VERSION
end

Protected Instance Methods

do_checkout_or_export(repository, path, options, sym) click to toggle source
# File lib/externals/ext.rb, line 771
def do_checkout_or_export repository, path, options, sym
  if File.exist?('.externals')
    raise "seems main project is already checked out here?"
  else
    #We appear to be attempting to checkout/export a main project
    scm = options[:scm]

    scm ||= infer_scm(repository)

    if !scm
      scm ||= configuration['main']
      scm &&= scm['scm']
    end

    unless scm
      raise "You need to either specify the scm as the first line in .externals, or use an option to specify it
      (such as --git or --svn)"
    end

    main_project = self.class.project_class(scm).new(
      :repository => repository,
      :path => path,
      :branch => options[:branch]
    )

    main_project.send(sym)
    main_project
  end
end
infer_scm(path) click to toggle source
# File lib/externals/ext.rb, line 801
def infer_scm(path)
  self.class.registered_scms.each do |scm|
    return scm if self.class.project_class(scm).scm_path?(path)
  end
  nil
end