Class: DamageControl::Project

Inherits:
Object
  • Object
show all
Includes:
Web::Configuration, ObjectTemplate
Defined in:
lib/damagecontrol/project.rb,
lib/damagecontrol/project_dependencies.rb,
app/controllers/rscm_ext.rb

Overview

Represents a project with associated SCM, Tracker and SCMWeb

Constant Summary collapse

DEFAULT_QUIET_PERIOD =

TODO: move to scms? not sure.…

10

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Web::Configuration

#selected?, #short

Methods included from ObjectTemplate

#dupe

Constructor Details

#initialize(name = "") ⇒ Project

Returns a new instance of Project.



79
80
81
82
83
84
85
86
87
88
# File 'lib/damagecontrol/project.rb', line 79

def initialize(name="")
  @name = name
  @publishers = Publisher::Base.classes.collect{|cls| cls.new}
  @scm = nil
  @tracker = Tracker::Null.new
  @scm_web = SCMWeb::Null.new
  # Default start time is 2 weeks ago
  @start_time = Time.now.utc - (3600*24*14)
  @quiet_period = DEFAULT_QUIET_PERIOD
end

Instance Attribute Details

#build_commandObject

Returns the value of attribute build_command.



44
45
46
# File 'lib/damagecontrol/project.rb', line 44

def build_command
  @build_command
end

#home_pageObject

Returns the value of attribute home_page.



34
35
36
# File 'lib/damagecontrol/project.rb', line 34

def home_page
  @home_page
end

#nameObject

Returns the value of attribute name.



33
34
35
# File 'lib/damagecontrol/project.rb', line 33

def name
  @name
end

#publishersObject

Returns the value of attribute publishers.



45
46
47
# File 'lib/damagecontrol/project.rb', line 45

def publishers
  @publishers
end

#quiet_periodObject

How long to sleep between each changesets invocation for non-transactional SCMs



42
43
44
# File 'lib/damagecontrol/project.rb', line 42

def quiet_period
  @quiet_period
end

#scmObject

Returns the value of attribute scm.



37
38
39
# File 'lib/damagecontrol/project.rb', line 37

def scm
  @scm
end

#scm_webObject

Returns the value of attribute scm_web.



39
40
41
# File 'lib/damagecontrol/project.rb', line 39

def scm_web
  @scm_web
end

#start_timeObject

Returns the value of attribute start_time.



35
36
37
# File 'lib/damagecontrol/project.rb', line 35

def start_time
  @start_time
end

#trackerObject

Returns the value of attribute tracker.



38
39
40
# File 'lib/damagecontrol/project.rb', line 38

def tracker
  @tracker
end

Class Method Details

.find_allObject

Loads all projects



68
69
70
71
72
# File 'lib/damagecontrol/project.rb', line 68

def Project.find_all
  Directories.project_names.collect do |name|
    Project.load(name)
  end
end

.load(name) ⇒ Object

Loads the project with the given name.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/damagecontrol/project.rb', line 48

def Project.load(name)
  config_file = Directories.project_config_file(name)
  Log.info "Loading project from #{config_file}"
  project = File.open(config_file) do |io|
    YAML::load(io)
  end

  # Add new publishers that may have be defined after the project was YAMLed.
  project.publishers = [] if project.publishers.nil?
  Publisher::Base.classes.collect{|cls| cls.new}.each do |publisher|
    publisher_of_same_type = project.publishers.find do |p|
      p.class == publisher.class
    end
    project.publishers << publisher unless publisher_of_same_type
  end

  project
end

Instance Method Details

#==(o) ⇒ Object



230
231
232
233
# File 'lib/damagecontrol/project.rb', line 230

def == (o)
  return false unless o.is_a?(Project)
  name == o.name
end

#add_dependency(project) ⇒ Object



5
6
# File 'lib/damagecontrol/project_dependencies.rb', line 5

def add_dependency(project)
end

#build(changeset_identifier, build_time) ⇒ Object

Returns the Build for the given changeset_identifier and build_time



