serverlessgems 0.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 +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +21 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/jets/gems.rb +19 -0
- data/lib/jets/gems/agree.rb +69 -0
- data/lib/jets/gems/api.rb +25 -0
- data/lib/jets/gems/api/concern.rb +7 -0
- data/lib/jets/gems/api/core.rb +81 -0
- data/lib/jets/gems/autoloader.rb +23 -0
- data/lib/jets/gems/check.rb +192 -0
- data/lib/jets/gems/config.rb +13 -0
- data/lib/jets/gems/config/token.rb +24 -0
- data/lib/jets/gems/exist.rb +32 -0
- data/lib/jets/gems/extract/base.rb +65 -0
- data/lib/jets/gems/extract/gem.rb +102 -0
- data/lib/jets/gems/report.rb +23 -0
- data/lib/jets/gems/version.rb +5 -0
- data/lib/serverlessgems.rb +1 -0
- data/serverlessgems.gemspec +31 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ee4f7043cd9fb6ace83079a2a82b3aeafb3ca2c9000e73fe6bff8dccd86b21f5
|
4
|
+
data.tar.gz: 4fc40b94cda06670351b39a401c9cb0eadb94030fd85f2c8d058590a75605f59
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 76e24348551d21d0d1266c972f11d9782c3d1a9ea36a653c62b25e4404b3a5146562c79e22df2b0ab860c0f4754e17ebf3663e9bbea15c042c673a7160dae826
|
7
|
+
data.tar.gz: 9f4f5d19b9833cf94da12b410475b44a8b519171d260b5119ab802e1fd246c8fcc6e3ddb504d7d20683a6d1a0aa3d5e8185f6a5657ae0ecde5f6cc88c2bb1869
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
This project *loosely tries* to adhere to [Semantic Versioning](http://semver.org/).
|
5
|
+
|
6
|
+
## [0.1.0]
|
7
|
+
- Initial release
|
8
|
+
- Rename to serverlessgems
|
9
|
+
- Zeitwerk autoloader
|
10
|
+
- Use Serverless Gems API
|
11
|
+
- .jets/config.yml support
|
12
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Tung Nguyen
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# serverlessgems
|
2
|
+
|
3
|
+
[](https://www.boltops.com)
|
4
|
+
|
5
|
+
[Jets](http://rubyonjets.com/) is a Ruby Serverless Framework on AWS Lambda. It allows you to create serverless applications with a beautiful language: Ruby. It includes everything required to build an application and deploy it to AWS Lambda. Jets makes serverless simple.
|
6
|
+
|
7
|
+
This library works with serverlessgems API to download and extract pre-compiled gems that work on AWS Lambda.
|
8
|
+
|
9
|
+
## Development
|
10
|
+
|
11
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
12
|
+
|
13
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
14
|
+
|
15
|
+
## Contributing
|
16
|
+
|
17
|
+
Bug reports and pull requests are welcome on GitHub at [boltops-tools/serverlessgems](https://github.com/boltops-tools/serverlessgems)
|
18
|
+
|
19
|
+
## License
|
20
|
+
|
21
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "jets/gems"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/jets/gems.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "jets"
|
2
|
+
|
3
|
+
$:.unshift(File.expand_path("../../", __FILE__))
|
4
|
+
require "jets/gems/autoloader"
|
5
|
+
Jets::Gems::Autoloader.setup
|
6
|
+
|
7
|
+
require "memoist"
|
8
|
+
require "yaml"
|
9
|
+
|
10
|
+
module Jets
|
11
|
+
module Gems
|
12
|
+
def ruby_folder
|
13
|
+
major, minor, _ = RUBY_VERSION.split('.')
|
14
|
+
[major, minor, '0'].join('.') # 2.5.1 => 2.5.0
|
15
|
+
end
|
16
|
+
|
17
|
+
extend self
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Jets::Gems
|
2
|
+
class Agree
|
3
|
+
def initialize
|
4
|
+
@agree_file = "#{ENV['HOME']}/.jets/agree"
|
5
|
+
end
|
6
|
+
|
7
|
+
# Only prompts if hasnt prompted before and saved a ~/.jets/agree file
|
8
|
+
def prompt
|
9
|
+
return if bypass_prompt
|
10
|
+
return if File.exist?(@agree_file)
|
11
|
+
|
12
|
+
puts <<~EOL
|
13
|
+
Jets sends data about your gems to serverlessgems.com so that it can build the necessary Lambda layer.
|
14
|
+
|
15
|
+
Reporting gems generally allows Serverless Gems to build new gems within a few minutes.
|
16
|
+
So if you run into missing gems, you can try deploying again after a few minutes.
|
17
|
+
Non-reported gems may take several days or longer.
|
18
|
+
|
19
|
+
Serverless Gems only collects the info it needs to run the service. More info: https://www.serverlessgems.com/privacy
|
20
|
+
|
21
|
+
This message will only appear once on this machine.
|
22
|
+
You can also automatically skip this message by setting JETS_AGREE=yes or JETS_AGREE=no
|
23
|
+
|
24
|
+
Is it okay to send your gem data to Serverless Gems? (Y/n)?
|
25
|
+
EOL
|
26
|
+
|
27
|
+
answer = $stdin.gets.strip
|
28
|
+
value = answer =~ /y/i ? 'yes' : 'no'
|
29
|
+
|
30
|
+
write_file(value)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Allow user to bypass prompt with JETS_AGREE=1 JETS_AGREE=yes etc
|
34
|
+
# Useful for CI/CD pipelines.
|
35
|
+
def bypass_prompt
|
36
|
+
agree = ENV['JETS_AGREE']
|
37
|
+
return false unless agree
|
38
|
+
|
39
|
+
if %w[1 yes true].include?(agree.downcase)
|
40
|
+
write_file('yes')
|
41
|
+
else
|
42
|
+
write_file('no')
|
43
|
+
end
|
44
|
+
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def yes?
|
49
|
+
File.exist?(@agree_file) && IO.read(@agree_file).strip == 'yes'
|
50
|
+
end
|
51
|
+
|
52
|
+
def no?
|
53
|
+
File.exist?(@agree_file) && IO.read(@agree_file).strip == 'no'
|
54
|
+
end
|
55
|
+
|
56
|
+
def yes!
|
57
|
+
write_file("yes")
|
58
|
+
end
|
59
|
+
|
60
|
+
def no!
|
61
|
+
write_file("no")
|
62
|
+
end
|
63
|
+
|
64
|
+
def write_file(content)
|
65
|
+
FileUtils.mkdir_p(File.dirname(@agree_file))
|
66
|
+
IO.write(@agree_file, content)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Jets::Gems
|
2
|
+
class Api
|
3
|
+
include Core
|
4
|
+
|
5
|
+
def endpoint
|
6
|
+
ENV['SG_API'] || Jets.config.gems.source || 'https://api.serverlessgems.com/api/v1'
|
7
|
+
end
|
8
|
+
|
9
|
+
def download_url(gem_name:, project:)
|
10
|
+
get("gem/download?gem_name=#{gem_name}&ruby_folder=#{ruby_folder}&project=#{project}")
|
11
|
+
end
|
12
|
+
|
13
|
+
def check_exist(gem_name:)
|
14
|
+
get("gem/exist?gem_name=#{gem_name}&ruby_folder=#{ruby_folder}")
|
15
|
+
end
|
16
|
+
|
17
|
+
def report_missing(gem_name:)
|
18
|
+
get("report/missing?gem_name=#{gem_name}&ruby_folder=#{ruby_folder}")
|
19
|
+
end
|
20
|
+
|
21
|
+
def ruby_folder
|
22
|
+
Jets::Gems.ruby_folder
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class Jets::Gems::Api
|
2
|
+
module Core
|
3
|
+
extend Memoist
|
4
|
+
|
5
|
+
# Always translate raw json response to ruby Hash
|
6
|
+
def request(klass, path, data={})
|
7
|
+
url = url(path)
|
8
|
+
req = build_request(klass, url, data)
|
9
|
+
resp = http.request(req) # send request
|
10
|
+
load_json(url, resp)
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_request(klass, url, data={})
|
14
|
+
req = klass.new(url) # url includes query string and uri.path does not, must used url
|
15
|
+
set_headers!(req)
|
16
|
+
if [Net::HTTP::Delete, Net::HTTP::Patch, Net::HTTP::Post, Net::HTTP::Put].include?(klass)
|
17
|
+
text = JSON.dump(data)
|
18
|
+
req.body = text
|
19
|
+
req.content_length = text.bytesize
|
20
|
+
end
|
21
|
+
req
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_headers!(req)
|
25
|
+
req['Authorization'] = token if token
|
26
|
+
req['Content-Type'] = "application/vnd.api+json"
|
27
|
+
end
|
28
|
+
|
29
|
+
def token
|
30
|
+
Jets::Gems::Config.instance.data['key']
|
31
|
+
end
|
32
|
+
|
33
|
+
def load_json(url, res)
|
34
|
+
uri = URI(url)
|
35
|
+
if ok?(res.code)
|
36
|
+
JSON.load(res.body)
|
37
|
+
else
|
38
|
+
puts "Error: Non-successful http response status code: #{res.code}"
|
39
|
+
puts "headers: #{res.each_header.to_h.inspect}"
|
40
|
+
puts "ServerlessGems API #{url}" if ENV['SG_DEBUG']
|
41
|
+
raise "ServerlessGems API called failed: #{uri.host}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Note: 422 is Unprocessable Entity. This means an invalid data payload was sent.
|
46
|
+
# We want that to error and raise
|
47
|
+
def ok?(http_code)
|
48
|
+
http_code =~ /^20/ || http_code =~ /^40/
|
49
|
+
end
|
50
|
+
|
51
|
+
def http
|
52
|
+
uri = URI(endpoint)
|
53
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
54
|
+
http.open_timeout = http.read_timeout = 30
|
55
|
+
http.use_ssl = true if uri.scheme == 'https'
|
56
|
+
http
|
57
|
+
end
|
58
|
+
memoize :http
|
59
|
+
|
60
|
+
# API does not include the /. IE: https://app.terraform.io/api/v2
|
61
|
+
def url(path)
|
62
|
+
"#{endpoint}/#{path}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def get(path)
|
66
|
+
request(Net::HTTP::Get, path)
|
67
|
+
end
|
68
|
+
|
69
|
+
def post(path, data={})
|
70
|
+
request(Net::HTTP::Post, path, data)
|
71
|
+
end
|
72
|
+
|
73
|
+
def patch(path, data={})
|
74
|
+
request(Net::HTTP::Patch, path, data)
|
75
|
+
end
|
76
|
+
|
77
|
+
def delete(path, data={})
|
78
|
+
request(Net::HTTP::Delete, path, data)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "zeitwerk"
|
2
|
+
|
3
|
+
module Jets::Gems
|
4
|
+
class Autoloader
|
5
|
+
class Inflector < Zeitwerk::Inflector
|
6
|
+
def camelize(basename, _abspath)
|
7
|
+
map = { cli: "CLI", version: "VERSION" }
|
8
|
+
map[basename.to_sym] || super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def setup
|
14
|
+
loader = Zeitwerk::Loader.new
|
15
|
+
loader.inflector = Inflector.new
|
16
|
+
lib = File.expand_path('../..', __dir__)
|
17
|
+
loader.push_dir(lib) # lib
|
18
|
+
loader.ignore("#{lib}/serverlessgems.rb")
|
19
|
+
loader.setup
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# Assumes gems were just built and checks the filesystem to find and detect for
|
2
|
+
# compiled gems. Unless the cli option is set to true, the it'll just check
|
3
|
+
# based on the gemspecs.
|
4
|
+
module Jets::Gems
|
5
|
+
class Check
|
6
|
+
extend Memoist
|
7
|
+
|
8
|
+
attr_reader :missing_gems
|
9
|
+
def initialize(options={})
|
10
|
+
@options = options
|
11
|
+
@missing_gems = [] # keeps track of gems that are not found in any of the serverlessgems source
|
12
|
+
end
|
13
|
+
|
14
|
+
def run!
|
15
|
+
puts "Gems source: #{gems_source}" if @options[:show_source]
|
16
|
+
run(exit_early: true)
|
17
|
+
end
|
18
|
+
|
19
|
+
def gems_source
|
20
|
+
ENV['SG_API'] || Jets.config.gems.source
|
21
|
+
end
|
22
|
+
|
23
|
+
# Checks whether the gem is found at the serverlessgems source.
|
24
|
+
def run(exit_early: false)
|
25
|
+
puts "Checking projects gems for binary Lambda gems..."
|
26
|
+
compiled_gems.each do |gem_name|
|
27
|
+
puts "Checking #{gem_name}..." if @options[:cli]
|
28
|
+
exist = Jets::Gems::Exist.new
|
29
|
+
@missing_gems << gem_name unless exist.check(gem_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
if exit_early && !@missing_gems.empty?
|
33
|
+
# Exits early if not all the linux gems are available.
|
34
|
+
# Better to error now than deploy a broken package to AWS Lambda.
|
35
|
+
# Provide users with message about using their own serverlessgems source.
|
36
|
+
puts missing_message
|
37
|
+
Report.new(@options).report(@missing_gems) if agree.yes?
|
38
|
+
exit 1
|
39
|
+
end
|
40
|
+
|
41
|
+
compiled_gems
|
42
|
+
end
|
43
|
+
|
44
|
+
def missing?
|
45
|
+
!@missing_gems.empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
def missing_message
|
49
|
+
template = <<-EOL
|
50
|
+
Your project requires compiled gems that are not currently available. Unavailable pre-compiled gems:
|
51
|
+
<% missing_gems.each do |gem| %>
|
52
|
+
* <%= gem -%>
|
53
|
+
<% end %>
|
54
|
+
|
55
|
+
Your current serverlessgems source: #{gems_source}
|
56
|
+
|
57
|
+
Jets is unable to build a deployment package that will work on AWS Lambda without the required pre-compiled gems. To remedy this, you can:
|
58
|
+
|
59
|
+
* Use another gem that does not require compilation.
|
60
|
+
* Create your own custom layer with the gem: http://rubyonjets.com/docs/extras/custom-lambda-layers/
|
61
|
+
<% if agree.yes? -%>
|
62
|
+
* No need to report this to us, as we've already been notified.
|
63
|
+
<% elsif agree.no? -%>
|
64
|
+
* You have choosen not to report data to serverlessgems so we will not be notified about these missing gems.
|
65
|
+
* You can edit ~/.jets/agree to change this.
|
66
|
+
* Reporting gems generally allows Serverless Gems to build the missing gems within a few minutes.
|
67
|
+
* You can try redeploying again after a few minutes.
|
68
|
+
* Non-reported gems may take days or even longer to be built.
|
69
|
+
<% end -%>
|
70
|
+
|
71
|
+
Compiled gems usually take some time to figure out how to build as they each depend on different libraries and packages.
|
72
|
+
More info: http://rubyonjets.com/docs/serverlessgems/
|
73
|
+
|
74
|
+
EOL
|
75
|
+
erb = ERB.new(template, nil, '-') # trim mode https://stackoverflow.com/questions/4632879/erb-template-removing-the-trailing-line
|
76
|
+
erb.result(binding)
|
77
|
+
end
|
78
|
+
|
79
|
+
def agree
|
80
|
+
Agree.new
|
81
|
+
end
|
82
|
+
memoize :agree
|
83
|
+
|
84
|
+
# Context, observations, and history:
|
85
|
+
#
|
86
|
+
# Two ways to check if gem is compiled.
|
87
|
+
#
|
88
|
+
# 1. compiled_gem_paths - look for .so and .bundle extension files in the folder itself.
|
89
|
+
# 2. gemspec - uses the gemspec metadata.
|
90
|
+
#
|
91
|
+
# Observations:
|
92
|
+
#
|
93
|
+
# * The gemspec approach generally finds more compiled gems than the compiled_gem_paths approach.
|
94
|
+
# * So when using the compiled_gem_paths some compiled are missed and not properly detected like http-parser.
|
95
|
+
# * However, some gemspec found compiled gems like json are weird and they don't work when they get replaced.
|
96
|
+
#
|
97
|
+
# History:
|
98
|
+
#
|
99
|
+
# * Started with compiled_gem_paths approach
|
100
|
+
# * Tried to gemspec approach, but ran into json-2.1.0 gem issues. bundler removes? http://bit.ly/39T8uln
|
101
|
+
# * Went to selective checking approach with `cli: true` option. This helped gather more data.
|
102
|
+
# * jets deploy - compiled_gem_paths
|
103
|
+
# * jets gems:check - gemspec_compiled_gems
|
104
|
+
# * Going back to compiled_gem_paths with:
|
105
|
+
# * Using the `weird_gem?` check to filter out gems removed by bundler. Note: Only happens with specific versions of json.
|
106
|
+
# * Keeping compiled_gem_paths for Jets Afterburner mode. Default to gemspec_compiled_gems otherwise
|
107
|
+
#
|
108
|
+
#
|
109
|
+
def compiled_gems
|
110
|
+
# @use_gemspec option finds compile gems with Gem::Specification
|
111
|
+
# The normal build process does not use this and checks the file system.
|
112
|
+
# So @use_gemspec is only used for this command:
|
113
|
+
#
|
114
|
+
# jets gems:check
|
115
|
+
#
|
116
|
+
# This is because it seems like some gems like json are remove and screws things up.
|
117
|
+
# We'll filter out for the json gem as a hacky workaround, unsure if there are more
|
118
|
+
# gems though that exhibit this behavior.
|
119
|
+
if @options[:use_gemspec] == false
|
120
|
+
# Afterburner mode
|
121
|
+
compiled_gems = compiled_gem_paths.map { |p| gem_name_from_path(p) }.uniq
|
122
|
+
# Double check that the gems are also in the gemspec list since that
|
123
|
+
# one is scoped to Bundler and will only included gems used in the project.
|
124
|
+
# This handles the possiblity of stale gems leftover from previous builds
|
125
|
+
# in the cache.
|
126
|
+
# TODO: figure out if we need
|
127
|
+
# compiled_gems.select { |g| gemspec_compiled_gems.include?(g) }
|
128
|
+
else
|
129
|
+
# default when use_gemspec not set
|
130
|
+
#
|
131
|
+
# jets deploy
|
132
|
+
# jets gems:check
|
133
|
+
#
|
134
|
+
gemspec_compiled_gems
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Use pre-compiled gem because the gem could have development header shared
|
139
|
+
# object file dependencies. The shared dependencies are packaged up as part
|
140
|
+
# of the pre-compiled gem so it is available in the Lambda execution environment.
|
141
|
+
#
|
142
|
+
# Example paths:
|
143
|
+
# Macosx:
|
144
|
+
# opt/ruby/gems/2.5.0/extensions/x86_64-darwin-16/2.5.0-static/nokogiri-1.8.1
|
145
|
+
# opt/ruby/gems/2.5.0/extensions/x86_64-darwin-16/2.5.0-static/byebug-9.1.0
|
146
|
+
# Official AWS Lambda Linux AMI:
|
147
|
+
# opt/ruby/gems/2.5.0/extensions/x86_64-linux/2.5.0-static/nokogiri-1.8.1
|
148
|
+
# Circleci Ubuntu based Linux:
|
149
|
+
# opt/ruby/gems/2.5.0/extensions/x86_64-linux/2.5.0/pg-0.21.0
|
150
|
+
def compiled_gem_paths
|
151
|
+
Dir.glob("#{Jets.build_root}/stage/opt/ruby/gems/*/extensions/**/**/*.{so,bundle}")
|
152
|
+
end
|
153
|
+
|
154
|
+
# Input: opt/ruby/gems/2.5.0/extensions/x86_64-darwin-16/2.5.0-static/byebug-9.1.0
|
155
|
+
# Output: byebug-9.1.0
|
156
|
+
def gem_name_from_path(path)
|
157
|
+
regexp = %r{opt/ruby/gems/\d+\.\d+\.\d+/extensions/.*?/.*?/(.*?)/}
|
158
|
+
path.match(regexp)[1] # gem_name
|
159
|
+
end
|
160
|
+
|
161
|
+
# So can also check for compiled gems with Gem::Specification
|
162
|
+
# But then also includes the json gem, which then bundler removes?
|
163
|
+
# We'll figure out the the json gems.
|
164
|
+
# https://gist.github.com/tongueroo/16f4aa5ac5393424103347b0e529495e
|
165
|
+
#
|
166
|
+
# This is a faster way to check but am unsure if there are more gems than just
|
167
|
+
# json that exhibit this behavior. So only using this technique for this commmand:
|
168
|
+
#
|
169
|
+
# jets gems:check
|
170
|
+
#
|
171
|
+
# Thanks: https://gist.github.com/aelesbao/1414b169a79162b1d795 and
|
172
|
+
# https://stackoverflow.com/questions/5165950/how-do-i-get-a-list-of-gems-that-are-installed-that-have-native-extensions
|
173
|
+
def gemspec_compiled_gems
|
174
|
+
specs = Gem::Specification.each.select { |spec| spec.extensions.any? }
|
175
|
+
specs.reject! { |spec| weird_gem?(spec.name) }
|
176
|
+
specs.map(&:full_name)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Filter out the weird special case gems that bundler deletes?
|
180
|
+
# Probably to fix some bug.
|
181
|
+
#
|
182
|
+
# $ bundle show json
|
183
|
+
# The gem json has been deleted. It was installed at:
|
184
|
+
# /home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/json-2.1.0
|
185
|
+
#
|
186
|
+
def weird_gem?(name)
|
187
|
+
command = "bundle show #{name} 2>&1"
|
188
|
+
output = `#{command}`
|
189
|
+
output.include?("has been deleted")
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Jets::Gems::Config
|
2
|
+
class Token
|
3
|
+
def key
|
4
|
+
data['key']
|
5
|
+
end
|
6
|
+
|
7
|
+
def data
|
8
|
+
load_yaml("#{ENV['HOME']}/.jets/config.yml")
|
9
|
+
end
|
10
|
+
|
11
|
+
# Ensure a Hash is returned
|
12
|
+
def load_yaml(path)
|
13
|
+
return {} unless File.exist?(path)
|
14
|
+
|
15
|
+
data = YAML.load_file(path)
|
16
|
+
if data.is_a?(Hash)
|
17
|
+
data
|
18
|
+
else
|
19
|
+
puts "WARN: #{path} is not in the correct format. Loading an empty hash.".color(:yellow)
|
20
|
+
{}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module Jets::Gems
|
4
|
+
class Exist
|
5
|
+
include Jets::Gems::Api::Concern
|
6
|
+
|
7
|
+
# We check all the availability before even downloading so we can provide a
|
8
|
+
# full list of gems they might want to research all at once instead of incrementally
|
9
|
+
#
|
10
|
+
# Examples:
|
11
|
+
#
|
12
|
+
# check(single_gem)
|
13
|
+
# check(list, of, gems)
|
14
|
+
def check(*gem_names)
|
15
|
+
gem_names = gem_names.flatten
|
16
|
+
exists = gem_names.inject({}) do |hash, gem_name|
|
17
|
+
exist = gem_exist?(gem_name)
|
18
|
+
hash[gem_name] = exist
|
19
|
+
hash.merge(hash)
|
20
|
+
end
|
21
|
+
|
22
|
+
exists.values.all? {|v| v } # all_exist
|
23
|
+
end
|
24
|
+
|
25
|
+
def gem_exist?(gem_name)
|
26
|
+
data = api.check_exist(gem_name: gem_name)
|
27
|
+
data["exist"]
|
28
|
+
rescue SocketError, OpenURI::HTTPError, OpenSSL::SSL::SSLError
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "open-uri"
|
2
|
+
|
3
|
+
module Jets::Gems::Extract
|
4
|
+
class Base
|
5
|
+
class NotFound < RuntimeError; end
|
6
|
+
|
7
|
+
attr_reader :project_root
|
8
|
+
def initialize(name, options={})
|
9
|
+
@name = name
|
10
|
+
@options = options
|
11
|
+
|
12
|
+
@downloads_root = options[:downloads_root] || "/tmp/jets/#{Jets.config.project_name}/serverlessgems"
|
13
|
+
end
|
14
|
+
|
15
|
+
def clean_downloads(folder)
|
16
|
+
path = "#{@downloads_root}/downloads/#{folder}"
|
17
|
+
say "Removing cache: #{path}"
|
18
|
+
FileUtils.rm_rf(path)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Using ` > /dev/null 2>&1` to suppress stderr message:
|
22
|
+
#
|
23
|
+
# lchmod (file attributes) error: Function not implemented
|
24
|
+
#
|
25
|
+
def unzip(zipfile_path, parent_folder_dest)
|
26
|
+
sh("cd #{parent_folder_dest} && unzip -qo #{zipfile_path} > /dev/null 2>&1")
|
27
|
+
end
|
28
|
+
|
29
|
+
def sh(command)
|
30
|
+
say "=> #{command}".color(:green)
|
31
|
+
success = system(command)
|
32
|
+
abort("Command Failed #{command}") unless success
|
33
|
+
success
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the dest path
|
37
|
+
def download_file(url, dest)
|
38
|
+
if File.exist?(dest)
|
39
|
+
say "File already downloaded #{dest}"
|
40
|
+
return dest
|
41
|
+
end
|
42
|
+
|
43
|
+
say "Downloading..."
|
44
|
+
FileUtils.mkdir_p(File.dirname(dest)) # ensure parent folder exists
|
45
|
+
|
46
|
+
File.open(dest, 'wb') do |saved_file|
|
47
|
+
URI.open(url, 'rb') do |read_file|
|
48
|
+
saved_file.write(read_file.read)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
dest
|
52
|
+
end
|
53
|
+
|
54
|
+
@@log_level = :info # default level is :info
|
55
|
+
# @@log_level = :debug # uncomment to debug
|
56
|
+
def log_level=(val)
|
57
|
+
@@log_level = val
|
58
|
+
end
|
59
|
+
|
60
|
+
def say(message, level=:info)
|
61
|
+
enabled = @@log_level == :debug || level == :debug
|
62
|
+
puts(message) if enabled
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require "gems"
|
2
|
+
|
3
|
+
module Jets::Gems::Extract
|
4
|
+
class Gem < Base
|
5
|
+
VERSION_PATTERN = /-(\d+\.\d+.*)/
|
6
|
+
include Jets::Gems::Api::Concern
|
7
|
+
|
8
|
+
def run
|
9
|
+
say "Will download and extract gem: #{full_gem_name}"
|
10
|
+
clean_downloads(:gems) if @options[:clean]
|
11
|
+
zipfile_path = download_gem
|
12
|
+
remove_current_gem if Jets.config.gems.clean
|
13
|
+
unzip_file(zipfile_path)
|
14
|
+
say("Gem #{full_gem_name} unpacked at #{project_root}")
|
15
|
+
end
|
16
|
+
|
17
|
+
def unzip_file(zipfile_path)
|
18
|
+
dest = "#{Jets.build_root}/stage/opt"
|
19
|
+
say "Unpacking into #{dest}"
|
20
|
+
FileUtils.mkdir_p(dest)
|
21
|
+
unzip(zipfile_path, dest)
|
22
|
+
end
|
23
|
+
|
24
|
+
# ensure that we always have the full gem name
|
25
|
+
def full_gem_name
|
26
|
+
return @full_gem_name if @full_gem_name
|
27
|
+
|
28
|
+
if @name.match(VERSION_PATTERN)
|
29
|
+
@full_gem_name = @name
|
30
|
+
return @full_gem_name
|
31
|
+
end
|
32
|
+
|
33
|
+
# name doesnt have a version yet, so grab the latest version and add it
|
34
|
+
version = Gems.versions(@name).first
|
35
|
+
@full_gem_name = "#{@name}-#{version["number"]}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def gem_name
|
39
|
+
full_gem_name.gsub(VERSION_PATTERN,'') # folder: byebug
|
40
|
+
end
|
41
|
+
|
42
|
+
# Downloads and extracts the linux gem into the proper directory.
|
43
|
+
# Extracts to: . (current directory)
|
44
|
+
#
|
45
|
+
# It produces a `bundled` folder.
|
46
|
+
# The folder contains the re-produced directory structure. Example with
|
47
|
+
# the gem: byebug-9.1.0
|
48
|
+
#
|
49
|
+
# vendor/gems/ruby/2.5.0/extensions/x86_64-darwin-16/2.5.0-static/byebug-9.1.0
|
50
|
+
#
|
51
|
+
def download_gem
|
52
|
+
# download - also move to /tmp/jets/demo/compiled_gems folder
|
53
|
+
url = gem_url
|
54
|
+
basename = File.basename(url).gsub(/\?.*/,'') # remove query string info
|
55
|
+
tarball_dest = download_file(url, download_path(basename))
|
56
|
+
unless tarball_dest
|
57
|
+
message = "Url: #{url} not found"
|
58
|
+
if @options[:exit_on_error]
|
59
|
+
say message
|
60
|
+
exit
|
61
|
+
else
|
62
|
+
raise NotFound.new(message)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
say "Downloaded to: #{tarball_dest}"
|
66
|
+
tarball_dest
|
67
|
+
end
|
68
|
+
|
69
|
+
# full_gem_name: byebug-9.1.0
|
70
|
+
def gem_url
|
71
|
+
data = api.download_url(gem_name: full_gem_name, project: Jets.config.project_name)
|
72
|
+
if data["download_url"]
|
73
|
+
data["download_url"]
|
74
|
+
else
|
75
|
+
puts data["message"].color(:red)
|
76
|
+
exit 1
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def download_path(filename)
|
81
|
+
"#{@downloads_root}/downloads/gems/#{filename}"
|
82
|
+
end
|
83
|
+
|
84
|
+
# Finds any currently install gems that matched with the gem name and version
|
85
|
+
# and remove them first.
|
86
|
+
# We clean up the current install gems first in case it was previously installed
|
87
|
+
# and has different *.so files that can be accidentally required. This
|
88
|
+
# happened with the pg gem.
|
89
|
+
def remove_current_gem
|
90
|
+
say "Removing current #{full_gem_name} gem installation:"
|
91
|
+
gem_dirs = Dir.glob("#{project_root}/**/*").select do |path|
|
92
|
+
File.directory?(path) &&
|
93
|
+
path =~ %r{vendor/gems} &&
|
94
|
+
File.basename(path) == full_gem_name
|
95
|
+
end
|
96
|
+
gem_dirs.each do |dir|
|
97
|
+
say " rm -rf #{dir}"
|
98
|
+
FileUtils.rm_rf(dir)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "net/http"
|
2
|
+
|
3
|
+
module Jets::Gems
|
4
|
+
class Report
|
5
|
+
include Jets::Gems::Api::Concern
|
6
|
+
|
7
|
+
def initialize(options={})
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def report(gems)
|
12
|
+
threads = []
|
13
|
+
gems.each do |gem_name|
|
14
|
+
threads << Thread.new do
|
15
|
+
api.report_missing(gem_name: gem_name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
# Wait for request to finish because the command might finish before
|
19
|
+
# the Threads even send the request. So we join them just case
|
20
|
+
threads.each(&:join)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "jets/gems"
|
@@ -0,0 +1,31 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "jets/gems/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "serverlessgems"
|
7
|
+
spec.version = Jets::Gems::VERSION
|
8
|
+
spec.authors = ["Tung Nguyen"]
|
9
|
+
spec.email = ["tongueroo@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Client Library works with Serverless Gems API for Jets Ruby Serverless Framework"
|
12
|
+
spec.homepage = "https://github.com/boltops-tools/serverlessgems"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
# Specify which files should be added to the gem when it is released.
|
16
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
17
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
18
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "gems"
|
25
|
+
spec.add_dependency "memoist"
|
26
|
+
spec.add_dependency "zeitwerk"
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler"
|
29
|
+
spec.add_development_dependency "rake"
|
30
|
+
spec.add_development_dependency "rspec"
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: serverlessgems
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tung Nguyen
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-01-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: gems
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: memoist
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: zeitwerk
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
- tongueroo@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- CHANGELOG.md
|
107
|
+
- Gemfile
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- bin/console
|
112
|
+
- bin/setup
|
113
|
+
- lib/jets/gems.rb
|
114
|
+
- lib/jets/gems/agree.rb
|
115
|
+
- lib/jets/gems/api.rb
|
116
|
+
- lib/jets/gems/api/concern.rb
|
117
|
+
- lib/jets/gems/api/core.rb
|
118
|
+
- lib/jets/gems/autoloader.rb
|
119
|
+
- lib/jets/gems/check.rb
|
120
|
+
- lib/jets/gems/config.rb
|
121
|
+
- lib/jets/gems/config/token.rb
|
122
|
+
- lib/jets/gems/exist.rb
|
123
|
+
- lib/jets/gems/extract/base.rb
|
124
|
+
- lib/jets/gems/extract/gem.rb
|
125
|
+
- lib/jets/gems/report.rb
|
126
|
+
- lib/jets/gems/version.rb
|
127
|
+
- lib/serverlessgems.rb
|
128
|
+
- serverlessgems.gemspec
|
129
|
+
homepage: https://github.com/boltops-tools/serverlessgems
|
130
|
+
licenses:
|
131
|
+
- MIT
|
132
|
+
metadata: {}
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
require_paths:
|
136
|
+
- lib
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
requirements: []
|
148
|
+
rubygems_version: 3.1.4
|
149
|
+
signing_key:
|
150
|
+
specification_version: 4
|
151
|
+
summary: Client Library works with Serverless Gems API for Jets Ruby Serverless Framework
|
152
|
+
test_files: []
|