AWS CDKのパッケージのバージョンはCDKプロジェクト内で揃えるようにしよう

2021.07.13

こんにちは、CX事業本部の若槻です。

今回は、AWS CDKのパッケージのバージョンはCDKプロジェクト内で揃えるようにしよう、という話です。

要旨

  • CDKパッケージのバージョンはCDKプロジェクト内では揃えるようにすると無用な不具合を回避できる
  • パッケージの追加はnpm install <パッケージ>@1.111.0 --save-exactのようにするとバージョンを固定できる

CDKスタックのLambdaリソースで型エラーが発生する

CDKプロジェクトを作成して下記のようなLambda Functionリソースを含むCDKスタックを作成しました。

lib/sample-app-stack.ts

import * as cdk from "@aws-cdk/core";
import * as cloudfront from "@aws-cdk/aws-cloudfront";
import * as s3 from "@aws-cdk/aws-s3";
import * as s3deploy from "@aws-cdk/aws-s3-deployment";
import * as iam from "@aws-cdk/aws-iam";
import * as lambda from "@aws-cdk/aws-lambda";

export class SampleAppStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const sampleFunction = new lambda.Function(this, "SampleFunction", {
      code: lambda.Code.asset("lambda/sample-lambda"),
      handler: "index.handler",
      runtime: lambda.Runtime.NODEJS_14_X,
      timeout: cdk.Duration.seconds(5),
    });

    //略
  }
}

するとLambda Functionリソースのプロパティのうちtimeoutで型エラーが発生していました。

エラーの内容を読んでも何が悪いんだかよく分かりません。

Type 'import("/Users/wakatsuki.ryuta/projects/local/aws-cdk-deploy-react/node_modules/@aws-cdk/core/lib/duration").Duration' is not assignable to type 'import("/Users/wakatsuki.ryuta/projects/local/aws-cdk-deploy-react/node_modules/@aws-cdk/aws-cloudfront/node_modules/@aws-cdk/core/lib/duration").Duration'.
  Types have separate declarations of a private property 'amount'.ts(2322)
function.d.ts(69, 14): The expected type comes from property 'timeout' which is declared here on type 'FunctionProps'
(property) FunctionOptions.timeout?: Duration | undefined
The function execution time (in seconds) after which Lambda terminates the function.

Because the execution time affects cost, set this value based on the function's expected execution time.

@default

Duration.seconds(3)
@stability — stable

原因調査、解決

この時のpackage.jsonが下記のようになります。よく見ると他のCDKのNodeパッケージが1.111.0または^1.111.0であるのに対し、@aws-cdk/aws-lambdaのみ^1.112.0とバージョンがずれています。

package.json

{
  "name": "aws-cdk-deploy-react",
  "version": "0.1.0",
  "bin": {
    "aws-cdk-deploy-react": "bin/aws-cdk-deploy-react.js"
  },
  "scripts": {
    "build": "tsc",
    "watch": "tsc -w",
    "test": "react-scripts test",
    "cdk": "cdk"
  },
  "devDependencies": {
    "@aws-cdk/assert": "1.111.0",
    "@types/jest": "^26.0.10",
    "@types/node": "10.17.27",
    "aws-cdk": "1.111.0",
    "jest": "^26.4.2",
    "ts-jest": "^26.2.0",
    "ts-node": "^9.0.0",
    "typescript": "~3.9.7"
  },
  "dependencies": {
    "@aws-cdk/aws-cloudfront": "^1.111.0",
    "@aws-cdk/aws-lambda": "^1.112.0",
    "@aws-cdk/aws-s3": "^1.111.0",
    "@aws-cdk/aws-s3-deployment": "^1.111.0",
    "@aws-cdk/core": "1.111.0",
    "source-map-support": "^0.5.16"
  }
}

ずれてる理由としては、Nodeパッケージをnpm install <パッケージ名>のようにしてインストールすると、その時点の最新版のパッケージがインストールされます。よって他のCDKのパッケージより後にインストールした@aws-cdk/aws-lambdaのみバージョンが上がっていたため、不具合が起きていたのでしょうか。

