import { join } from 'path'; // import { join, dirname } from 'path'; import vanillaGlob_ from 'glob'; import execa from 'execa'; import fs from 'fs'; import { ensureDir } from 'fs-extra'; import { promisify } from 'util'; const vanillaGlob = promisify(vanillaGlob_); 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(' ')}"...`); try { await execa(gemPath, ['install', ...gems, '-​-install-dir', vendorDir, '-​-no-document'], { cwd: workDir, shell: true, 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 console.log('looking for vendor directory...') await execa('find', [workPath], { stdio: 'inherit' }) const vendorFiles = await vanillaGlob('vendor/bundle/ruby/*', { cwd: workPath, }) console.log(vendorFiles) let vendorDir: string if (vendorFiles.length) { vendorDir = join(workPath, vendorFiles[0]) console.log('found', vendorFiles[0]) } else { vendorDir = join(workPath, 'vendor', 'bundle', 'ruby', '2.5.3') console.log('not found, creating', join('vendor', 'bundle', 'ruby', '2.5.3')) } await ensureDir(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, }; };