257
258
259
# File 'lib/damagecontrol/project.rb', line 257

def build(changeset_identifier, build_time)
  Build.new(name, changeset_identifier, build_time, "FIXME")
end

#builds(changeset_identifier) ⇒ Object

Returns an array of existing Build s for the given changeset_identifier.



249
250
251
252
253
254
# File 'lib/damagecontrol/project.rb', line 249

def builds(changeset_identifier)
  Directories.build_dirs(name, changeset_identifier).collect do |dir|
    # The dir's basename will always be a Time
    Build.new(name, changeset_identifier, File.basename(dir).to_identifier, "TODO: get from file")
  end
end

#changeset(changeset_identifier) ⇒ Object



212
213
214
215
216
# File 'lib/damagecontrol/project.rb', line 212

def changeset(changeset_identifier)
 result = changesets(changeset_identifier, 1)[0]
 raise "No changeset with id '#{changeset_identifier}' for project '#{name}'" unless result
 result
end

#changeset_identifiersObject



218
219
220
# File 'lib/damagecontrol/project.rb', line 218

def changeset_identifiers
  changesets_persister.identifiers
end

#changesets(changeset_identifier, prior) ⇒ Object



208
209
210
# File 'lib/damagecontrol/project.rb', line 208

def changesets(changeset_identifier, prior)
  changesets_persister.load_upto(changeset_identifier, prior)
end

#changesets_dirObject



204
205
206
# File 'lib/damagecontrol/project.rb', line 204

def changesets_dir
  Directories.changesets_dir(name)
end

#changesets_persisterObject



235
236
237
# File 'lib/damagecontrol/project.rb', line 235

def changesets_persister
  DamageControl::Visitor::YamlPersister.new(changesets_dir)
end

#changesets_rss_exists?Boolean

Returns:

  • (Boolean)


200
201
202
# File 'lib/damagecontrol/project.rb', line 200

def changesets_rss_exists?
  File.exist?(changesets_rss_file)
end

#changesets_rss_fileObject

Where RSS is written.



176
177
178
# File 'lib/damagecontrol/project.rb', line 176

def changesets_rss_file
  Directories.changesets_rss_file(name)
end

#checked_out?Boolean

Returns:

  • (Boolean)


180
181
182
# File 'lib/damagecontrol/project.rb', line 180

def checked_out?
  @scm.checked_out?(checkout_dir)
end

#checkout(changeset_identifier) ⇒ Object

Checks out files to project’s checkout directory. Writes the checked out files to checkout_list_file. The changeset_identifier parameter is a String or a Time representing a changeset.



126
127
128
129
130
131
132
133
# File 'lib/damagecontrol/project.rb', line 126

def checkout(changeset_identifier)
  File.open(checkout_list_file, "w") do |f|
    scm.checkout(checkout_dir, changeset_identifier) do |file_name|
      f << file_name << "\n"
      f.flush
    end
  end
end

#checkout_dirObject



192
193
194
# File 'lib/damagecontrol/project.rb', line 192

def checkout_dir
  Directories.checkout_dir(name)
end

#checkout_list_fileObject

Path to file containing pathnames of latest checked out files.



118
119
120
# File 'lib/damagecontrol/project.rb', line 118

def checkout_list_file
  Directories.checkout_list_file(name)
end

#deleteObject



226
227
228
# File 'lib/damagecontrol/project.rb', line 226

def delete
  File.delete(Directories.project_dir(name))
end

#delete_working_copyObject



196
197
198
# File 'lib/damagecontrol/project.rb', line 196

def delete_working_copy
  File.delete(checkout_dir)
end

#execute_build(changeset_identifier, build_reason) {|build| ... } ⇒ Object

Creates, persists and executes a Build for the changeset with the given changeset_identifier. Should be called with a block of arity 1 that will receive the build.

Yields:



242
243
244
245
246
# File 'lib/damagecontrol/project.rb', line 242

def execute_build(changeset_identifier, build_reason)
  scm.checkout(checkout_dir, changeset_identifier)
  build = Build.new(name, changeset_identifier, Time.now.utc, build_reason)
  yield build
