Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#19 Added validation for edge cases and included user informative logs & errors #22

Merged
merged 2 commits into from
Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
78 changes: 61 additions & 17 deletions lib/bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ Promise.promisifyAll(fs);

function runCommand(cmd,args,options,cli) {
const ps = spawnSync(cmd, args,options);
cli.log(ps.stdout.toString())
if (ps.error && ps.error.code != 'ENOENT') {
cli.log(ps.stderr.toString())
if (ps.stdout && this.debug){
cli.log(ps.stdout.toString())
}
if (ps.stderr){
cli.log(ps.stderr.toString())
}
if (ps.error && ps.error.code == 'ENOENT'){
return ps;
}
if (ps.error) {
throw new Error(ps.error);
} else if (ps.status !== 0) {
cli.log(ps.stderr.toString())
throw new Error(ps.stderr);
}
return ps;
Expand All @@ -24,29 +30,39 @@ function docker(args, options,cli){

function cleanBuild(){
this.cli = this.serverless.cli
this.cli.log("Cleaning ruby layer build")
this.cli.log("Clearing previous build ruby layer build")
this.ruby_layer = path.join(this.servicePath,'.serverless','ruby_layer')
if (fs.pathExistsSync(this.ruby_layer)){
fs.removeSync(this.ruby_layer)
}
}

function bundleInstall(){
this.cli.log(this.ruby_layer)
const gem_path = path.join(this.ruby_layer,'Gemfile')
fs.copySync(path.join(this.servicePath,'Gemfile'), gem_path )
this.debug = process.env.SLS_DEBUG;
const gem_file_path = path.join(this.servicePath,'Gemfile')
if (!fs.pathExistsSync(gem_file_path)){
throw new Error("No Gemfile found in the path "+gem_file_path+"\n Please add a Gemfile in the path");
}

fs.copySync(path.join(this.servicePath,'Gemfile'), path.join(this.ruby_layer,'Gemfile') )
const bundle_args = ['bundle', 'install', '--path=build','--without', 'test', 'development']
const options={cwd : this.ruby_layer, encoding : 'utf8'}
this.build_path = path.join(this.ruby_layer, 'build')
fs.mkdirSync(this.build_path)
const options = {cwd : this.ruby_layer, encoding : 'utf8'}
if (this.options.use_docker) {
docker_name = 'lambci/lambda:build-'+this.serverless.service.provider.runtime
ps=docker(['version'], options,this.cli)
if (ps.error && ps.error.code === 'ENOENT') {
throw new Error('docker command not found');
throw new Error('docker command not found. Please install docker https://www.docker.com/products/docker-desktop');
}
let buildDocker = false;
if (this.options.docker_file) {
fs.copySync(path.join(this.servicePath, this.options.docker_file),
const docker_file_path = path.join(this.servicePath, this.options.docker_file)
if (!fs.pathExistsSync(docker_file_path)){
throw new Error("No Dockerfile found in the path "+docker_file_path);
}

fs.copySync(docker_file_path,
path.join(this.ruby_layer,'Dockerfile'))
buildDocker = true
}else if (this.options.docker_yums) {
Expand All @@ -60,10 +76,13 @@ function bundleInstall(){
buildDocker = true
}
if (buildDocker) {
this.cli.log("Building docker for bundle install")
docker_name ='ruby-layer:docker'
docker(['build', '-t', docker_name, '-f', 'Dockerfile', '.'], options,this.cli)
}

if (this.options.native_libs) {
this.cli.log("Packing the native libraries from the specified path")
ps=docker(['run', '-d', docker_name, 'false'], options,this.cli)
container_id = ps.stdout.toString().trim()
const lib_path = path.join(this.build_path,'lib')
Expand All @@ -72,15 +91,27 @@ function bundleInstall(){
ps=docker(['cp','-L', container_id+':'+lib_to_copy, lib_path],options,this.cli)
})
}

this.cli.log("Installing gem using docker bundler")
args=['run','--rm', '-i','-v', `${this.ruby_layer}:/var/gem_build`, '-w', '/var/gem_build']
args.push(docker_name)

docker(args.concat(bundle_args), options,this.cli)
} else {
ps = runCommand("bundle",['-v'],options,this.cli)
ps = runCommand("bundle",['-v'], options, this.cli)
if (ps.error && ps.error.code === 'ENOENT') {
throw new Error('bundle command not found in local');
throw new Error('bundle command not found in local. Please install ruby. https://www.ruby-lang.org/en/downloads/');
}
this.cli.log(bundle_args.slice(1,bundle_args.length))
this.cli.log("Installing gem using local bundler")
if (this.debug) {
this.cli.log("Ruby layer Path: \n "+ this.ruby_layer)
ps = runCommand("ruby",['--version'], options, this.cli)
this.cli.log("Ruby version: "+ ps.stdout.toString().trim())
ps = runCommand("bundle",['-v'],options,this.cli)
this.cli.log("Bundler version: "+ ps.stdout.toString().trim())
this.cli.log(bundle_args.join(" "))
}

runCommand(bundle_args[0],bundle_args.slice(1,bundle_args.length),options,this.cli)
}
}
Expand Down Expand Up @@ -122,8 +153,13 @@ function zipBundleFolder() {
this.gem_folder= fs.readdirSync(path.join(this.build_path,'ruby'))[0]
fs.removeSync(path.join(this.build_path,'ruby',this.gem_folder,'cache'))
const platform = process.platform == 'win32' ? 'DOS' : 'UNIX'
return zipDir(this.build_path,
path.join(this.ruby_layer, 'gemLayer.zip'),
zipping_message = "Zipping the gemfiles"
if (this.options.native_libs) {
zipping_message+=" and native libs"
}
this.gemLayer_zip_path = path.join(this.ruby_layer, 'gemLayer.zip')
this.cli.log(zipping_message+ ' to '+ this.gemLayer_zip_path)
return zipDir(this.build_path, this.gemLayer_zip_path,
{ platform: platform, compression: 'DEFLATE',
compressionOptions: { level: 9 }});
}
Expand All @@ -140,12 +176,17 @@ function excludePackage(){
}

function configureLayer() {
this.cli.log("Configuring Layer and GEM_PATH to the functions")
if(this.debug){
this.cli.log("GEM_PATH:" + "/opt/ruby/"+this.gem_folder)
this.cli.log("Zip Path:" + this.gemLayer_zip_path )
}
if (!this.serverless.service.layers) {
this.serverless.service.layers = {};
}
this.serverless.service.layers['gemLayer'] = Object.assign(
{
package: {artifact: path.join(this.ruby_layer, 'gemLayer.zip')},
package: {artifact: this.gemLayer_zip_path },
name: `${
this.serverless.service.service
}-${this.serverless.providers.aws.getStage()}-ruby-bundle`,
Expand All @@ -157,6 +198,9 @@ function configureLayer() {
);

Object.keys(this.serverless.service.functions).forEach(funcName => {
if(this.debug){
this.cli.log("Configuring Layer for function: " + funcName)
}
const function_ = this.serverless.service.getFunction(funcName)
function_.environment={}
function_.environment["GEM_PATH"]="/opt/ruby/"+this.gem_folder
Expand Down
8 changes: 6 additions & 2 deletions test/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ const path = require('path');

function runCommand(cmd,args,options) {
const ps = spawnSync(cmd, args,options);
console.log(ps.stderr.toString())
console.log(ps.stdout.toString());
if (ps.stdout){
console.log(ps.stdout.toString())
}
if (ps.stderr){
console.log(ps.stderr.toString())
}
if (ps.error) {
throw new Error(ps.error);
} else if (ps.status !== 0) {
Expand Down