import { join } from 'path'; // import { join, dirname } from 'path'; import execa from 'execa'; import fs from 'fs'; import { ensureDir } from 'fs-extra'; import { promisify } from 'util'; const readFile = promisify(fs.readFile); const writeFile = promisify(fs.writeFile); import { getWriteableDirectory, download, glob, createLambda, BuildOptions, } from '@now/build-utils'; import { downloadAndInstallBundler } from './gem-install'; async function gemInstall(gemPath: string, workDir: string, vendorDir: string, ...gems: string[]) { console.log(`running "gem install ${gems.join(' ')}"...`); await execa(gemPath, ['help', 'install'], { stdio: 'inherit' }) try { await execa(gemPath, ['install', ...gems, '-​-install-dir', vendorDir, '-​-no-document'], { cwd: workDir, stdio: 'inherit', }); } catch (err) { console.log(`failed to run "gem install ${gems.join(' ')}"...`); throw err; } } // async function bundleInstall(bundlePath: string, workDir: string, ...args: string[]) { // console.log(`running "bundle ${args.join(' ')}"...`); // try { // await execa(bundlePath, args, { // cwd: workDir, // stdio: 'inherit', // env: { // 'BUNDLE_SILENCE_ROOT_WARNING': '1', // } // }); // } catch (err) { // console.log(`failed to run "bundle ${args.join(' ')}"...`); // throw err; // } // } export const config = { maxLambdaSize: '5mb', }; export const build = async ({ workPath, files, entrypoint }: BuildOptions) => { console.log('downloading files...'); // eslint-disable-next-line no-param-reassign files = await download(files, workPath); // this is where `ruby, gem and bundler` will be installed to // we need it to be under `/tmp` const gemHome = await getWriteableDirectory(); process.env.GEM_HOME = gemHome; const { gemPath } = await downloadAndInstallBundler(); // const { gemPath, bundlerPath } = await downloadAndInstallBundler(); // reuse existing vendor directory if available const vendorFiles = await glob('vendor/bundle/ruby/*', workPath) let vendorDir: string if (Object.keys(vendorFiles).length) { vendorDir = vendorFiles[Object.keys(vendorFiles)[0]].fsPath } else { vendorDir = join(workPath, 'vendor', 'bundle', 'ruby', '2.5.3') } await ensureDir(vendorDir) console.log('Using vendor dir', vendorDir); // install single requirement for http await gemInstall(gemPath, workPath, vendorDir, 'httparty'); // const fsFiles = await glob('**', workPath); // const entryDirectory = dirname(entrypoint); // const gemFile = join(entryDirectory, 'Gemfile'); // if (fsFiles[gemFile]) { // console.log('found "Gemfile"'); // const gemFilePath = fsFiles[gemFile].fsPath; // await bundleInstall(bundlerPath, workPath, 'package', '--no-install', '--gemfile', gemFilePath); // } const originalRbPath = join(__dirname, 'now_init.rb'); const originalNowHandlerRbContents = await readFile(originalRbPath, 'utf8'); // will be used on `from $here import handler` // for example, `from api.users import handler` console.log('entrypoint is', entrypoint); const userHandlerFilePath = entrypoint.replace(/\.rb$/, ''); const nowHandlerRbContents = originalNowHandlerRbContents.replace( /__NOW_HANDLER_FILENAME/g, userHandlerFilePath, ); // in order to allow the user to have `server.rb`, we need our `server.rb` to be called // somethig else const nowHandlerRbFilename = 'now__handler__ruby'; await writeFile( join(workPath, `${nowHandlerRbFilename}.rb`), nowHandlerRbContents, ); const lambda = await createLambda({ files: await glob('**', workPath), handler: `${nowHandlerRbFilename}.handler`, runtime: 'ruby2.5', environment: {}, }); return { [entrypoint]: lambda, }; };