code_cache 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|