gem_on_demand 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 517e2aeefdc0e9f09c3432de6c218d87f6f67587
4
+ data.tar.gz: 2d79c118bec199a2af27b74a90096ce852bc0840
5
+ SHA512:
6
+ metadata.gz: e8f8848702f5eacf1480cc2468337c4fc1cc9516f1b944a88cbc513fd8dae8558d01f6e7b49fda3eb66b5f99ea26964e81eebf248a5a626498780e5f9cc7d8c3
7
+ data.tar.gz: 78b4e5368d71432e328e1c1547dc8d48a2ac1146cea2b95c9d99eebd8fa32126e8bf9940d7d9173c8682dd7d44a6896b50d04f125764654337a153e23d41837f
checksums.yaml.gz.sig ADDED
Binary file
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (C) 2013 Michael Grosser <michael@grosser.it>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,49 @@
1
+ require 'optparse'
2
+ require 'gem_on_demand/version'
3
+ require 'gem_on_demand/server'
4
+
5
+ module GemOnDemand
6
+ class CLI
7
+ class << self
8
+ def run(argv)
9
+ options = parse_options(argv)
10
+ if expire = options[:expire]
11
+ GemOnDemand.expire(*expire.split("/"))
12
+ else
13
+ GemOnDemand::Server.run!(options)
14
+ end
15
+ 0
16
+ end
17
+
18
+ private
19
+
20
+ def parse_options(argv)
21
+ options = {}
22
+ parser = OptionParser.new do |opts|
23
+ opts.banner = <<-BANNER.gsub(/^ {12}/, "")
24
+ Run your own gem server that fetches from github, uses tags as version and builds gems on demand
25
+
26
+ Usage:
27
+ gem-on-demand --server
28
+
29
+ Options:
30
+ BANNER
31
+ opts.on("-s", "--server", "Start server") { options[:server] = true }
32
+ opts.on("-e", "--expire NAME", String, "Expire gem cache for {user}/{project}") { |name| options[:expire] = name }
33
+ opts.on("-p", "--port PORT", Integer, "Port for server (default #{GemOnDemand::Server.port})") { |port| options[:port] = port }
34
+ opts.on("-h", "--help", "Show this.") { puts opts; exit }
35
+ opts.on("-v", "--version", "Show Version"){ puts GemOnDemand::VERSION; exit}
36
+ end
37
+ parser.parse!(argv)
38
+
39
+ # force server for now ...
40
+ if !options.delete(:server) && !options[:expire]
41
+ puts parser
42
+ exit 1
43
+ end
44
+
45
+ options
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,30 @@
1
+ require 'sinatra/base'
2
+ require 'gem_on_demand'
3
+
4
+ module GemOnDemand
5
+ class Server < Sinatra::Base
6
+ set :port, 7154
7
+ set :lock, true # multi threading is not supported when doing chdir foo
8
+
9
+ get '/:username/api/v1/dependencies' do
10
+ user = params[:username]
11
+ if gems = params[:gems]
12
+ dependencies = GemOnDemand.dependencies(user, gems.split(","))
13
+ if params[:debug]
14
+ dependencies.inspect
15
+ else
16
+ Marshal.dump(dependencies)
17
+ end
18
+ else
19
+ "" # first request wants no response ...
20
+ end
21
+ end
22
+
23
+ get '/:username/gems/:project-:version.gem' do
24
+ user = params[:username]
25
+ project = params[:project]
26
+ version = params[:version]
27
+ GemOnDemand.build_gem(user, project, version)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module GemOnDemand
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,173 @@
1
+ require 'tmpdir'
2
+
3
+ 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
+ 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
+
15
+ class << self
16
+ def build_gem(user, project, version)
17
+ inside_of_project(user, project) do
18
+ cache("gem-#{version}") do
19
+ checkout_version("v#{version}")
20
+ gemspec = "#{project}.gemspec"
21
+ remove_signing(gemspec)
22
+ sh("gem build #{gemspec}")
23
+ File.read("#{project}-#{version}.gem")
24
+ end
25
+ end
26
+ end
27
+
28
+ def dependencies(user, gems)
29
+ (gems - HEAVY_FORKED).map do |project|
30
+ project_dependencies(user, project)
31
+ end.flatten
32
+ end
33
+
34
+ def expire(user, project)
35
+ remove_directory "#{PROJECT_CACHE}/#{user}/#{project}/#{DATA_CACHE}"
36
+ end
37
+
38
+ private
39
+
40
+ def project_dependencies(user, project)
41
+ inside_of_project(user, project) do
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)
66
+ end
67
+ 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
+ end
173
+ end
data.tar.gz.sig ADDED
Binary file
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gem_on_demand
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Grosser
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MRAwDgYDVQQDDAdtaWNo
14
+ YWVsMRcwFQYKCZImiZPyLGQBGRYHZ3Jvc3NlcjESMBAGCgmSJomT8ixkARkWAml0
15
+ MB4XDTE0MDIwNDIwMjk0MVoXDTE1MDIwNDIwMjk0MVowPzEQMA4GA1UEAwwHbWlj
16
+ aGFlbDEXMBUGCgmSJomT8ixkARkWB2dyb3NzZXIxEjAQBgoJkiaJk/IsZAEZFgJp
17
+ dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMorXo/hgbUq97+kII9H
18
+ MsQcLdC/7wQ1ZP2OshVHPkeP0qH8MBHGg6eYisOX2ubNagF9YTCZWnhrdKrwpLOO
19
+ cPLaZbjUjljJ3cQR3B8Yn1veV5IhG86QseTBjymzJWsLpqJ1UZGpfB9tXcsFtuxO
20
+ 6vHvcIHdzvc/OUkICttLbH+1qb6rsHUceqh+JrH4GrsJ5H4hAfIdyS2XMK7YRKbh
21
+ h+IBu6dFWJJByzFsYmV1PDXln3UBmgAt65cmCu4qPfThioCGDzbSJrGDGLmw/pFX
22
+ FPpVCm1zgYSb1v6Qnf3cgXa2f2wYGm17+zAVyIDpwryFru9yF/jJxE38z/DRsd9R
23
+ /88CAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFLIj
24
+ Z1x7SnjGGHK+MiVZkFjjS/iMMB0GA1UdEQQWMBSBEm1pY2hhZWxAZ3Jvc3Nlci5p
25
+ dDAdBgNVHRIEFjAUgRJtaWNoYWVsQGdyb3NzZXIuaXQwDQYJKoZIhvcNAQEFBQAD
26
+ ggEBAExBcUWfGuamYn+IddOA0Ws8jUKwB14RXoZRDrTiTAlMm3Bkg2OKyxS3uJXa
27
+ 6Z+LwFiZwVYk62yHXqNzEJycQk4SEmY+xDWLj0p7X6qEeU4QZKwR1TwJ5z3PTrZ6
28
+ irJgM3q7NIBRvmTzRaAghWcQn+Eyr5YLOfMksjVBMUMnzh5/ZDgq53LphgJbGwvz
29
+ ScJAgfNclLHnjk9q1mT1s0e1FPWbiAL3siBIR5HpH8qtSEiivTf2ntciebOqS93f
30
+ F5etKHZg0j3eHO31/i2HnswY04lqGImUu6aM5EnijFTB7PPW2KwKKM4+kKDYFdlw
31
+ /0WV1Ng2/Y6qsHwmqGg2VlYj2h4=
32
+ -----END CERTIFICATE-----
33
+ date: 2014-03-01 00:00:00.000000000 Z
34
+ dependencies:
35
+ - !ruby/object:Gem::Dependency
36
+ name: sinatra
37
+ requirement: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ description:
50
+ email: michael@grosser.it
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - MIT-LICENSE.txt
56
+ - lib/gem_on_demand.rb
57
+ - lib/gem_on_demand/cli.rb
58
+ - lib/gem_on_demand/server.rb
59
+ - lib/gem_on_demand/version.rb
60
+ homepage: https://github.com/grosser/gem_on_demand
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.0.14
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Run your own gem server that fetches from github, uses tags as version and
84
+ builds gems on demand
85
+ test_files: []
metadata.gz.sig ADDED
@@ -0,0 +1,3 @@
1
+ ��|&|�q���k��q�N�/�_��AZ/� ��ax&�=,Q/Xx�0�?,I�O���#0���A9�!Յ9ߏ2P}��ͤM
2
+ ��T�@�zޝV��2� ~���(�*$��D$r�+g��9X��i�:e ,�7hZ�d�Nִ��������/K ������J"P�[ic�UN���"�u\1��:�\߀��H�]�"v��1
3
+ ��Hx��vݷ���o�F� 2�#��6)�����zM%