Class: Helium::Deployer

Inherits:
Object
  • Object
show all
Includes:
Observable
Defined in:
lib/helium/deployer.rb

Overview

The Deployer class is responsible for performing all of Helium’s central functions: downloading projects from Git, exporting static copies of branches, building projects using Jake and generating the dependency file.

To run, the class requires a file called ‘deploy.yml` in the target directory, listing projects with their Git URLs. (See tests and README for examples.)

Instance Method Summary collapse

Constructor Details

#initialize(path, output_dir = 'output', options = {}) ⇒ Deployer

Initialize using the directory containing the ‘deploy.yml` file, and the name of the directory to export repositories and static files to.

deployer = Helium::Deployer.new('path/to/app', 'js')


17
18
19
20
21
22
23
# File 'lib/helium/deployer.rb', line 17

def initialize(path, output_dir = 'output', options = {})
  @path       = File.expand_path(path)
  @config     = join(@path, CONFIG_FILE)
  raise "Expected config file at #{@config}" unless File.file?(@config)
  @output_dir = join(@path, output_dir)
  @options    = options
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(*args, &block) ⇒ Object (private)

We use method_missing to create shorthands for FileUtils methods.



218
219
220
# File 'lib/helium/deployer.rb', line 218

def method_missing(*args, &block)
  FileUtils.__send__(*args, &block)
end

Instance Method Details

#checkout(project) ⇒ Object

Checks out (or updates if already checked out) a project by name from its Git repository. If the project is new, we use ‘git clone` to copy it, otherwise we use `git fetch` to update it.



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/helium/deployer.rb', line 61

def checkout(project)
  dir = repo_dir(project)
  if File.directory?(join(dir, GIT))
    log :git_fetch, "Updating Git repo in #{ dir }"
    cd(dir) { `git fetch origin` }
  else
    url = projects[project]
    log :git_clone, "Cloning Git repo #{ url } into #{ dir }"
    `git clone #{ url } "#{ dir }"`
  end
end

#cleanup!Object

Removes any repositories and static files for projects not listed in the the ‘deploy.yml` file.



151
152
153
154
155
156
157
158
159
160
# File 'lib/helium/deployer.rb', line 151

def cleanup!
  [repo_dir, static_dir].each do |dir|
    next unless File.directory?(dir)
    (Dir.entries(dir) - %w[. ..]).each do |entry|
      path = join(dir, entry)
      next unless File.directory?(path)
      rm_rf(path) unless projects.has_key?(entry)
    end
  end
end

#configObject

Returns the deserialized contents of ‘deploy.yml`.



26
27
28
# File 'lib/helium/deployer.rb', line 26

def config
  @config_data ||= YAML.load(File.read(@config))
end

#deploy!(project, build = true) ⇒ Object

Deploys an individual project by checking it out from Git, exporting static copies of all its branches and building them using Jake.



52
53
54
55
56
# File 'lib/helium/deployer.rb', line 52

def deploy!(project, build = true)
  checkout(project)
  export(project)
  run_builds! if build
end

#export(project) ⇒ Object

Exports static copies of a project from every branch and tag in its Git repository. Existing static copies on disk are removed. Mappings from branch/tag names to commit IDs are stored in heads.yml in the project directory.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/helium/deployer.rb', line 76

def export(project)
  repo_dir = repo_dir(project)
  repo     = Grit::Repo.new(repo_dir)
  
  export_directory = static_dir(project)
  mkdir_p(export_directory)
  
  heads = head_mappings(project)
  File.open(static_dir(project, HEAD_LIST), 'w') { |f| f.write(YAML.dump(heads)) }
  
  heads.values.uniq.each do |commit|
    target = static_dir(project, commit)
    next if File.directory?(target)
    
    log :export, "Exporting commit '#{ commit }' of '#{ project }' into #{ target }"
    cp_r(repo_dir, target)
    
    cd(target) { `git checkout #{commit}` }
  end
end

#projectsObject

Returns a hash of projects names and their Git URLs.



31
32
33
34
35
36
37
38
39
40
# File 'lib/helium/deployer.rb', line 31

def projects
  return @projects if defined?(@projects)
  
  raise "No configuration for JS.Class" unless js_class = config[JS_CLASS]
  @jsclass_version = js_class['version']
  
  @projects = config['projects'] || {}
  @projects[JS_CLASS] = js_class['repository']
  @projects
end

#repo_dir(project = nil) ⇒ Object

Returns the path to the Git repository for a given project.



163
164
165
166
# File 'lib/helium/deployer.rb', line 163

def repo_dir(project = nil)
  path = [@output_dir, REPOS, project].compact
  join(*path)
end

#run!(project = nil) ⇒ Object

Runs all the deploy actions. If given a project name, only checks out and builds the given project, otherwise builds all projects in ‘deploy.yml`.



44
45
46
47
48
# File 'lib/helium/deployer.rb', line 44

def run!(project = nil)
  return deploy!(project) if project
  projects.each { |project, url| deploy!(project, false) }
  run_builds!
end

#run_builds!(options = nil) ⇒ Object

Scans all the checked-out projects for Jake build files and builds those projects that have such a file. As the build progresses we use Jake event hooks to collect dependencies and generated file paths, and when all builds are finished we generate a JS.Packages file listing all the files discovered. This file should be included in web pages to set up the the packages manager for loading our projects.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/helium/deployer.rb', line 102

def run_builds!(options = nil)
  options ||= @options
  
  @tree     = Trie.new
  @custom   = options[:custom]
  @location = options[:location]
  manifest  = []
  
  # Loop over checked-out projects. Skip directories with no Jake file.
  Find.find(static_dir) do |path|
    next unless File.directory?(path) and File.file?(join(path, JAKE_FILE))
    
    project, commit = *path.split(SEP)[-2..-1]
    heads = YAML.load(File.read(join(path, '..', HEAD_LIST)))
    branches = heads.select { |(head, id)| id == commit }.map { |pair| pair.first }
    
    Jake.clear_hooks!
    
    # Event listener to capture file information from Jake
    hook = lambda do |build, package, build_type, file|
      if build_type == :min
        @js_loader = file if File.basename(file) == LOADER_FILE and
                             project == JS_CLASS and
                             branches.include?(@jsclass_version)
        
        file = file.sub(path, '')
        manifest << join(project, commit, file)
        
        branches.each do |branch|
          @tree[[project, branch]] = commit
          @tree[[project, branch, file]] = package.meta
        end
      end
    end
    jake_hook(:file_created, &hook)
    jake_hook(:file_not_changed, &hook)
    
    log :jake_build, "Building branch '#{ branches * "', '" }' of '#{ project }' from #{ join(path, JAKE_FILE) }"
    
    begin; Jake.build!(path)
    rescue; end
  end
  
  generate_manifest!
  manifest + [PACKAGES, PACKAGES_MIN]
end

#static_dir(project = nil, branch = nil) ⇒ Object

Returns the path to the static export directory for a given project and branch.



169
170
171
172
# File 'lib/helium/deployer.rb', line 169

def static_dir(project = nil, branch = nil)
  path = [@output_dir, STATIC, project, branch].compact
  join(*path)
end