Class: ScmWorkspace

Inherits:
Object
  • Object
show all
Defined in:
lib/scm_workspace.rb,
lib/scm_workspace/version.rb

Defined Under Namespace

Classes: Error

Constant Summary collapse

CONFIGURE_OPTIONS =
{
  svn: {
    "A" => "authors_file",
    "b" => "branches",
    "m!" => "minimize_rul",
    "q+" => "quiet",
    "r" => "revision",
    "s" => "stdlayout",
    "t" => "tags",
    "T" => "trunk",
  },

  git: {
    'n' => 'no_checkout',
    'l' => 'local',
    's' => 'shared',
    'o' => 'origin',
    'b' => 'branch',
    'u' => 'upload_pack',
    'c' => 'config',
  }
}
VERSION =
"0.3.4"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config, options = {}) ⇒ ScmWorkspace

Returns a new instance of ScmWorkspace.



21
22
23
24
25
26
# File 'lib/scm_workspace.rb', line 21

def initialize(config, options = {})
  @root = config[:workspace]
  @logger = options[:logger]
  @verbose = options[:verbose] || (ENV["VERBOSE"] =~ /true|yes|on/)
  @svn_branch_prefix = options[:svn_branch_prefix] || ENV["SVN_BRANCH_PREFIX"] || "branches"
end

Instance Attribute Details

#loggerObject



28
29
30
# File 'lib/scm_workspace.rb', line 28

def logger
  @logger ||= Tengine::Support::NullLogger.new
end

#rootObject (readonly)

Returns the value of attribute root.



17
18
19
# File 'lib/scm_workspace.rb', line 17

def root
  @root
end

#svn_branch_prefixObject

Returns the value of attribute svn_branch_prefix.



19
20
21
# File 'lib/scm_workspace.rb', line 19

def svn_branch_prefix
  @svn_branch_prefix
end

#verboseObject

Returns the value of attribute verbose.



20
21
22
# File 'lib/scm_workspace.rb', line 20

def verbose
  @verbose
end

Class Method Details

.guess_scm_type(url) ⇒ Object



345
346
347
348
349
350
351
# File 'lib/scm_workspace.rb', line 345

def guess_scm_type(url)
  case url
  when /\Agit:\/\//, /\Agit\@/, /\.git\Z/ then :git
  when /svn/ then :svn
  else nil
  end
end

Instance Method Details

#accessible?Boolean

submoduleでない場合 true submoduleの場合 true gitの対象外の場合 false

Returns:

  • (Boolean)


271
272
273
# File 'lib/scm_workspace.rb', line 271

def accessible?
  File.exist?(File.join(repo_dir, ".git"))
end

#branch_namesObject



188
189
190
191
192
193
194
195
196
197
198
# File 'lib/scm_workspace.rb', line 188

