Thursday, October 13, 2011

Subversion and Third Party Software

My team uses several third-party libraries and applications for our development efforts. Some of these third-party packages are downloaded source packages, and others are pulled down as a Subversion working copy. I want all third-party packages saved in our Subversion repository so that we can easily track the versions we are using. This poses a problem: if I simply copy the vendor's working copy into my repository's working copy, I'll blow away my repository's svn connection information. So how do I make this work?

Subversion's treatment of handling a vendor branch assumes that the third-party code is arriving in a tarball, ZIP file, etc. This isn't the situation in my case, so this treatment doesn't seem like a great fix.

I looked into using Subversion's svn:externals property to create a directory in my repository's space that points directly to a third-party repository. This approach would work really well, except that we quite often make tweaks to the third-party applications to support our particular environment.

In the end, I needed a way to copy the contents of the vendor working copy into my repository's working copy, without bringing over any of the .svn directories. To accomplish this task, I wrote the following Ruby script:


require "optparse"

FROM_DIR = "c:\\fromdir"
TO_DIR = "c:\\todir"

compare_only = false
copy_only = false
fromdir = "" #FROM_DIR
todir = "" #TO_DIR

ARGV.options { |opt|
opt.banner = "Usage: Copy source from a vendor
working copy to an internal vendor copy."
opt.on_head("Options:")
opt.on("--from-directory fromdir", "-f",
"Directory that contains vendor source.") do |fdir| fromdir << fdir end
opt.on("--to-directory todir", "-t",
"Directory that the vendor source gets copied
to (vendor branch).") do |tdir| todir << tdir end
opt.on("--compare-only", "-o",
"Only compare the contents of the FROM directory
to the TO directory.") { compare_only = true }
opt.on("--copy-only", "-c",
"Only do the file copy. Don't compare directories
to validate results.") { copy_only = true }

opt.parse!
}

def crawl_dir(starting_dir, destination_dir)
Dir.foreach(starting_dir) { |entry|
if(entry.index(".svn") == nil &&
entry != "." &&
entry != "..")
if(File.directory?(starting_dir + entry))
if(!Dir.exists?(destination_dir + entry))
#puts "Create Directory #{destination_dir + entry}"
Dir.mkdir(destination_dir + entry)
end
crawl_dir(starting_dir + entry + "\\", destination_dir + entry + "\\")
else
#puts "Copy File #{destination_dir + entry}"
`copy /Y #{starting_dir + entry} #{destination_dir + entry}`
end
end
}
end

@missingstuff = Array.new
def add_missing_stuff(item)
@missingstuff.push(item)
end

def compare_dir(starting_dir, destination_dir)
Dir.foreach(starting_dir) { |entry|
if(entry.index(".svn") == nil &&
entry != "." &&
entry != "..")
if(File.directory?(starting_dir + entry))
if(!Dir.exists?(destination_dir + entry))
add_missing_stuff(destination_dir + entry)
end
compare_dir(starting_dir + entry + "\\", destination_dir + entry + "\\")
else
if(!File.exists?(destination_dir + entry))
add_missing_stuff(destination_dir + entry)
end
end
end
}
end

if(!compare_only)
crawl_dir(fromdir, todir)
end
if(!copy_only)
compare_dir(fromdir, todir)
@missingstuff.each { |item|
puts "Missing #{item}"
}
end


The script isn't perfect, but it got me started. It does take the FROM and TO directories as arguments, and allows the user to specify the operations desired. The user can choose to only copy files, to only compare a destination directory to a source directory, or to copy files and compare the results (default action).

Hopefully I'll be able to refine the script as we use it.

No comments:

Post a Comment