6

我正在尝试制作两个 AWS Lambda 函数(用打字稿编写)。这两个函数共享相同的代码来与 API 交互。为了不必将相同的代码复制到两个不同的 Lambda,我想将我的共享代码移动到本地模块,并让我的两个 Lambda 都依赖于所述模块。

我最初尝试在两个 lambda 之间查看代码是使用 monorepo 和 lerna。我当前的项目结构如下所示:

- lerna.json
- package.json
- packages
  - api
    - package.json
  - lambdas
    - funcA
      - package.json
    - func B
      - package.json

lerna.json:

{
  "packages": [
    "packages/api",
    "packages/lambdas/*"
  ],
  "version": "1.0.0"
}

在我的 Lambda 函数的每个 package.json 中,我都可以包含我的本地 api 模块:

"dependencies": {
    "@local/api": "*"
}

有了这个,我已经能够将公共代码移动到它自己的模块中。但是,我现在不确定如何捆绑我的函数以部署到 AWS Lambda。lerna 有没有办法创建一个可以部署的包?

4

4 回答 4

1

由于cp -rL在 mac 上不起作用,我不得不想出类似的东西。

如果您的所有包都属于一个范围(@org),则这是一个有效的工作流程:

在你的 lerna repo 的 package.json 中:

"scripts": {
    "deploy": "lerna exec \"rm -rf node_modules\" && lerna bootstrap -- --production && lerna run deploy && lerna bootstrap"
}

在包含您的 lambda 函数的包中:

"scripts":{
    "deploy": "npm pack && tar zxvf packagename-version.tgz && rm -rf node_modules/@org && cp -r node_modules/* package/node_modules && cd package && npm dedupe"
}

现在将“packagename-version”和“@org”替换为项目的相应值。还将所有依赖包添加到“bundledDependencies”。

在您的 lerna mono repo 的根目录中运行后npm run deploy,您最终会在包含您的 lambda 函数的包中找到一个文件夹“包”。它具有运行您的功能所需的所有依赖项。从那里拿走它。

我曾希望 usingnpm pack可以让我利用 .npmignore 文件,但似乎这不起作用。如果有人知道如何使它工作,请告诉我。

于 2019-05-28T10:19:31.443 回答
1

我已经为同样的问题苦苦挣扎了一段时间,最后我不得不为此做点什么。

我正在使用一个名为slice-node-modules的小包,如在这个类似的问题中找到的,这对于我的目的来说已经足够了。随着我将更多的项目整合到 monorepos 中并开始使用作为兄弟姐妹而不是外部发布的共享依赖项,我遇到了这种方法的缺点。

我创建了一个名为lerna-to-lambda的新工具,专门针对我的用例量身定制。我以最少的文档公开发布了它,希望足以帮助处于类似情况的其他人。它的要点是,您l2l在安装所有依赖项之后在捆绑步骤中运行,并将所需的内容复制到输出目录中,然后准备好使用 SAM 或其他方式部署到 Lambda。

例如,在 README 中,您的 Lambda 函数中可能包含以下内容package.json

"scripts": {
  ...
  "clean": "rimraf build lambda",
  "compile": "tsc -p tsconfig.build.json",
  "package": "l2l -i build -o lambda",
  "build": "yarn run clean && yarn run compile && yarn run package"
},

在这种情况下,该compile步骤是将源目录中的 TypeScript 文件编译为该build目录中的 JavaScript 文件。然后,该步骤将所有代码与 Lambda 的所有依赖项(除了)package捆绑到目录中,这就是您要部署到 AWS 的内容。如果有人使用纯 JavaScript 而不是 TypeScript,他们可以在打包之前将必要的 .js 文件复制到目录中。buildaws-sdklambdabuild

我意识到这个问题已经超过 2 年了,从那时起你可能已经想出了自己的解决方案和/或解决方法。但由于它仍然与我相关,我认为它仍然与外面的人相关,所以我分享。

于 2021-01-06T03:13:31.383 回答
0

我在安装过程中使用了自定义脚本来复制依赖项。这将允许我使用相同的代码开发和部署应用程序。

项目结构 在 lambda_a 的 package.json 文件中,我有以下行:

  "scripts": {
    "install": "node ./install_libs.js @libs/library_a"
  },

@libs/library_a 可以通过以下语句由 lambda 代码使用:

const library_a = require('@libs/library_a')

对于 SAM 构建,我使用 lmbdas 文件夹中的以下命令:

export SAM_BUILD=true && sam build

install_libs.js

console.log("Starting module installation")
var fs = require('fs');
var path = require('path');
var {exec} = require("child_process");

if (!fs.existsSync("node_modules")) {
    fs.mkdirSync("node_modules");
}
if (!fs.existsSync("node_modules/@libs")) {
    fs.mkdirSync("node_modules/@libs");
}

const sam_build = process.env.SAM_BUILD || false
libs_path = "../../"
if (sam_build) {
    libs_path = "../../" + libs_path
}


process.argv.forEach(async function (val, index, array) {
    if (index > 1) {
        var currentLib = libs_path + val
        console.log(`Building lib ${currentLib}`)
        await exec(`cd ${currentLib} && npm install` , function (error, stdout, stderr){
            if (error) {
                console.log(`error: ${error.message}`);
                return;
            }
            console.log(`stdout: ${stdout}`);
            console.log('Importing module : ' + currentLib);
            copyFolderRecursiveSync(currentLib, "node_modules/@libs")
        });
    }
});


function copyFolderRecursiveSync(source, target) {
    var files = [];
    // Check if folder needs to be created or integrated
    var targetFolder = path.join(target, path.basename(source));
    if (!fs.existsSync(targetFolder)) {
        fs.mkdirSync(targetFolder);
    }

    // Copy
    if (fs.lstatSync(source).isDirectory()) {
        files = fs.readdirSync(source);
        files.forEach(function (file) {
            var curSource = path.join(source, file);
            if (fs.lstatSync(curSource).isDirectory()) {
                copyFolderRecursiveSync(curSource, targetFolder);
            } else {
                copyFileSync(curSource, targetFolder);
            }
        });
    }
}

function copyFileSync(source, target) {
    var targetFile = target;
    // If target is a directory, a new file with the same name will be created
    if (fs.existsSync(target)) {
        if (fs.lstatSync(target).isDirectory()) {
            targetFile = path.join(target, path.basename(source));
        }
    }
    fs.writeFileSync(targetFile, fs.readFileSync(source));
}
于 2021-01-21T10:30:39.747 回答
0

运行lerna bootstrap将在每个“包”中创建一个 node_modules 文件夹。这将包括所有 lerna 管理的依赖项以及该特定包的外部依赖项。

从那时起,您对每个 lambda 的部署将不知道您正在使用 lerna。部署包将需要包含该特定 lambda 的代码和该 lambda 的 node_modules 文件夹 - 您可以将它们压缩并手动上传,或者使用 SAM 或 CloudFormation 之类的东西。

编辑:正如你正确指出的那样,你最终会在你的 node_modules 文件夹中得到符号链接,这使得打包起来很尴尬。为了解决这个问题,你可以在打包部署之前运行这样的东西:

cp -rL lambdas/funcA/node_modules lambdas/funcA/packaged/node_modules

-L将强制将符号链接的目录复制到文件夹中,然后您可以对其进行压缩。

于 2018-12-11T15:48:03.607 回答