% npm install @aws-cdk/aws-lambda

実際にインストールされているバージョンを確認してみると、1.113.01.111.0が混在しています。@aws-cdk/aws-lambda@aws-cdk/assert@aws-cdk/coreaws-cdkとバージョンがずれています。

% npm list --depth=0
aws-cdk-deploy-react@0.1.0 /Users/wakatsuki.ryuta/projects/local/aws-cdk-deploy-react
├── @aws-cdk/assert@1.111.0
├── @aws-cdk/aws-cloudfront@1.113.0
├── @aws-cdk/aws-lambda@1.113.0
├── @aws-cdk/aws-s3-deployment@1.113.0
├── @aws-cdk/aws-s3@1.113.0
├── @aws-cdk/core@1.111.0
├── @types/jest@26.0.24
├── @types/node@10.17.27
├── aws-cdk@1.111.0
(略)

そこで下記のようにバージョンを1.111.0に指定して@aws-cdk/aws-lambdaをインストール(ダウングレード)してみます。(後述しますが、これは厳密なバージョン指定方法ではありません。)

% npm install @aws-cdk/aws-lambda@1.111.0

package.json上で@aws-cdk/aws-lambda^1.111.0となりました。

package.json

  "dependencies": {
    "@aws-cdk/aws-cloudfront": "^1.111.0",
    "@aws-cdk/aws-lambda": "^1.111.0",
    "@aws-cdk/aws-s3": "^1.111.0",
    "@aws-cdk/aws-s3-deployment": "^1.111.0",
    "@aws-cdk/core": "1.111.0",
    "source-map-support": "^0.5.16"
  }

また@aws-cdk/aws-lambdaの実際のインストールバージョンも1.111.0となっていました。

% npm list --depth=0                     
aws-cdk-deploy-react@0.1.0 /Users/wakatsuki.ryuta/projects/local/aws-cdk-deploy-react
├── @aws-cdk/assert@1.111.0
├── @aws-cdk/aws-cloudfront@1.113.0
├── @aws-cdk/aws-lambda@1.111.0
├── @aws-cdk/aws-s3-deployment@1.113.0
├── @aws-cdk/aws-s3@1.113.0
├── @aws-cdk/core@1.111.0
├── @types/jest@26.0.24
├── @types/node@10.17.27
├── aws-cdk@1.111.0
(略)

すると、Lambda Functionリソースのtimeoutのエラーは出なくなりました。

結果として、AWS CDKのパッケージのバージョンはCDKプロジェクト内で揃えるようにした方が良いようです。

Nodeパッケージのバージョンを固定する

Nodeパッケージのバージョニングはsemantic versioningであり、キャレット^が付くと「最も大きな0でないバージョニングを変えない範囲」のバージョンとなります。またnpm install @aws-cdk/aws-lambda@1.111.0のようにインストールすると既定で^付きとなります。例えば^1.111.0と指定された時のバージョンは1.111.0または1.112.0または1.113.0を取りうるので厳密なバージョン指定はできません。よって今回のような不具合を極力回避するためには、CDKパッケージをインストールする際には^を付けずにバージョンを固定してパッケージを管理する必要があります。

バージョンを固定してNodeパッケージをインストールしたい場合は、--save-exactオプションを使用します。

--save-exactを使用してCDKパッケージをインストールしてみます。

% npm install @aws-cdk/aws-lambda@1.111.0 --save-exact

するとpackage.json上でも^が付かない1.111.0となりました。

package.json

  "dependencies": {
    "@aws-cdk/aws-cloudfront": "^1.111.0",
    "@aws-cdk/aws-lambda": "1.111.0",
    "@aws-cdk/aws-s3": "^1.111.0",
    "@aws-cdk/aws-s3-deployment": "^1.111.0",
    "@aws-cdk/core": "1.111.0",
    "source-map-support": "^0.5.16"
  }

CDKパッケージを追加でインストールする場合は、既にあるほかのパッケージのバージョンに合わせて--save-exactオプションを使用してインストールを行うようにすると良さそうですね。

以上