This is an automated email from the ASF dual-hosted git repository.

sebb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/whimsy.git


The following commit(s) were added to refs/heads/master by this push:
     new 4a629b39 Initial commit of SVN file update with revision checking
4a629b39 is described below

commit 4a629b394eb005505a079e73021de43e1713faa4
Author: Sebb <[email protected]>
AuthorDate: Fri Jun 6 17:27:17 2025 +0100

    Initial commit of SVN file update with revision checking
---
 lib/spec/lib/svn_spec.rb | 17 +++++++++++
 lib/whimsy/asf/svn.rb    | 79 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 96 insertions(+)

diff --git a/lib/spec/lib/svn_spec.rb b/lib/spec/lib/svn_spec.rb
index 412ee7ed..f6ff7c28 100644
--- a/lib/spec/lib/svn_spec.rb
+++ b/lib/spec/lib/svn_spec.rb
@@ -241,6 +241,23 @@ describe ASF::SVN do
     end
   end
 
+  describe 'ASF:SVN.getfile' do
+    it "getfile(public url,'HEADER.html') should return the revision and 
contents" do
+      repo = File.join(ASF::SVN.svnurl('minutes'),'HEADER.html')
+      revision, content = ASF::SVN.getfile(repo)
+      expect(revision).to match(/\d+/)
+      expect(content.size).to be > 260 # need a better test
+    end
+    it "getfile(public url,'HEADER.html', block) should yield revision, path 
and contents" do
+      repo = File.join(ASF::SVN.svnurl('minutes'),'HEADER.html')
+      ASF::SVN.getfile(repo) do |revision, filepath, content|
+        expect(revision).to match(/\d+/)
+        expect(filepath).to match(%r{/HEADER.html})
+        expect(content.size).to be > 260 # need a better test
+      end
+    end
+  end
+
   describe 'ASF::SVN.svn' do
     it 'svn(nil,nil) should raise error' do
       expect { ASF::SVN.svn(nil,nil) }.to raise_error(ArgumentError, 'command 
must not be nil')
diff --git a/lib/whimsy/asf/svn.rb b/lib/whimsy/asf/svn.rb
index c438a775..7ef51c85 100644
--- a/lib/whimsy/asf/svn.rb
+++ b/lib/whimsy/asf/svn.rb
@@ -616,6 +616,7 @@ module ASF
 
     # retrieve revision, content for a file in svn
     # N.B. There is a window between fetching the revision and getting the 
file contents
+    # Returns ['0', nil] if the revision cannot be determined
     def self.get(path, user=nil, password=nil)
       revision, _ = self.getInfoItem(path, 'revision', {user: user, password: 
password})
       if revision
@@ -677,6 +678,84 @@ module ASF
       end
     end
 
+    # retrieve last-changed-revision (string), content for a file in svn
+    # ensures revision agrees with file content
+    # Params:
+    # url - URL of file in SVN (must exist)
+    # env - user/password from Auth
+    # block - if passed a block, yields revision, filepath, contents
+    # else returns: revision, content
+    def self.getfile(url, env=nil, &block)
+
+      dir, _, basename = url.rpartition('/')
+
+      rc = 0
+      Dir.mktmpdir do |tmpdir|
+        # create an empty checkout
+        self.svn!('checkout', [dir, tmpdir], {depth: 'empty', env: env})
+
+        # retrieve the file to be updated
+        tmpfile = File.join(tmpdir, basename)
+
+        self.svn!('update', tmpfile, {env: env})
+
+        revision, err = self.getInfoItem(tmpfile, 'last-changed-revision')
+        raise Exception.new("getfile:getInfoItem failed with #{err}") unless 
err.nil?
+        if block
+          yield revision, tmpfile, File.read(tmpfile)
+        else
+          return revision, File.read(tmpfile)
+        end
+      end      
+    end
+
+    # update a file in SVN, working entirely in a temporary directory
+    # Intended for use from GUI code
+    # Must be used with a block, which is passed the temporary directory name
+    # and the current file contents (may be empty string)
+    # The block must return the updated file contents
+    #
+    # Parameters:
+    # url - the SVN url to be used which must exist as a file
+    # msg - commit message
+    # env - environment (queried for user and password)
+    # wunderbar - wunderbar context
+    # expected_revision - revision that is being updated (string)
+    # options - hash of:
+    #  :dryrun - show command (excluding credentials), without executing it
+    #  :diff - show diff before committing
+    def self.updatefile(url, msg, env, wunderbar, expected_revision, 
options={})
+      rc = 0
+      self.getfile(url, env) do |actual_revision, tmpfile, previous_contents|
+
+        if actual_revision != expected_revision
+          if options[:dryrun]
+            return "svn revision check failed: expected: #{expected_revision} 
actual: #{actual_revision}"
+          else
+            raise "svn revision check failed: expected: #{expected_revision} 
actual: #{actual_revision}"
+          end
+        end
+
+        contents = yield previous_contents
+
+        File.write tmpfile, contents
+
+        self.svn_('diff', tmpfile, wunderbar) if options[:diff] or 
options[:dryrun]
+        return if options[:dryrun]
+
+        # commit the changes
+        rc = self.svn_('commit', tmpfile, wunderbar, {msg: msg, env: env})
+
+        # fail if there are pending changes
+        out, _err = self.svn('status', tmpfile) # Need to use svn rather than 
svn_ here
+        unless rc == 0 && out && out.empty?
+          raise "svn failure #{rc} #{url.inspect} #{out}"
+        end
+
+      end
+      rc # return last status
+    end
+    
     # update a file or directory in SVN, working entirely in a temporary
     # directory
     # Intended for use from GUI code

Reply via email to