Class: ASF::SVN

Inherits:
Object
  • Object
show all
Defined in:
lib/whimsy/asf/svn.rb

Class Method Summary collapse

Class Method Details

.[](name) ⇒ Object



34
35
36
# File 'lib/whimsy/asf/svn.rb', line 34

def self.[](name)
  self.find!(name)
end

.[]=(name, path) ⇒ Object



30
31
32
# File 'lib/whimsy/asf/svn.rb', line 30

def self.[]=(name, path)
  @testdata[name] = File.expand_path(path).untaint
end

.find(name) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/whimsy/asf/svn.rb', line 38

def self.find(name)
  return @testdata[name] if @testdata[name]

  result = repos[(@mock+name.sub('private/','')).to_s.sub(/\/*$/, '')] ||
    repos[(@base+name).to_s.sub(/\/*$/, '')] # lose trailing slash

  return result if result

  # recursively try parent directory
  if name.include? '/'
    base = File.basename(name).untaint
    result = find(File.dirname(name))
    if result and File.exist?(File.join(result, base))
      File.join(result, base)
    end
  end
end

.find!(name) ⇒ Object



56
57
58
59
60
61
62
63
64
# File 'lib/whimsy/asf/svn.rb', line 56

def self.find!(name)
  result = self.find(name)

  if not result
    raise Exception.new("Unable to find svn checkout for #{@base + name}")
  end

  result
end

.get(path, user = nil, password = nil) ⇒ Object

retrieve revision, content for a file in svn



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/whimsy/asf/svn.rb', line 67

def self.get(path, user=nil, password=nil)
  # build svn info command
  cmd = ['svn', 'info', path, '--non-interactive']

  # password was supplied, add credentials
  if password
    cmd += ['--username', user, '--password', password, '--no-auth-cache']
  end

  # default the values to return
  revision = '0'
  content = nil

  # issue svn info command
  stdout, status = Open3.capture2(*cmd)
  if status.success?
    # extract revision number
    revision = stdout[/^Revision: (\d+)/, 1]

    # extract contents
    cmd[1] = 'cat'
    content, status = Open3.capture2(*cmd)
  end

  # return results
  return revision, content
end

.reposObject



15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/whimsy/asf/svn.rb', line 15

def self.repos
  @semaphore.synchronize do
    svn = Array(ASF::Config.get(:svn)).map {|dir| dir.untaint}
    @repos ||= Hash[Dir[*svn].map { |name| 
      next unless Dir.exist? name.untaint
      Dir.chdir name.untaint do
        out, err, status = Open3.capture3('svn', 'info')
        if status.success?
          [out[/URL: (.*)/,1].sub(/^http:/,'https:'), Dir.pwd.untaint]
        end
      end
    }.compact]
  end
end

.update(path, msg, env, _, options = {}) ⇒ Object

update a file or directory in SVN, working entirely in a temporary directory



97
98
99
100
101
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/whimsy/asf/svn.rb', line 97

def self.update(path, msg, env, _, options={})
  if File.directory? path
    dir = path
    basename = nil
  else
    dir = File.dirname(path)
    basename = File.basename(path)
  end

  if path.start_with? '/' and not path.include? '..' and File.exist?(path)
    dir.untaint
    basename.untaint
  end
  
  tmpdir = Dir.mktmpdir.untaint

  begin
    # create an empty checkout
    _.system ['svn', 'checkout', '--depth', 'empty',
      ['--username', env.user, '--password', env.password],
      `svn info #{dir}`[/URL: (.*)/, 1], tmpdir]

    # retrieve the file to be updated (may not exist)
    if basename
      tmpfile = File.join(tmpdir, basename).untaint
      _.system ['svn', 'update',
        ['--username', env.user, '--password', env.password],
        tmpfile]
    else
      tmpfile = nil
    end

    # determine the new contents
    if not tmpfile
      # updating a directory
      previous_contents = contents = nil
      yield tmpdir, ''
    elsif File.file? tmpfile
      # updating an existing file
      previous_contents = File.read(tmpfile)
      contents = yield tmpdir, File.read(tmpfile)
    else
      # updating a new file
      previous_contents = nil
      contents = yield tmpdir, ''
      previous_contents = File.read(tmpfile) if File.file? tmpfile
    end
 
    # create/update the temporary copy
    if contents and not contents.empty?
      File.write tmpfile, contents
      if not previous_contents
        _.system ['svn', 'add',
          ['--username', env.user, '--password', env.password],
          tmpfile]
      end
    elsif tmpfile and File.file? tmpfile
      File.unlink tmpfile
      _.system ['svn', 'delete',
        ['--username', env.user, '--password', env.password],
        tmpfile]
    end

    if options[:dryrun]
      # show what would have been committed
      rc = _.system ['svn', 'diff', tmpfile]
    else
      # commit the changes
      rc = _.system ['svn', 'commit', tmpfile || tmpdir,
        ['--username', env.user, '--password', env.password],
        '--message', msg.untaint]
    end

    # fail if there are pending changes
    unless rc == 0 and `svn st #{tmpfile || tmpdir}`.empty?
      raise "svn failure #{path.inspect}"
    end
  ensure
    FileUtils.rm_rf tmpdir
  end
end