def branch_names
  return nil unless accessible?
  case scm_type
  when :git then
    result = system_at_root!("git branch -r").lines.map{|path| path.sub(/\A\s*origin\//, '').strip }
    result.delete_if{|name| name =~ /\AHEAD ->/}
    result
  when :svn then
    system_at_root!("git branch -r").lines.map{|path| path.strip }
  end
end

#checkout(branch_name) ⇒ Object



107
108
109
110
111
112
113
# File 'lib/scm_workspace.rb', line 107

def checkout(branch_name)
  logger.info("-" * 100)
  system_at_root!("git checkout #{branch_name}")
  case scm_type
  when :git then system_at_root!("git reset --hard origin/#{branch_name}")
  end
end

#clearObject



89
90
91
92
93
94
95
96
97
98
# File 'lib/scm_workspace.rb', line 89

def clear
  return unless Dir.exist?(root)
  fileutils.chdir(root) do
    (Dir.glob("*") + Dir.glob(".*")).each do |d|
      next if d =~ /\A\.+\Z/
      logger.info("remove_entry_secure #{d.inspect}")
      fileutils.remove_entry_secure(d)
    end
  end
end

#cleared?Boolean

Returns:

  • (Boolean)


283
284
285
286
# File 'lib/scm_workspace.rb', line 283

def cleared?
  # !configured?
  empty?
end

#commit_infoObject



179
180
181
182
183
184
185
186
# File 'lib/scm_workspace.rb', line 179

def commit_info
  {
    "scm_type" => scm_type.to_s,
    "url" => url,
    "branch" => current_branch_name,
    "commit_key" => current_commit_key
  }
end

#configure(url) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/scm_workspace.rb', line 63

def configure(url)
  unless empty?
    msg = "#{repo_dir} is not empty. You must clear it"
    msg << "\nls -la #{repo_dir}" << `ls -la #{repo_dir}` if verbose
    raise msg
  end
  url, opt = url.split(/\s+/, 2)
  scm_type = self.class.guess_scm_type(url)
  case scm_type
  when :git then
    logger.info("*" * 100)
    logger.info("SCM configure")
    system!("git clone #{url} #{repo_dir} #{opt}")
  when :svn then
    fileutils.chdir(@root) do
      cmd = "git svn clone #{url} #{repo_dir} #{opt}"
      cmd << " > /dev/null 2>&1" unless verbose
      logger.info("*" * 100)
      logger.info("SCM configure")
      system(cmd)
    end
  else
    raise "Unknown SCM type: #{url}"
  end
end

#configured?Boolean

submoduleでない場合 true submoduleの場合 false gitの対象外の場合 false

Returns:

  • (Boolean)


278
279
280
# File 'lib/scm_workspace.rb', line 278

def configured?
  Dir.exist?(File.join(repo_dir, ".git"))
end

#contains?(obj) ⇒ Boolean

Returns:

  • (Boolean)


288
289
290
# File 'lib/scm_workspace.rb', line 288

def contains?(obj)
  !!system_at_root!("git show #{obj} --oneline --quiet") rescue false
end

#current_branch_nameObject



220
221
222
223
224
225
226
# File 'lib/scm_workspace.rb', line 220

def current_branch_name
  return nil unless accessible?
  case scm_type
  when :git then git_current_branch_name
  when :svn then svn_current_branch_name
  end
end

#current_commit_keyObject



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/scm_workspace.rb', line 162

def current_commit_key
  return nil unless accessible?
  result = current_sha
  case scm_type
  when :svn then
    rev = nil
    cnt = 0
    while rev.nil?
      rev = in_repo_dir{ `git svn find-rev #{result}`.strip }
      cnt += 1
      raise "failed to get svn revision for #{result}" if cnt > 10
    end
    result << ':' << rev
  end
  result
end

#current_shaObject



157
158
159
160
# File 'lib/scm_workspace.rb', line 157

def current_sha
  return nil unless accessible?
  system_at_root!("git log -1").scan(/^commit ([0-9a-f]+)$/).flatten.first
end

#current_tag_namesObject



258
259
260
261
# File 'lib/scm_workspace.rb', line 258

def current_tag_names
  return nil unless accessible?
  system_at_root!("git describe --tags #{current_sha}").lines.map(&:strip) rescue []
end

#empty?Boolean

Returns:

  • (Boolean)


100
101
102
103
104
105
# File 'lib/scm_workspace.rb', line 100

def empty?
  return true unless Dir.exist?(root)
  FileUtils.chdir(root) do
    return (Dir.glob("*") + Dir.glob(".*")).reject{|d| d =~ /\A\.+\Z/}.empty?
  end
end

#fetchObject



124
125
126
127
128
129
130
# File 'lib/scm_workspace.rb', line 124

def fetch
  logger.info("-" * 100)
  case scm_type
  when :git then system_at_root!("git fetch origin")
  when :svn then system_at_root!("git svn fetch")
  end
end

#fileutilsObject



32
33
34
# File 'lib/scm_workspace.rb', line 32

def fileutils
  @fileutils ||= FileUtils.with_logger(logger)
end

#git_current_branch_name(dir = repo_dir) ⇒ Object



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/scm_workspace.rb', line 228

def git_current_branch_name(dir = repo_dir)
  return nil unless Dir.exist?(dir)
  FileUtils.chdir(dir) do
    # http://qiita.com/sugyan/items/83e060e895fa8ef2038c
    result = `git symbolic-ref --short HEAD`.strip
    return result unless result.nil? || result.empty?
    result = `git status`.scan(/On branch\s*(.+)\s*$/).flatten.first
    return result unless result.nil? || result.empty?
    work = `git log --decorate -1`.scan(/^commit\s[0-9a-f]+\s\((.+)\)/).
      flatten.first.split(/,/).map(&:strip).reject{|s| s =~ /HEAD\Z/}
    r = work.select{|s| s =~ /origin\//}.first
    r ||= work.first
    result = r.nil? ? nil : r.sub(/\Aorigin\//, '')
    return result
  end
rescue => e
  # puts "[#{e.class}] #{e.message}"
  # puts "Dir.pwd: #{Dir.pwd}"
  # puts "git status\n" << `git status`
  raise e
end

#git_repo?Boolean

Returns:

  • (Boolean)


315
316
317
318
# File 'lib/scm_workspace.rb', line 315

def git_repo?
  return nil unless accessible?
  !remotes.empty? rescue false
end

#in_rootObject Also known as: in_repo_dir



337
338
339
340
341
# File 'lib/scm_workspace.rb', line 337

def in_root
  fileutils.chdir(repo_dir) do
    return yield
  end
end

#remotesObject



200
201
202
203
204
205
# File 'lib/scm_workspace.rb', line 200

def remotes
  system_at_root!("git remote -v show").lines.each_with_object({}) do |line, d|
    name, url, other = line.strip.split(/[\t\s]+/, 3)
    d[name] = url
  end
end

#repo_dirObject



264
265
266
# File 'lib/scm_workspace.rb', line 264

def repo_dir
  @root
end

#reset_hard(tag) ⇒ Object Also known as: move



115
116
117
118
119
120
121
# File 'lib/scm_workspace.rb', line 115

def reset_hard(tag)
  logger.info("-" * 100)
  case scm_type
  when :git then system_at_root!("git reset --hard #{tag}")
  when :svn then raise "Illegal operation for svn"
  end
end

#scm_typeObject



325
326
327
328
329
330
# File 'lib/scm_workspace.rb', line 325

def scm_type
  return nil unless accessible?
  return :git if git_repo?
  return :svn if svn_repo?
  nil
end

#statusObject



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/scm_workspace.rb', line 132

def status
  logger.info("-" * 100)
  case scm_type
  when :git then
    status_text = system_at_root!("git status") # in_repo_dir{ `git status` }
    value = status_text.scan(/Your branch is behind 'origin\/#{current_branch_name}' by (\d+\s+commits)/)
    if value && !value.empty?
      "There is/are #{value.flatten.join}"
    else
      "everything is up-to-dated."
    end
  when :svn then
    status_text = system_at_root!("git log --branches --oneline #{current_sha}..")
    lines = status_text.split(/\n/)
    if lines.empty?
      "everything is up-to-dated."
    else
      latest_sha = lines.first
      "There is/are #{lines.length} commits." <<
        " current revision: " << system_at_root!("git svn find-rev #{current_sha}").strip <<
        " latest revision: "  << system_at_root!("git svn find-rev #{latest_sha}").strip
    end
  end
end

#svn_current_branch_nameObject



250
251
252
253
254
255
256
# File 'lib/scm_workspace.rb', line 250

def svn_current_branch_name
  info = svn_info
  r = info[:url].sub(info[:repository_root], '')
  r.sub!(/\A\//, '')
  r.sub!(svn_branch_prefix + "/", '')
  r
end

#svn_infoObject



332
333
334
335
# File 'lib/scm_workspace.rb', line 332

def svn_info
  txt = system_at_root!("git svn info")
  return txt.scan(/^(.+?): (.*)$/).each_with_object({}){|(k,v), d| d[k.downcase.gsub(/\s/, '_').to_sym] = v }
end

#svn_repo?Boolean

Returns:

  • (Boolean)


320
321
322
323
# File 'lib/scm_workspace.rb', line 320

def svn_repo?
  return nil unless accessible?
  Dir.exist?(File.join(repo_dir, '.git', 'svn'))
end

#system!(cmd) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/scm_workspace.rb', line 36

def system!(cmd)
  logger.info("executing: #{cmd}")
  buf = []
  IO.popen("#{cmd} 2>&1") do |io|
    while line = io.gets
      # puts line
      buf << line
    end
  end

  if $?.exitstatus == 0
    msg = "\e[33mSUCCESS: %s\e[0m" % cmd
    r = buf.join
    msg << "\n" << r.strip if verbose
    logger.info(msg)
    return r
  else
    msg = "\e[31mFAILURE: %s\n%s\e[0m" % [cmd, buf.join.strip]
    logger.error(msg)
    raise Error, msg
  end
end

#system_at_root!(cmd) ⇒ Object



59
60
61
# File 'lib/scm_workspace.rb', line 59

def system_at_root!(cmd)
  fileutils.chdir(root){ system!(cmd) }
end

#tag_namesObject



207
208
209
210
# File 'lib/scm_workspace.rb', line 207

def tag_names
  return nil unless accessible?
  system_at_root!("git tag").lines.map{|path| path.strip.strip }
end

#urlObject



212
213
214
215
216
217
218
# File 'lib/scm_workspace.rb', line 212

def url
  return nil unless accessible?
  case scm_type
  when :git then remotes["origin"]
  when :svn then svn_info[:repository_root]
  end
end