gem_on_demand 1.0.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 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%