end

#exists?Boolean

Returns:

  • (Boolean)


184
185
186
# File 'lib/damagecontrol/project.rb', line 184

def exists?
  File.exists?(project_config_file)
end

#latest_buildObject

Returns the latest build.



262
263
264
265
266
267
268
# File 'lib/damagecontrol/project.rb', line 262

def latest_build
  changeset_identifiers.reverse.each do |changeset_identifier|
    builds = builds(changeset_identifier)
    return builds[-1] unless builds.empty?
  end
  nil
end

#latest_changeset_identifierObject



222
223
224
# File 'lib/damagecontrol/project.rb', line 222

def latest_changeset_identifier
  changesets_persister.latest_identifier
end

#next_changeset_identifier(d = changesets_dir) ⇒ Object

Returns the identifier (int label or time) that should be used to get the next (unrecorded) changeset. This is the identifier following the latest recorded changeset. This identifier is determined by looking at the directory names under changesets_dir. If there are none, this method returns nil.



169
170
171
172
173
# File 'lib/damagecontrol/project.rb', line 169

def next_changeset_identifier(d=changesets_dir)
  # See String extension at top of this file.
  latest_identifier = DamageControl::Visitor::YamlPersister.new(d).latest_identifier
  latest_identifier ? latest_identifier + 1 : nil
end

#poll {|changesets| ... } ⇒ Object

Polls SCM for new changesets and yields them to the given block.

Yields:



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/damagecontrol/project.rb', line 136

def poll
  start = Time.now
  from = next_changeset_identifier || @start_time
  
  Log.info "Getting changesets for #{name} from #{from} (retrieved from #{checkout_dir})"
  changesets = @scm.changesets(checkout_dir, from)
  if(!changesets.empty? && !@scm.transactional?)
    # We're dealing with a non-transactional SCM (like CVS/StarTeam/ClearCase,
    # unlike Subversion/Monotone). Sleep a little, get the changesets again.
    # When the changesets are not changing, we can consider the last commit done
    # and the quiet period elapsed. This is not 100% failsafe, but will work
    # under most circumstances. In the worst case, we'll miss some files in
    # the changesets for really slow commits, but they will be part of the next 
    # changeset (on next poll).
    commit_in_progress = true
    while(commit_in_progress)
      @quiet_period ||= DEFAULT_QUIET_PERIOD
      Log.info "Sleeping for #{@quiet_period} seconds since #{name}'s SCM (#{@scm.name}) is not transactional."
      sleep @quiet_period
      next_changesets = @scm.changesets(checkout_dir, from)
      commit_in_progress = changesets != next_changesets
      changesets = next_changesets
    end
    Log.info "Quiet period elapsed for #{name}"
  end
  Log.info "Got changesets for #{@name} in #{Time.now.difference_as_text(start)}"
  yield changesets
end

#publish(build) ⇒ Object

Tells all publishers to publish a build



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/damagecontrol/project.rb', line 91

def publish(build)
  @publishers.each do |publisher| 
    begin
      if(publisher.enabled)
        Log.info("Publishing #{publisher.name} for #{@name}")
        publisher.publish(build) 
      else
        Log.info("Skipping disabled publisher #{publisher.name} for #{@name}")
      end
    rescue => e
      Log.error "Error running publisher #{publisher.name} for project #{name}"
      Log.error  e.message
      Log.error "  " + e.backtrace.join("  \n")
    end
  end
end

#saveObject

Saves the state of this project to persistent store (YAML)



109
110
111
112
113
114
115
# File 'lib/damagecontrol/project.rb', line 109

def save
  f = project_config_file
  FileUtils.mkdir_p(File.dirname(f))
  File.open(f, "w") do |io|
    YAML::dump(self, io)
  end
end

#scm_exists?Boolean

Returns:

  • (Boolean)


188
189
190
# File 'lib/damagecontrol/project.rb', line 188

def scm_exists?
  scm.exists?
end