[アップデート]AWS SAM CLIでnodejsをビルドする際に元々のソースディレクトリを参照しビルドができるようになりました
初めに
昨日、AWS SAM CLIのv1.104.0
がリリースされました。
今回のソースコードを格納しているディレクトリを参照しでビルドを行うオプションである--build-in-source
オプションが追加されたようです。
PRに詳しいコメントや関連issuesの記載がなくソースコードの変更や実際の動作を書くにする限りの挙動のため本来意図しているものかは不明ですが、こちらのオプションを利用するとビルドが一部リソースが元々のソースディレクトリ内を参照するようになりこれにより大量の依存ライブラリが含まれる環境におけるビルド時間の短縮等が見込めるようになりそうです。
--build-in-source / --no-build-in-source Opts in to build project in the source folder. The following workflows support building in source: ['nodejs12.x', 'nodejs14.x', 'nodejs16.x', 'nodejs18.x', 'Makefile', 'esbuild']
現在はnodejs周りのみがサポートされており実際の動作を確認する限りnode_modules
フォルダが対象となりました(現時点ではnodejs20.x
は対象外のようです)。
sam buildの仕組みについて
まず前提知識としてsam build
の仕組みの一部を理解する必要があります。
以前別の記事で紹介させていただきましたがSAMでビルドを行う際にはソースコードが格納されているディレクトリで直接ビルドするのではなく、一旦別のディレクトリにコピーしそれを対象としビルドする仕組みとなっております。
この辺りの具体的な処理はランタイムにより異なるのですがnodejsの場合は以下のような処理が行われます。
- 一時フォルダに
npm pack
+npm unpacked
を利用して不要なものもを取り除いた上で展開する - 最終的なsamのビルドの成果物が格納される各プロジェクト内の
.aws-sam/build
内に移す npm install
により各種ライブラリをインストールする
$ sam build --no-cached --debug ... 2023-12-08 01:05:24,821 | NODEJS packaging file:/Users/xxxx/sam-app-node/hello-world to /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmp262yxsqf 2023-12-08 01:05:24,821 | executing NPM: ['npm', 'pack', '-q', 'file:/Users/xxxx/sam-app-node/hello-world'] 2023-12-08 01:05:25,040 | NODEJS packed to hello_world-1.0.0.tgz 2023-12-08 01:05:25,040 | NODEJS extracting to /var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmp262yxsqf/unpacked 2023-12-08 01:05:25,041 | NodejsNpmBuilder:NpmPack succeeded 2023-12-08 01:05:25,042 | Running NodejsNpmBuilder:CopyNpmrcAndLockfile 2023-12-08 01:05:25,042 | NodejsNpmBuilder:CopyNpmrcAndLockfile succeeded 2023-12-08 01:05:25,042 | Running NodejsNpmBuilder:CopySource 2023-12-08 01:05:25,043 | Creating target folders at /Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction 2023-12-08 01:05:25,043 | Copying directory metadata from source (/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmp262yxsqf/unpacked/package) to destination (/Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction) 2023-12-08 01:05:25,044 | Copying source file (/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmp262yxsqf/unpacked/package/package.json) to destination (/Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction/package.json) 2023-12-08 01:05:25,044 | Copying source file (/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmp262yxsqf/unpacked/package/app.mjs) to destination (/Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction/app.mjs) 2023-12-08 01:05:25,045 | NodejsNpmBuilder:CopySource succeeded 2023-12-08 01:05:25,045 | Running NodejsNpmBuilder:NpmInstall 2023-12-08 01:05:25,045 | NODEJS installing in: /Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction 2023-12-08 01:05:25,045 | executing NPM: ['npm', 'install', '-q', '--no-audit', '--no-save', '--unsafe-perm', '--production'] 2023-12-08 01:05:33,828 | NodejsNpmBuilder:NpmInstall succeeded 2023-12-08 01:05:33,829 | Running NodejsNpmBuilder:CleanUpNpmrc
キャッシュを持っているとnpm install
は省略されることがあるようですが一旦別の場所に退避・複製が行われるためその分の時間はかかってしまいます。
2023-12-08 01:56:36,909 | NodejsNpmBuilder:CleanUp succeeded 2023-12-08 01:56:36,909 | Running NodejsNpmBuilder:CopyDependencies 2023-12-08 01:56:36,910 | Creating target folders at .aws-sam/deps/c39c3d19-50b7-449f-b51f-6b1bdf4c1986/node_modules 2023-12-08 01:56:36,910 | Copying directory metadata from source (/Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction/node_modules) to destination (.aws-sam/deps/c39c3d19-50b7-449f-b51f-6b1bdf4c1986/node_modules) 2023-12-08 01:56:36,911 | Creating target folders at .aws-sam/deps/c39c3d19-50b7-449f-b51f-6b1bdf4c1986/node_modules/proxy-from-env 2023-12-08 01:56:36,911 | Copying directory metadata from source (/Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction/node_modules/proxy-from-env) to destination (.aws-sam/deps/c39c3d19-50b7-449f-b51f-6b1bdf4c1986/node_modules/proxy-from-env) 2023-12-08 01:56:36,912 | Copying source file (/Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction/node_modules/proxy-from-env/test.js) to destination (.aws-sam/deps/c39c3d19-50b7-449f-b51f-6b1bdf4c1986/node_modules/proxy-from-env/test.js) 2023-12-08 01:56:36,912 | Copying source file (/Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction/node_modules/proxy-from-env/LICENSE) to destination (.aws-sam/deps/c39c3d19-50b7-449f-b51f-6b1bdf4c1986/node_modules/proxy-from-env/LICENSE) 2023-12-08 01:56:36,913 | Copying source file (/Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction/node_modules/proxy-from-env/.eslintrc) to destination (.aws-sam/deps/c39c3d19-50b7-449f-b51f-6b1bdf4c1986/node_modules/proxy-from-env/.eslintrc)
今回のオプションを指定すると
こちらのオプションを指定した場合前述の2.のステップを実行したのち元々のソースディレクトリ内のnode_modules
にシンボリックリンクを貼りnpm update
が実行されます(installではないらしい)。
% sam build --no-cached --debug --build-in-source ... 2023-12-08 00:34:29,953 | Creating target folders at /Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction 2023-12-08 00:34:29,954 | Copying directory metadata from source (/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpw2owilpv/unpacked/package) to destination (/Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction) 2023-12-08 00:34:29,954 | Copying source file (/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpw2owilpv/unpacked/package/package.json) to destination (/Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction/package.json) 2023-12-08 00:34:29,955 | Copying source file (/var/folders/8m/b717gxmx46l7ytmt2smb507w0000gp/T/tmpw2owilpv/unpacked/package/app.mjs) to destination (/Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction/app.mjs) 2023-12-08 00:34:29,955 | NodejsNpmBuilder:CopySource succeeded 2023-12-08 00:34:29,956 | Running NodejsNpmBuilder:NpmUpdate 2023-12-08 00:34:29,956 | NODEJS updating in: /Users/xxxx/sam-app-node/hello-world 2023-12-08 00:34:29,956 | executing NPM: ['npm', 'update', '--no-audit', '--no-save', '--unsafe-perm', '--production', '--no-package-lock', '--install-links'] 2023-12-08 00:34:36,950 | NodejsNpmBuilder:NpmUpdate succeeded 2023-12-08 00:34:36,951 | Running NodejsNpmBuilder:LinkSource 2023-12-08 00:34:36,951 | Creating symlink; source: /Users/xxxx/sam-app-node/hello-world/node_modules, destination: /Users/xxxx/sam-app-node/.aws-sam/build/HelloWorldFunction/node_modules 2023-12-08 00:34:36,952 | NodejsNpmBuilder:LinkSource succeeded 2023-12-08 00:34:36,952 | Running NodejsNpmBuilder:CleanUpNpmrc 2023-12-08 00:34:36,952 | NodejsNpmBuilder:CleanUpNpmrc succeeded 2023-12-08 00:34:36,953 | Async execution completed 2023-12-08 00:34:36,954 | There is no customer defined id or cdk path defined for resource HelloWorldFunction, so we will use the resource logical id as the resource id 2023-12-08 00:34:36,954 | 2 resources found in the stack 2023-12-08 00:34:36,955 | Found Serverless function with name='HelloWorldFunction' and CodeUri='hello-world/' 2023-12-08 00:34:29,955 | NodejsNpmBuilder:CopySource succeeded 2023-12-08 00:34:29,956 | Running NodejsNpmBuilder:NpmUpdate 2023-12-08 00:34:29,956 | NODEJS updating in: /Users/xxxx/sam-app-node/hello-world 2023-12-08 00:34:29,956 | executing NPM: ['npm', 'update', '--no-audit', '--no-save', '--unsafe-perm', '--production', '--no-package-lock', '--install-links'] 2023-12-08 00:34:36,950 | NodejsNpmBuilder:NpmUpdate succeeded
ビルド結果を確認してみるとnode_modules
に元々のソースコード側を参照するようなシンボリックリンクが貼られていることがわかります。
% tree .aws-sam/build .aws-sam/build ├── HelloWorldFunction │ ├── app.mjs │ ├── node_modules -> /Users/xxxxx/hello-world/node_modules │ └── package.json └── template.yaml
インストール先がソースコード格納先となるのでそちらの方にnode_modulesができてインストールされる形となります。
% ls -l hello-world total 96 -rw-r--r-- 1 xxxxx staff 761 12 7 19:18 app.mjs drwxr-xr-x 95 xxxxx staff 3040 12 8 01:56 node_modules -rw-r--r-- 1 xxxxx staff 38587 12 8 01:56 package-lock.json -rw-r--r-- 1 xxxxx staff 469 12 8 01:56 package.json drwxr-xr-x 3 xxxxx staff 96 12 7 19:18 tests
終わりに
自分が正直このオプションのメリットを正確に理解できていない気がしますが少し面白いアップデートが入っているような気がします。
確認遅くなってしまうかもしれませんがぜひはてブコメントやTwitter共有コメントで有識者の嬉しい点のご意見いただけると助かります。
キャッシュなしでビルドする際には毎回npm install
の回避、キャッシュが存在しても一部ファイルの複製コストはなくせるため少なくともビルドは高速化は見込めますし、
(他に良い方法があった気がしますが)特別事情でnode_modules
の中身を書き換える際に持ち込みが簡単になるのでような使い方はあるのかなと思います。