code_cache 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9b35ba34266cc6f725541d547606b0bda66347ff
4
+ data.tar.gz: 7b9e153b63507075879e93025035381296f86a55
5
+ SHA512:
6
+ metadata.gz: a0f4c2d7416cd4c446c3662cd307d3a712ea9957a505ad200b04f23f7e05416fa631f23ce48e483607749231fa03d7f0f4f86517a51adc3b6aa1f0e0d8db9e1f
7
+ data.tar.gz: fef3d5e9b7294679c4ef00497dcb4c83167968872a5f180821a8226b2adfd00d15b4cd9cdd5d7577af6f861d3c5655f1c425b9880b4f3c6de262d233dd0f240e
@@ -0,0 +1,24 @@
1
+ # code_cache
2
+ Version control checkout abstraction and cache.
3
+
4
+ ## Usage
5
+
6
+ repo = CodeCache.repo( repo_url )
7
+ begin
8
+ repo.checkout( :head, 'path/to/checkout' )
9
+ rescue => e
10
+ puts "Checkout failed"
11
+ end
12
+
13
+ ## Testing
14
+
15
+ Tests run against the github svn and git endpoints for this repository. Just run:
16
+
17
+ bundle
18
+ bundle exec rspec
19
+
20
+ ## License
21
+
22
+ Code Cache is available to everyone under the terms of the MIT open source licence. Take a look at the LICENSE file in the code.
23
+
24
+ Copyright (c) 2015 BBC
@@ -0,0 +1,22 @@
1
+ require 'code_cache/repo/git'
2
+ require 'code_cache/repo/svn'
3
+
4
+ module CodeCache
5
+
6
+ def self.identify(url)
7
+ if (url =~ /.*\.git\s*$/)
8
+ :git
9
+ else
10
+ :svn
11
+ end
12
+ end
13
+
14
+ def self.repo(url)
15
+ if self.identify(url) == :git
16
+ Repo::Git.new(url)
17
+ else
18
+ Repo::SVN.new(url)
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,56 @@
1
+ require 'fileutils'
2
+
3
+ module CodeCache
4
+
5
+ class Repo
6
+
7
+ attr_accessor :url, :cache
8
+
9
+ def initialize(url, options = {})
10
+ @cache = options[:cache] || '/tmp/code_cache'
11
+ @url = url
12
+
13
+ check_repo(url)
14
+ end
15
+
16
+ def create_cache(revision)
17
+ if !Dir.exist? location_in_cache(revision)
18
+ FileUtils.mkdir_p @cache
19
+ end
20
+ end
21
+
22
+ # Calculates the location of a cached checkout
23
+ def location_in_cache( revision = nil )
24
+ begin
25
+ elements = [cache, repo_type, split_url, revision].flatten.compact.collect { |i| i.to_s }
26
+ File.join( elements )
27
+ rescue => e
28
+ raise CacheCalculationError.new(e.msg + e.backtrace.to_s)
29
+ end
30
+ end
31
+
32
+ def repo_type
33
+ self.class.to_s.split('::').last.downcase
34
+ end
35
+
36
+ end
37
+
38
+ class BadRepo < StandardError
39
+ end
40
+
41
+ class UnknownCheckoutError < StandardError
42
+ end
43
+
44
+ class CacheCalculationError < StandardError
45
+ end
46
+
47
+ class CacheCorruptionError < StandardError
48
+ end
49
+
50
+ class UpdateError < StandardError
51
+ end
52
+
53
+ class CopyError < StandardError
54
+ end
55
+
56
+ end
@@ -0,0 +1,73 @@
1
+ require 'code_cache/repo'
2
+
3
+ module CodeCache
4
+ class Repo::Git < Repo
5
+
6
+ def initialize(url, options = {})
7
+ super(url, options)
8
+ end
9
+
10
+ # Split the url into a unique array for the cache path
11
+ def split_url
12
+ match = /(?:(?:git|ssh)@(.*):|(?:https?:\/\/(.*?)\/))(.*).git/.match(url)
13
+ match.captures.compact
14
+ end
15
+
16
+ # Check access to the repo in question
17
+ def check_repo(url)
18
+ output = `git ls-remote #{url} 2>&1`
19
+ if ($? != 0)
20
+ raise BadRepo.new(url + "<<<" + output.to_s + ">>>")
21
+ end
22
+ end
23
+
24
+ # Checkout a particular revision from the repo into the destination
25
+ # Caches the checkout in the process
26
+ def checkout( revision, destination )
27
+
28
+ if revision != :head
29
+ raise "Checking out anything other than the head of the default branch not supported"
30
+ end
31
+
32
+ cache_destination = location_in_cache()
33
+
34
+ # Try and copy into the cache first
35
+ clone_or_update_repo_in_cache(cache_destination)
36
+
37
+ puts "Cache: #{cache_destination}"
38
+ puts "Destination: #{destination}"
39
+
40
+ checkout_from_cache_to_destination(cache_destination, destination, revision)
41
+
42
+ true
43
+ end
44
+
45
+ def clone_or_update_repo_in_cache(cache_destination)
46
+ update_result = update_cache(cache_destination)
47
+ if update_result[:status] == true
48
+ return true
49
+ else
50
+ clone_bare_repo_to_cache(cache_destination)
51
+ end
52
+ end
53
+
54
+ def update_cache(cache_destination)
55
+ output = `GIT_DIR=#{cache_destination} git fetch origin +refs/heads/*:refs/heads/* 2>&1`
56
+ status = $? == 0
57
+ { :output => output, :status => status }
58
+ end
59
+
60
+ def clone_bare_repo_to_cache(cache_destination)
61
+ output = `git clone --bare #{url} #{cache_destination} 2>&1`
62
+ status = $? == 0
63
+ { :output => output, :status => status }
64
+ end
65
+
66
+ def checkout_from_cache_to_destination(cache_destination, destination, revision)
67
+ output = `git clone --single-branch #{cache_destination} #{destination} 2>&1`
68
+ status = $? == 0
69
+ { :output => output, :status => status }
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,99 @@
1
+ require 'code_cache/repo'
2
+
3
+ module CodeCache
4
+ class Repo::SVN < Repo
5
+
6
+ def initialize(url, options = {})
7
+ super(url, options)
8
+ end
9
+
10
+ # Checkout a particular revision from the repo into the destination
11
+ # Caches the checkout in the process
12
+ def checkout( revision, destination )
13
+
14
+ if revision != :head
15
+ raise "Checking out revisions other than head currently not supported"
16
+ end
17
+
18
+ cache_destination = location_in_cache(revision)
19
+
20
+ # Try and copy into the cache first
21
+ begin
22
+ perform_cache_checkout_or_update(cache_destination)
23
+ FileUtils.mkdir_p(destination)
24
+
25
+ FileUtils.cp_r(cache_destination+'/.', destination)
26
+ # Ensure the final copy is consistent
27
+ raise CopyError if !Dir.exist?(destination)
28
+ output = perform_update(destination)
29
+ if !output[:status]
30
+ raise UpdateError.new(output)
31
+ end
32
+
33
+ rescue => e
34
+ puts "Unhandled randomness: " + e.to_s + ' - ' + e.backtrace.to_s
35
+ # If we can't copy into the cache, copy directly to the destination
36
+ perform_checkout(destination)
37
+ end
38
+
39
+ true
40
+ end
41
+
42
+ def perform_cache_checkout_or_update(destination, attempts = 3)
43
+ update_result = perform_update(destination)
44
+ if update_result[:status] == true
45
+ return true
46
+ else
47
+ checkout_result = perform_checkout(destination)
48
+ if checkout_result[:status]
49
+ return true
50
+ else
51
+ # Cache update in progress, retry with some sleeps
52
+ if checkout_result[:output] =~ /E155037/
53
+ sleep 1
54
+ if attempts > 0
55
+ perform_cache_checkout_or_update( destination, attempts-1 )
56
+ else
57
+ return false
58
+ end
59
+
60
+ # Cache has become corrupted
61
+ elsif checkout_result[:output] =~ /E155004/
62
+ raise CacheCorruptionError.new(url)
63
+ else
64
+ raise UnknownCheckoutError.new(url)
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ # Perform an svn update on a directory -- checks its a valid svn dir first
71
+ def perform_update(destination)
72
+ output = `svn info #{destination} 2>&1 && svn up #{destination} 2>&1`
73
+ status = $? == 0
74
+ { :output => output, :status => status }
75
+ end
76
+
77
+ def perform_checkout(destination)
78
+ output = `svn co #{url} #{destination} 2>&1`
79
+ status = $? == 0
80
+ { :output => output, :status => status }
81
+ end
82
+
83
+ # Check access to the repo in question
84
+ def check_repo(url)
85
+ output = `svn ls #{url} 2>&1`
86
+ if ($? != 0)
87
+ raise BadRepo.new(url + "<<<" + output.to_s + ">>>")
88
+ end
89
+ end
90
+
91
+ # Split the url into a unique array for the cache path
92
+ def split_url
93
+ url.split('//').drop(1).collect{ |e| e.split('/') }.flatten
94
+ end
95
+ end
96
+
97
+
98
+
99
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: code_cache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - David Buckhurst
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.3'
27
+ description: Provides a simple api for checking out svn and git repositories. Caches
28
+ checkouts locally so that subsequent checkouts are optimised.
29
+ email: david.buckhurst@bbc.co.uk
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - README.md
35
+ - lib/code_cache.rb
36
+ - lib/code_cache/repo.rb
37
+ - lib/code_cache/repo/git.rb
38
+ - lib/code_cache/repo/svn.rb
39
+ homepage: https://github.com/bbc/code_cache
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.4.8
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Abstracts & caches svn & git operations
63
+ test_files: []