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