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.
- checksums.yaml +7 -0
- data/README.md +24 -0
- data/lib/code_cache.rb +22 -0
- data/lib/code_cache/repo.rb +56 -0
- data/lib/code_cache/repo/git.rb +73 -0
- data/lib/code_cache/repo/svn.rb +99 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -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
|
data/README.md
ADDED
@@ -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
|
data/lib/code_cache.rb
ADDED
@@ -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: []
|