gem_on_demand 1.0.0 → 1.1.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/gem_on_demand.rb +23 -153
- data/lib/gem_on_demand/checkout.rb +80 -0
- data/lib/gem_on_demand/file_cache.rb +28 -0
- data/lib/gem_on_demand/project.rb +57 -0
- data/lib/gem_on_demand/utils.rb +31 -0
- data/lib/gem_on_demand/version.rb +1 -1
- metadata +6 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b6bcd00804e18316740f3994fedc5e6023610c5
|
4
|
+
data.tar.gz: c8ea84b03172bbe87a8e51365870e229e5a3a973
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe4905d4fd9018f8028bddf38c61ef28d0346b7e4e91c0af4f007e36c770669e1f36acd28641d7eb055f41555d230de15bf80b9139cf15101971082364158b76
|
7
|
+
data.tar.gz: 802126017f0d2d5b05a88d18514246e7c76469ca65c8cf7df5381d71b61bcd0641fd762886a5e7e8cd87bdb0e3142db4be1c2ac6b695fc3fd124d7375aa1b5e2
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/lib/gem_on_demand.rb
CHANGED
@@ -1,173 +1,43 @@
|
|
1
1
|
require 'tmpdir'
|
2
|
+
require 'gem_on_demand/checkout'
|
3
|
+
require 'gem_on_demand/project'
|
4
|
+
require 'gem_on_demand/utils'
|
5
|
+
require 'gem_on_demand/file_cache'
|
2
6
|
|
3
7
|
module GemOnDemand
|
4
|
-
PROJECT_CACHE = File.expand_path("~/.gem-on-demand/cache")
|
5
|
-
DATA_CACHE = "cache"
|
6
|
-
CACHE_DURATION = 15 * 60 # for project tags
|
7
|
-
ProjectNotFound = Class.new(Exception)
|
8
|
-
VERSION_REX = /^v?\d+\.\d+\.\d+(\.\w+)?$/ # with or without v and pre-release (cannot do others or we get: 'Malformed version number string 1.0.0-rails3' from bundler)
|
9
8
|
HEAVY_FORKED = ["rails", "mysql", "mysql2"]
|
10
|
-
MAX_VERSIONS = 50 # some projects just have a million versions ...
|
11
|
-
DEPENDENCIES = "dependencies"
|
12
|
-
NOT_FOUND = "not-found"
|
13
|
-
UPDATED_AT = "updated_at"
|
14
9
|
|
15
10
|
class << self
|
16
11
|
def build_gem(user, project, version)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
gemspec = "#{project}.gemspec"
|
21
|
-
remove_signing(gemspec)
|
22
|
-
sh("gem build #{gemspec}")
|
23
|
-
File.read("#{project}-#{version}.gem")
|
24
|
-
end
|
12
|
+
checkout = Checkout.new(user, project)
|
13
|
+
checkout.chdir do
|
14
|
+
Project.new(user, project, checkout.cache).build_gem(version)
|
25
15
|
end
|
26
16
|
end
|
27
17
|
|
28
18
|
def dependencies(user, gems)
|
29
19
|
(gems - HEAVY_FORKED).map do |project|
|
30
|
-
|
20
|
+
checkout = Checkout.new(user, project)
|
21
|
+
begin
|
22
|
+
checkout.chdir do
|
23
|
+
Project.new(user, project, checkout.cache).dependencies
|
24
|
+
end
|
25
|
+
rescue Checkout::NotFound
|
26
|
+
[]
|
27
|
+
end
|
31
28
|
end.flatten
|
32
29
|
end
|
33
30
|
|
31
|
+
# expire update related caches so next run gets fresh tags + versions
|
34
32
|
def expire(user, project)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
cache DEPENDENCIES do
|
43
|
-
versions_for_project.last(MAX_VERSIONS).map do |version|
|
44
|
-
next unless dependencies = dependencies_for_version(project, version)
|
45
|
-
{
|
46
|
-
:name => project,
|
47
|
-
:number => version.sub(/^v/, ""),
|
48
|
-
:platform => "ruby",
|
49
|
-
:dependencies => Marshal.load(dependencies)
|
50
|
-
}
|
51
|
-
end.compact
|
52
|
-
end
|
53
|
-
end
|
54
|
-
rescue ProjectNotFound
|
55
|
-
[]
|
56
|
-
end
|
57
|
-
|
58
|
-
def versions_for_project
|
59
|
-
sh("git tag").split($/).grep(VERSION_REX)
|
60
|
-
end
|
61
|
-
|
62
|
-
def dependencies_for_version(project, version)
|
63
|
-
cache "dependencies-#{version}" do
|
64
|
-
checkout_version(version)
|
65
|
-
sh(%{ruby -e 'print Marshal.dump(eval(File.read("#{project}.gemspec")).runtime_dependencies.map{|d| [d.name, d.requirement.to_s]})'}, :fail => :allow)
|
33
|
+
checkout = Checkout.new(user, project)
|
34
|
+
[
|
35
|
+
Checkout::UPDATED_AT,
|
36
|
+
Checkout::NOT_FOUND,
|
37
|
+
Project::DEPENDENCIES
|
38
|
+
].each do |key|
|
39
|
+
checkout.cache.delete key
|
66
40
|
end
|
67
41
|
end
|
68
|
-
|
69
|
-
def cache(file, value = nil, &block)
|
70
|
-
ensure_directory(DATA_CACHE)
|
71
|
-
file = "#{DATA_CACHE}/#{file}"
|
72
|
-
if block
|
73
|
-
if File.exist?(file)
|
74
|
-
Marshal.load(File.read(file))
|
75
|
-
else
|
76
|
-
result = yield
|
77
|
-
File.write(file, Marshal.dump(result))
|
78
|
-
result
|
79
|
-
end
|
80
|
-
else
|
81
|
-
if value.nil?
|
82
|
-
Marshal.load(File.read(file)) if File.exist?(file)
|
83
|
-
else
|
84
|
-
File.write(file, Marshal.dump(value))
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def expire_key(key)
|
90
|
-
key = "#{DATA_CACHE}/#{key}"
|
91
|
-
File.unlink(key) if File.exist?(key)
|
92
|
-
end
|
93
|
-
|
94
|
-
def sh(command, options = { })
|
95
|
-
puts command
|
96
|
-
result = `#{command}`
|
97
|
-
if $?.success?
|
98
|
-
result
|
99
|
-
elsif options[:fail] == :allow
|
100
|
-
false
|
101
|
-
else
|
102
|
-
raise "Command failed: #{result}"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def inside_of_project(user, project, &block)
|
107
|
-
dir = "#{PROJECT_CACHE}/#{user}"
|
108
|
-
ensure_directory(dir)
|
109
|
-
Dir.chdir(dir) do
|
110
|
-
clone_or_refresh_project(user, project)
|
111
|
-
Dir.chdir(project, &block)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def ensure_directory(dir)
|
116
|
-
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
117
|
-
end
|
118
|
-
|
119
|
-
def clone_or_refresh_project(user, project)
|
120
|
-
if File.directory?("#{project}/.git")
|
121
|
-
if refresh?(project)
|
122
|
-
Dir.chdir(project) do
|
123
|
-
sh "git fetch origin"
|
124
|
-
expire_key DEPENDENCIES
|
125
|
-
end
|
126
|
-
refreshed!(project)
|
127
|
-
end
|
128
|
-
elsif not_found?(project)
|
129
|
-
raise ProjectNotFound
|
130
|
-
else
|
131
|
-
remove_directory(project)
|
132
|
-
found = sh "git clone git@github.com:#{user}/#{project}.git", :fail => :allow
|
133
|
-
if found
|
134
|
-
refreshed!(project)
|
135
|
-
else
|
136
|
-
not_found!(project)
|
137
|
-
raise ProjectNotFound
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def remove_directory(project)
|
143
|
-
FileUtils.rm_rf(project) if File.exist?(project)
|
144
|
-
end
|
145
|
-
|
146
|
-
def not_found?(project)
|
147
|
-
File.directory?(project) && Dir.chdir(project) { cache(NOT_FOUND) }
|
148
|
-
end
|
149
|
-
|
150
|
-
def not_found!(project)
|
151
|
-
ensure_directory(project)
|
152
|
-
Dir.chdir(project) { cache(NOT_FOUND, true) }
|
153
|
-
end
|
154
|
-
|
155
|
-
def refreshed!(project)
|
156
|
-
Dir.chdir(project) { cache(UPDATED_AT, Time.now.to_i) }
|
157
|
-
end
|
158
|
-
|
159
|
-
def refresh?(project)
|
160
|
-
Dir.chdir(project) { cache(UPDATED_AT).to_i } < Time.now.to_i - CACHE_DURATION
|
161
|
-
end
|
162
|
-
|
163
|
-
def checkout_version(version)
|
164
|
-
sh("git checkout #{version} --force")
|
165
|
-
end
|
166
|
-
|
167
|
-
# ERROR: While executing gem ... (Gem::Security::Exception)
|
168
|
-
# certificate /CN=michael/DC=grosser/DC=it not valid after 2014-02-03 18:13:11 UTC
|
169
|
-
def remove_signing(gemspec)
|
170
|
-
File.write(gemspec, File.read(gemspec).gsub(/.*\.(signing_key|cert_chain).*/, ""))
|
171
|
-
end
|
172
42
|
end
|
173
43
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module GemOnDemand
|
2
|
+
class Checkout
|
3
|
+
NotFound = Class.new(Exception)
|
4
|
+
NOT_FOUND = "not-found"
|
5
|
+
UPDATED_AT = "updated_at"
|
6
|
+
DIR = File.expand_path("~/.gem-on-demand/cache")
|
7
|
+
CACHE_DURATION = 15 * 60 # for project tags
|
8
|
+
|
9
|
+
attr_accessor :user, :project
|
10
|
+
|
11
|
+
def initialize(user, project)
|
12
|
+
self.user = user
|
13
|
+
self.project = project
|
14
|
+
Utils.ensure_directory(dir)
|
15
|
+
end
|
16
|
+
|
17
|
+
def chdir(&block)
|
18
|
+
clone_or_refresh
|
19
|
+
Dir.chdir(dir, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def cache
|
23
|
+
@cache ||= FileCache.new("#{dir}/cache")
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def dir
|
29
|
+
"#{DIR}/#{user}/#{project}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def was_not_found?
|
33
|
+
cache.read(NOT_FOUND)
|
34
|
+
end
|
35
|
+
|
36
|
+
def not_found!
|
37
|
+
cache.write(NOT_FOUND, true)
|
38
|
+
raise NotFound
|
39
|
+
end
|
40
|
+
|
41
|
+
def need_refresh?
|
42
|
+
cache.read(UPDATED_AT).to_i < Time.now.to_i - CACHE_DURATION
|
43
|
+
end
|
44
|
+
|
45
|
+
def fresh!
|
46
|
+
cache.write(UPDATED_AT, Time.now.to_i)
|
47
|
+
end
|
48
|
+
|
49
|
+
def clone_or_refresh
|
50
|
+
if cloned?
|
51
|
+
refresh if need_refresh?
|
52
|
+
elsif was_not_found?
|
53
|
+
not_found!
|
54
|
+
else
|
55
|
+
clone
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def refresh
|
60
|
+
Dir.chdir(dir) { Utils.sh "git fetch origin" }
|
61
|
+
cache.delete Project::DEPENDENCIES
|
62
|
+
fresh!
|
63
|
+
end
|
64
|
+
|
65
|
+
def clone
|
66
|
+
Utils.remove_directory(dir)
|
67
|
+
cloned = Utils.sh "git clone git@github.com:#{user}/#{project}.git #{dir}", :fail => :allow
|
68
|
+
Utils.ensure_directory(dir)
|
69
|
+
if cloned
|
70
|
+
fresh!
|
71
|
+
else
|
72
|
+
not_found!
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def cloned?
|
77
|
+
File.directory?("#{dir}/.git")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module GemOnDemand
|
2
|
+
class FileCache
|
3
|
+
def initialize(dir)
|
4
|
+
@dir = dir
|
5
|
+
end
|
6
|
+
|
7
|
+
def write(file, value)
|
8
|
+
Utils.ensure_directory(@dir)
|
9
|
+
file = "#{@dir}/#{file}"
|
10
|
+
File.write(file, Marshal.dump(value))
|
11
|
+
value
|
12
|
+
end
|
13
|
+
|
14
|
+
def read(file)
|
15
|
+
file = "#{@dir}/#{file}"
|
16
|
+
Marshal.load(File.read(file)) if File.exist?(file)
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete(file)
|
20
|
+
file = "#{@dir}/#{file}"
|
21
|
+
File.unlink(file) if File.exist?(file)
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch(file, &block)
|
25
|
+
read(file) || write(file, yield)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module GemOnDemand
|
2
|
+
class Project
|
3
|
+
MAX_VERSIONS = 50 # some projects just have a million versions ...
|
4
|
+
DEPENDENCIES = "dependencies"
|
5
|
+
VERSION_REX = /^v?\d+\.\d+\.\d+(\.\w+)?$/ # with or without v and pre-release (cannot do others or we get: 'Malformed version number string 1.0.0-rails3' from bundler)
|
6
|
+
|
7
|
+
attr_accessor :user, :name, :cache
|
8
|
+
|
9
|
+
def initialize(user, name, cache)
|
10
|
+
self.user = user
|
11
|
+
self.name = name
|
12
|
+
self.cache = cache
|
13
|
+
end
|
14
|
+
|
15
|
+
def dependencies
|
16
|
+
cache.fetch DEPENDENCIES do
|
17
|
+
versions.last(MAX_VERSIONS).map do |version|
|
18
|
+
next unless dependencies = dependencies_for_version(version)
|
19
|
+
{
|
20
|
+
:name => name,
|
21
|
+
:number => version.sub(/^v/, ""),
|
22
|
+
:platform => "ruby",
|
23
|
+
:dependencies => dependencies
|
24
|
+
}
|
25
|
+
end.compact
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_gem(version)
|
30
|
+
cache.fetch("gem-#{version}") do
|
31
|
+
checkout_version("v#{version}")
|
32
|
+
gemspec = "#{name}.gemspec"
|
33
|
+
Utils.remove_signing(gemspec)
|
34
|
+
Utils.sh("gem build #{gemspec}")
|
35
|
+
File.read("#{name}-#{version}.gem")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def dependencies_for_version(version)
|
42
|
+
cache.fetch "dependencies-#{version}" do
|
43
|
+
checkout_version(version)
|
44
|
+
result = Utils.sh(%{ruby -e 'print Marshal.dump(eval(File.read("#{name}.gemspec")).runtime_dependencies.map{|d| [d.name, d.requirement.to_s]})'}, :fail => :allow)
|
45
|
+
Marshal.load(result) if result
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def checkout_version(version)
|
50
|
+
Utils.sh("git checkout #{version} --force")
|
51
|
+
end
|
52
|
+
|
53
|
+
def versions
|
54
|
+
Utils.sh("git tag").split($/).grep(VERSION_REX)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module GemOnDemand
|
2
|
+
module Utils
|
3
|
+
class << self
|
4
|
+
def sh(command, options = { })
|
5
|
+
puts command
|
6
|
+
result = `#{command}`
|
7
|
+
if $?.success?
|
8
|
+
result
|
9
|
+
elsif options[:fail] == :allow
|
10
|
+
false
|
11
|
+
else
|
12
|
+
raise "Command failed: #{result}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def ensure_directory(dir)
|
17
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove_directory(dir)
|
21
|
+
FileUtils.rm_rf(dir) if File.exist?(dir)
|
22
|
+
end
|
23
|
+
|
24
|
+
# ERROR: While executing gem ... (Gem::Security::Exception)
|
25
|
+
# certificate /CN=michael/DC=grosser/DC=it not valid after 2014-02-03 18:13:11 UTC
|
26
|
+
def remove_signing(gemspec)
|
27
|
+
File.write(gemspec, File.read(gemspec).gsub(/.*\.(signing_key|cert_chain).*/, ""))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gem_on_demand
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
F5etKHZg0j3eHO31/i2HnswY04lqGImUu6aM5EnijFTB7PPW2KwKKM4+kKDYFdlw
|
31
31
|
/0WV1Ng2/Y6qsHwmqGg2VlYj2h4=
|
32
32
|
-----END CERTIFICATE-----
|
33
|
-
date: 2014-03-
|
33
|
+
date: 2014-03-02 00:00:00.000000000 Z
|
34
34
|
dependencies:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: sinatra
|
@@ -54,8 +54,12 @@ extra_rdoc_files: []
|
|
54
54
|
files:
|
55
55
|
- MIT-LICENSE.txt
|
56
56
|
- lib/gem_on_demand.rb
|
57
|
+
- lib/gem_on_demand/checkout.rb
|
57
58
|
- lib/gem_on_demand/cli.rb
|
59
|
+
- lib/gem_on_demand/file_cache.rb
|
60
|
+
- lib/gem_on_demand/project.rb
|
58
61
|
- lib/gem_on_demand/server.rb
|
62
|
+
- lib/gem_on_demand/utils.rb
|
59
63
|
- lib/gem_on_demand/version.rb
|
60
64
|
homepage: https://github.com/grosser/gem_on_demand
|
61
65
|
licenses:
|
metadata.gz.sig
CHANGED
Binary file
|