forked from joshuaflanagan/serverless-ruby-package
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
154 lines (130 loc) · 5.33 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
'use strict';
const path = require("path");
const { execSync } = require('child_process');
const fs = require('fs');
class PackageRubyBundlePlugin {
get config() {
const config = Object.assign(
{
alwaysCrossCompileExtensions: true,
debug: !!process.env.SRP_DEBUG,
},
(
this.serverless.service.custom &&
this.serverless.service.custom.rubyPackage
) || {}
);
// give precedence to environment variable, if set
if (typeof(process.env.CROSS_COMPILE_EXTENSIONS) !== 'undefined'){
const override = (/^(?:y|yes|true|1|on)$/i.test(process.env.CROSS_COMPILE_EXTENSIONS));
config.alwaysCrossCompileExtensions = override;
}
return config;
}
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.hooks = {
'before:package:createDeploymentArtifacts': this.beforePackage.bind(this),
'before:package:function:package': this.beforePackage.bind(this),
};
}
log(message){
this.serverless.cli.log(message, "ruby-package");
}
rubyVersion() {
// RbConfig::CONFIG['ruby_version']
const runtime_version = this.serverless.service.provider.runtime
const match = runtime_version.match(/ruby(\d+\.\d+)/);
if (match) {
return `${match[1]}.0`;
}
// If not set, default to 3.3.0
return '3.3.0';
}
beforePackage(){
this.warnOnUnsupportedRuntime();
const gemRoot = `vendor/bundle/ruby/${this.rubyVersion()}`;
const extensionDir = `${gemRoot}/extensions/x86_64-linux/${this.rubyVersion()}`;
const excludeGemTests = true; //TODO: make configurable
const identifyGemsScript = `
require 'json'
root = ENV['GEM_HOME']
gems = Bundler.definition.specs_for([:default]).reject{|s| s.name=='bundler'}
details = gems.map{|gem|
{
extensions: !!gem.extensions.any?,
name: gem.full_name,
path: gem.full_gem_path.split(root).last,
}
}
puts JSON.generate(details.sort_by{|x| x[:name]})
`
this.serverless.service.package.excludeDevDependencies = false; // only relevant to nodejs
this.serverless.service.package.exclude = ["**"]; // force whitelist
this.serverless.service.package.include.push("vendor/bundle/bundler/**"); // bundler standalone files
const gemFilePath = path.join(this.serverless.config.servicePath, "Gemfile");
const bundleEnv = Object.assign({
"BUNDLE_GEMFILE": gemFilePath
}, process.env);
const output = execSync("bundle exec ruby", {input: identifyGemsScript, env: bundleEnv});
const gems = JSON.parse(output)
if (gems.some(x=>x.extensions)){
if (this.config.alwaysCrossCompileExtensions){
this.nativeLinuxBundle();
}
}
if (gems.length < 10) {
this.log(`Packaging gems: ${gems.map(x=>x.name).join(" ")}`);
} else {
this.log(`Packaging ${gems.length} gems`);
}
gems.forEach((gem) =>{
this.serverless.service.package.include.push(`${gemRoot}${gem.path}/**`);
if (gem.extensions){
this.serverless.service.package.include.push(`${extensionDir}/${gem.name}/**`);
}
// includes that start with a ! are treated as excludes when evaluating,
// but are ordered along with the includes. If these patterns were
// specified as excludes, they would be evaluated first, and then the
// includes on the gem paths would bring them back.
this.serverless.service.package.include.push(`!${gemRoot}${gem.path}/.git/**`);
if (excludeGemTests) {
this.serverless.service.package.include.push(`!${gemRoot}${gem.path}/test/**`);
this.serverless.service.package.include.push(`!${gemRoot}${gem.path}/spec/**`);
}
});
}
nativeLinuxBundle(){
this.log(`Building gems with native extensions for linux`);
const localPath = this.serverless.config.servicePath;
const imageTag = this.serverless.service.provider.runtime.slice(-3);
const dockerImage = `amazon/aws-lambda-ruby:${imageTag}`;
const command = `docker run --rm \
--volume "${localPath}:/var/task" \
--entrypoint '/bin/bash' \
${dockerImage} \
'/var/task/node_modules/serverless-ruby-package/build-gems.sh'`
if (this.config.debug){
this.log(`docker image: ${dockerImage}`);
this.log(`command: ${command}`);
}
execSync(command)
}
warnOnUnsupportedRuntime(){
const runtimes = ['ruby2.7', 'ruby3.2', 'ruby3.3'];
if (this.config.debug){
this.log(`platform: ${process.platform}`);
this.log(`provider: ${this.serverless.service.provider.name}`);
this.log(`runtime: ${this.serverless.service.provider.runtime}`);
}
if (this.serverless.service.provider.name != 'aws'){
this.log(`WARNING: serverless-ruby-package has only been tested with the AWS provider. It may not work with ${this.serverless.service.provider.name}, but bug reports are welcome.`);
return;
}
if (!runtimes.includes(this.serverless.service.provider.runtime)){
this.log(`WARNING: serverless-ruby-package has only been tested with the following ruby runtimes: ${runtimes.join(', ')}. It may not work with ${this.serverless.service.provider.runtime}, but bug reports are welcome.`);
}
}
}
module.exports = PackageRubyBundlePlugin;