cdk-nextjs 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +2404 -2723
- package/.jsii.tabl.json +1 -1
- package/API.md +1681 -1930
- package/CLAUDE.md +2 -1
- package/README.md +429 -130
- package/assets/lambdas/post-deploy/post-deploy.lambda/index.js +148 -169
- package/assets/lambdas/redeploy-stage/redeploy-stage.lambda/index.js +875 -0
- package/docs/adapter-runtime.md +185 -0
- package/docs/breaking-changes.md +667 -4
- package/docs/caching-guide.md +156 -112
- package/docs/cdk-nextjs-cfn-exec-policy.json +2 -1
- package/docs/development-guide.md +1 -1
- package/docs/harness-coverage.md +598 -0
- package/docs/init-cache-deployment.md +32 -1
- package/docs/next-build-output-guide.md +30 -20
- package/examples/README.md +5 -1
- package/lib/adapter/__fixtures__/app-playground-base-path.json +4926 -0
- package/lib/adapter/__fixtures__/app-playground.json +4917 -0
- package/lib/adapter/__fixtures__/pages-i18n.json +1728 -0
- package/lib/adapter/adapter.mjs +1548 -185
- package/lib/adapter/adapter.test.js +106 -0
- package/lib/adapter/aws-cache-store.d.ts +489 -0
- package/lib/adapter/aws-cache-store.js +1004 -0
- package/lib/adapter/aws-cache-store.test.d.ts +1 -0
- package/lib/adapter/aws-cache-store.test.js +361 -0
- package/lib/adapter/build-outputs.d.ts +80 -0
- package/lib/adapter/build-outputs.js +852 -0
- package/lib/adapter/build-outputs.test.d.ts +1 -0
- package/lib/adapter/build-outputs.test.js +861 -0
- package/lib/adapter/cache-handler.d.ts +9 -2
- package/lib/adapter/cache-handler.js +72 -96
- package/lib/adapter/cache-handler.mjs +18956 -17774
- package/lib/adapter/cache-handler.test.js +263 -31
- package/lib/adapter/cache-kinds.d.ts +32 -0
- package/lib/adapter/cache-kinds.js +35 -0
- package/lib/adapter/cache-kinds.test.d.ts +1 -0
- package/lib/adapter/cache-kinds.test.js +69 -0
- package/lib/adapter/cache-utils.d.ts +90 -0
- package/lib/adapter/cache-utils.js +199 -4
- package/lib/adapter/cache-utils.test.d.ts +1 -0
- package/lib/adapter/cache-utils.test.js +179 -0
- package/lib/adapter/function-groups.d.ts +215 -0
- package/lib/adapter/function-groups.js +1099 -0
- package/lib/adapter/function-groups.test.d.ts +1 -0
- package/lib/adapter/function-groups.test.js +743 -0
- package/lib/adapter/init-cache.d.ts +6 -0
- package/lib/adapter/init-cache.js +245 -0
- package/lib/adapter/init-cache.test.d.ts +1 -0
- package/lib/adapter/init-cache.test.js +137 -0
- package/lib/adapter/local-file-cache-handler.d.ts +23 -0
- package/lib/adapter/local-file-cache-handler.js +52 -1
- package/lib/adapter/memory-cache-handler.d.ts +26 -17
- package/lib/adapter/memory-cache-handler.js +32 -50
- package/lib/adapter/memory-cache-handler.test.js +105 -85
- package/lib/adapter/s3-cache-handler.d.ts +179 -40
- package/lib/adapter/s3-cache-handler.js +807 -277
- package/lib/adapter/s3-cache-handler.test.js +1186 -31
- package/lib/adapter/use-cache-common.d.ts +90 -0
- package/lib/adapter/use-cache-common.js +250 -0
- package/lib/adapter/use-cache-common.test.d.ts +1 -0
- package/lib/adapter/use-cache-common.test.js +646 -0
- package/lib/adapter/use-cache-default-handler.d.ts +24 -0
- package/lib/adapter/use-cache-default-handler.js +90 -0
- package/lib/adapter/use-cache-default-handler.mjs +38645 -0
- package/lib/adapter/use-cache-handlers.test.d.ts +1 -0
- package/lib/adapter/use-cache-handlers.test.js +349 -0
- package/lib/adapter/use-cache-remote-handler.d.ts +26 -0
- package/lib/adapter/use-cache-remote-handler.js +186 -0
- package/lib/{image-optimization/handler.mjs → adapter/use-cache-remote-handler.mjs} +33572 -38798
- package/lib/generated-structs/OptionalNextjsApiProps.d.ts +59 -0
- package/lib/generated-structs/OptionalNextjsApiProps.js +3 -0
- package/lib/generated-structs/OptionalNextjsCacheProps.d.ts +15 -6
- package/lib/generated-structs/OptionalNextjsCacheProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsContainersProps.d.ts +25 -11
- package/lib/generated-structs/OptionalNextjsContainersProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsDistributionProps.d.ts +31 -9
- package/lib/generated-structs/OptionalNextjsDistributionProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsPostDeployProps.d.ts +8 -0
- package/lib/generated-structs/OptionalNextjsPostDeployProps.js +1 -1
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.d.ts +6 -4
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.js +1 -1
- package/lib/index.d.ts +6 -7
- package/lib/index.js +2 -4
- package/lib/lambdas/post-deploy/prune-cache-bucket.d.ts +7 -3
- package/lib/lambdas/post-deploy/prune-cache-bucket.js +57 -71
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.js +114 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.js +23 -31
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.js +71 -0
- package/lib/lambdas/post-deploy/prune-s3.d.ts +14 -5
- package/lib/lambdas/post-deploy/prune-s3.js +41 -12
- package/lib/lambdas/post-deploy/prune-s3.test.js +97 -18
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.d.ts +13 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.js +55 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.d.ts +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.js +68 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.d.ts +20 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.js +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.d.ts +1 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.js +93 -0
- package/lib/nextjs-api.d.ts +102 -18
- package/lib/nextjs-api.js +338 -38
- package/lib/nextjs-api.test.js +537 -1
- package/lib/nextjs-build/global-containers.Dockerfile +15 -10
- package/lib/nextjs-build/nextjs-build.d.ts +339 -63
- package/lib/nextjs-build/nextjs-build.js +684 -327
- package/lib/nextjs-build/nextjs-build.test.d.ts +1 -0
- package/lib/nextjs-build/nextjs-build.test.js +412 -0
- package/lib/nextjs-build/patch-fetch.js +1 -1
- package/lib/nextjs-build/patch-fetch.test.js +166 -11
- package/lib/nextjs-build/regional-containers.Dockerfile +17 -10
- package/lib/nextjs-cache.d.ts +82 -4
- package/lib/nextjs-cache.js +186 -10
- package/lib/nextjs-cache.test.js +176 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.d.ts +21 -8
- package/lib/nextjs-compute/nextjs-compute-base-props.js +41 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-compute-base-props.test.js +44 -0
- package/lib/nextjs-compute/nextjs-containers.d.ts +19 -0
- package/lib/nextjs-compute/nextjs-containers.js +57 -20
- package/lib/nextjs-compute/nextjs-functions.d.ts +122 -7
- package/lib/nextjs-compute/nextjs-functions.js +113 -67
- package/lib/nextjs-compute/nextjs-functions.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-functions.test.js +99 -0
- package/lib/nextjs-distribution.d.ts +194 -23
- package/lib/nextjs-distribution.js +535 -62
- package/lib/nextjs-distribution.test.d.ts +1 -0
- package/lib/nextjs-distribution.test.js +697 -0
- package/lib/nextjs-post-deploy.d.ts +13 -5
- package/lib/nextjs-post-deploy.js +13 -10
- package/lib/nextjs-post-deploy.test.d.ts +1 -0
- package/lib/nextjs-post-deploy.test.js +34 -0
- package/lib/nextjs-static-assets.d.ts +17 -4
- package/lib/nextjs-static-assets.js +22 -5
- package/lib/root-constructs/nextjs-base-construct.d.ts +102 -58
- package/lib/root-constructs/nextjs-base-construct.js +139 -47
- package/lib/root-constructs/nextjs-global-containers.d.ts +18 -20
- package/lib/root-constructs/nextjs-global-containers.js +20 -71
- package/lib/root-constructs/nextjs-global-functions.d.ts +26 -28
- package/lib/root-constructs/nextjs-global-functions.js +52 -70
- package/lib/root-constructs/nextjs-regional-containers.d.ts +18 -5
- package/lib/root-constructs/nextjs-regional-containers.js +8 -15
- package/lib/root-constructs/nextjs-regional-functions.d.ts +22 -18
- package/lib/root-constructs/nextjs-regional-functions.js +8 -26
- package/lib/runtime/api-gateway-path.d.ts +34 -0
- package/lib/runtime/api-gateway-path.js +71 -0
- package/lib/runtime/api-gateway-path.test.d.ts +1 -0
- package/lib/runtime/api-gateway-path.test.js +65 -0
- package/lib/runtime/case-redirect.d.ts +22 -0
- package/lib/runtime/case-redirect.js +63 -0
- package/lib/runtime/case-redirect.test.d.ts +1 -0
- package/lib/runtime/case-redirect.test.js +32 -0
- package/lib/runtime/core.d.ts +234 -0
- package/lib/runtime/core.js +1213 -0
- package/lib/runtime/core.test.d.ts +1 -0
- package/lib/runtime/core.test.js +1678 -0
- package/lib/runtime/deployment-root.d.ts +13 -0
- package/lib/runtime/deployment-root.js +29 -0
- package/lib/runtime/dispatch.d.ts +331 -0
- package/lib/runtime/dispatch.js +751 -0
- package/lib/runtime/dispatch.test.d.ts +1 -0
- package/lib/runtime/dispatch.test.js +853 -0
- package/lib/runtime/entrypoints.d.ts +57 -0
- package/lib/runtime/entrypoints.js +115 -0
- package/lib/runtime/http/abort-signal.d.ts +6 -0
- package/lib/runtime/http/abort-signal.js +19 -0
- package/lib/runtime/http/abort-signal.test.d.ts +1 -0
- package/lib/runtime/http/abort-signal.test.js +49 -0
- package/lib/runtime/http/node-server.d.ts +38 -0
- package/lib/runtime/http/node-server.js +99 -0
- package/lib/runtime/http/node-server.test.d.ts +1 -0
- package/lib/runtime/http/node-server.test.js +96 -0
- package/lib/runtime/http/node-sink.d.ts +16 -0
- package/lib/runtime/http/node-sink.js +48 -0
- package/lib/runtime/http/node-sink.test.d.ts +1 -0
- package/lib/runtime/http/node-sink.test.js +66 -0
- package/lib/runtime/http/request.d.ts +62 -0
- package/lib/runtime/http/request.js +134 -0
- package/lib/runtime/http/request.test.d.ts +1 -0
- package/lib/runtime/http/request.test.js +103 -0
- package/lib/runtime/http/response.d.ts +77 -0
- package/lib/runtime/http/response.js +229 -0
- package/lib/runtime/http/response.test.d.ts +1 -0
- package/lib/runtime/http/response.test.js +178 -0
- package/lib/runtime/http/sink.d.ts +73 -0
- package/lib/runtime/http/sink.js +188 -0
- package/lib/runtime/http/sink.test.d.ts +1 -0
- package/lib/runtime/http/sink.test.js +257 -0
- package/lib/runtime/image-utils.d.ts +78 -0
- package/lib/runtime/image-utils.js +129 -0
- package/lib/runtime/image-utils.test.d.ts +1 -0
- package/lib/runtime/image-utils.test.js +237 -0
- package/lib/runtime/image.d.ts +76 -0
- package/lib/runtime/image.js +296 -0
- package/lib/runtime/image.test.d.ts +1 -0
- package/lib/runtime/image.test.js +405 -0
- package/lib/runtime/lambda-event.d.ts +4 -0
- package/lib/runtime/lambda-event.js +113 -0
- package/lib/runtime/lambda-event.test.d.ts +1 -0
- package/lib/runtime/lambda-event.test.js +119 -0
- package/lib/runtime/lambda.mjs +41595 -0
- package/lib/runtime/load-module.d.ts +8 -0
- package/lib/runtime/load-module.js +35 -0
- package/lib/runtime/manifest.d.ts +215 -0
- package/lib/runtime/manifest.js +118 -0
- package/lib/runtime/middleware.d.ts +43 -0
- package/lib/runtime/middleware.js +113 -0
- package/lib/runtime/middleware.test.d.ts +1 -0
- package/lib/runtime/middleware.test.js +259 -0
- package/lib/runtime/next-modules.d.ts +55 -0
- package/lib/runtime/next-modules.js +124 -0
- package/lib/runtime/next-modules.test.d.ts +1 -0
- package/lib/runtime/next-modules.test.js +34 -0
- package/lib/runtime/public-files.d.ts +32 -0
- package/lib/runtime/public-files.js +117 -0
- package/lib/runtime/public-files.test.d.ts +1 -0
- package/lib/runtime/public-files.test.js +97 -0
- package/lib/runtime/server.mjs +41588 -0
- package/lib/runtime/static-files.d.ts +50 -0
- package/lib/runtime/static-files.js +266 -0
- package/lib/runtime/static-files.test.d.ts +1 -0
- package/lib/runtime/static-files.test.js +118 -0
- package/lib/runtime/util.d.ts +28 -0
- package/lib/runtime/util.js +79 -0
- package/lib/utils/base-path.d.ts +106 -13
- package/lib/utils/base-path.js +254 -41
- package/lib/utils/base-path.test.js +219 -51
- package/lib/utils/get-architecture.d.ts +7 -8
- package/lib/utils/get-architecture.js +12 -13
- package/lib/utils/zip-directory.d.ts +14 -0
- package/lib/utils/zip-directory.js +121 -0
- package/lib/utils/zip-directory.test.d.ts +1 -0
- package/lib/utils/zip-directory.test.js +99 -0
- package/package.json +22 -3
- package/scripts/capture-adapter-fixture.mjs +325 -0
- package/scripts/e2e-deploy.sh +398 -0
- package/scripts/e2e-harness/README.md +707 -0
- package/scripts/e2e-harness/app.js +370 -0
- package/scripts/e2e-harness/common.sh +249 -0
- package/scripts/e2e-harness/screen.mjs +302 -0
- package/scripts/e2e-harness/stage-proxy.mjs +145 -0
- package/scripts/e2e-harness/type-filters.mjs +47 -0
- package/scripts/e2e-logs.sh +75 -0
- package/scripts/e2e-offline.sh +91 -0
- package/scripts/e2e-sweep.sh +370 -0
- package/scripts/e2e-warm.sh +114 -0
- package/scripts/zero-config-build.mjs +418 -0
- package/docs/1k-concurrent-users-executions.png +0 -0
- package/docs/plans/adapter-runtime-release.md +0 -362
- package/docs/plans/phase-0-image-function-opt-in.md +0 -295
- package/lib/generated-structs/OptionalDockerImageFunctionProps.d.ts +0 -425
- package/lib/generated-structs/OptionalDockerImageFunctionProps.js +0 -3
- package/lib/image-optimization/handler-utils.d.ts +0 -34
- package/lib/image-optimization/handler-utils.js +0 -83
- package/lib/image-optimization/handler-utils.test.js +0 -178
- package/lib/nextjs-build/functions.Dockerfile +0 -31
- package/lib/nextjs-compute/nextjs-image-function.d.ts +0 -53
- package/lib/nextjs-compute/nextjs-image-function.js +0 -59
- package/lib/utils/experimental-flags.d.ts +0 -24
- package/lib/utils/experimental-flags.js +0 -30
- /package/lib/{image-optimization/handler-utils.test.d.ts → adapter/adapter.test.d.ts} +0 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# NEXT_TEST_DEPLOY_SCRIPT_PATH for the Next.js adapter compatibility harness.
|
|
4
|
+
#
|
|
5
|
+
# Runs with `cwd` set to the isolated temporary app the harness created for one
|
|
6
|
+
# test file, and must print the deployment URL - and nothing else - to stdout.
|
|
7
|
+
#
|
|
8
|
+
# @see https://nextjs.org/docs/app/api-reference/adapters/testing-adapters
|
|
9
|
+
# @see scripts/e2e-harness/README.md
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
# stdout is the URL channel. Keep a copy of it on fd 3 and point fd 1 at stderr
|
|
13
|
+
# so that every command below, and anything it spawns, is diagnostics.
|
|
14
|
+
exec 3>&1
|
|
15
|
+
exec 1>&2
|
|
16
|
+
|
|
17
|
+
: "${ADAPTER_DIR:?must be set to the cdk-nextjs checkout (the harness passes it through)}"
|
|
18
|
+
HARNESS_DIR="$ADAPTER_DIR/scripts/e2e-harness"
|
|
19
|
+
# shellcheck source=scripts/e2e-harness/common.sh
|
|
20
|
+
source "$HARNESS_DIR/common.sh"
|
|
21
|
+
|
|
22
|
+
APP_DIR="$PWD"
|
|
23
|
+
CDK_BIN="${CDK_BIN:-$ADAPTER_DIR/node_modules/.bin/cdk}"
|
|
24
|
+
# Every file the package's `exports` points at: the adapter, and the cache
|
|
25
|
+
# handlers it resolves through them (src/adapter/adapter.mts). Read first, so
|
|
26
|
+
# that a failed read ends the script rather than checking nothing.
|
|
27
|
+
EXPORTED="$(node -p 'Object.values(require(process.argv[1]).exports).map((e) => e.default).join("\n")' "$ADAPTER_DIR/package.json")"
|
|
28
|
+
while IFS= read -r required; do
|
|
29
|
+
if [ ! -f "$ADAPTER_DIR/$required" ]; then
|
|
30
|
+
echo "harness: $ADAPTER_DIR/$required is missing. Run \`pnpm bundle && pnpm compile\` in $ADAPTER_DIR." >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
done <<<"$EXPORTED"
|
|
34
|
+
if [ ! -x "$CDK_BIN" ]; then
|
|
35
|
+
echo "harness: no CDK CLI at $CDK_BIN. Run \`pnpm i\` in $ADAPTER_DIR, or set CDK_BIN." >&2
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
STACK_NAME="$(harness_stack_name)"
|
|
40
|
+
printf '%s\n' "$STACK_NAME" >"$HARNESS_STACK_FILE"
|
|
41
|
+
echo "harness: app=$APP_DIR stack=$STACK_NAME"
|
|
42
|
+
|
|
43
|
+
# A previous file's deploy that outran its per-file timeout was killed here, but
|
|
44
|
+
# its `cdk deploy` can outlive it, and a full update keeps going server-side.
|
|
45
|
+
# Deploying over either would fail or interleave two fixtures, so wait for both
|
|
46
|
+
# to settle: the process for HARNESS_ORPHAN_WAIT (10 minutes) before it is
|
|
47
|
+
# killed, the stack status for 30 minutes. The status checks below and
|
|
48
|
+
# `cdk deploy` itself report what is left. (A change set that was never
|
|
49
|
+
# executed, REVIEW_IN_PROGRESS, never settles by itself.)
|
|
50
|
+
harness_wait_for_cdk "$STACK_NAME"
|
|
51
|
+
for _ in $(seq 1 120); do
|
|
52
|
+
STACK_STATUS="$(harness_stack_status "$STACK_NAME")" || break
|
|
53
|
+
case "$STACK_STATUS" in
|
|
54
|
+
REVIEW_IN_PROGRESS) break ;;
|
|
55
|
+
*_IN_PROGRESS) echo "harness: $STACK_NAME is $STACK_STATUS; waiting" ;;
|
|
56
|
+
*) break ;;
|
|
57
|
+
esac
|
|
58
|
+
sleep 15
|
|
59
|
+
done
|
|
60
|
+
|
|
61
|
+
# The harness creates the app with `skipInstall`, so there is no node_modules yet.
|
|
62
|
+
#
|
|
63
|
+
# Which package manager installs it is the app's choice, not ours: a fixture can
|
|
64
|
+
# override `packageJson.packageManager` (`test/lib/next-modes/base.ts` merges it in
|
|
65
|
+
# verbatim), and `corepack pnpm` then hard-refuses with "This project is configured
|
|
66
|
+
# to use npm because …/package.json has a packageManager field" - which is how
|
|
67
|
+
# test/e2e/handle-non-hoisted-swc-helpers, the one fixture that pins `npm@10.9.2`,
|
|
68
|
+
# failed its deploy outright. So read the field and honor it.
|
|
69
|
+
#
|
|
70
|
+
# corepack is only used for pnpm, where the pinned version matters and this repo
|
|
71
|
+
# already relies on it. `npm` ships with node, and going through corepack would
|
|
72
|
+
# add a download of a specific npm build for no benefit.
|
|
73
|
+
PM="$(node -e 'const f=require("path").join(process.argv[1],"package.json");let s="pnpm";try{s=(require(f).packageManager||"pnpm").split("@")[0]}catch{};process.stdout.write(s)' "$APP_DIR")"
|
|
74
|
+
case "$PM" in
|
|
75
|
+
pnpm)
|
|
76
|
+
if command -v corepack >/dev/null 2>&1; then PM_CMD=(corepack pnpm); else PM_CMD=(pnpm); fi
|
|
77
|
+
# Without it pnpm walks up out of /tmp looking for a workspace root.
|
|
78
|
+
INSTALL_ARGS=(--ignore-workspace --prefer-offline)
|
|
79
|
+
;;
|
|
80
|
+
*)
|
|
81
|
+
PM_CMD=("$PM")
|
|
82
|
+
# No `--prefer-offline` here, deliberately. pnpm treats it as a hint and falls
|
|
83
|
+
# back to the network when a cached packument has no matching version; npm
|
|
84
|
+
# treats the stale packument as the answer and fails outright with
|
|
85
|
+
#
|
|
86
|
+
# npm error code ETARGET
|
|
87
|
+
# npm error notarget No matching version found for next@16.3.5.
|
|
88
|
+
#
|
|
89
|
+
# even though the registry does have it. Any recently published version in the
|
|
90
|
+
# tree hits this - reproduced with `next` itself and, on a warmer cache, with
|
|
91
|
+
# its transitive `postcss@8.5.23` - so it fails whenever this machine's npm
|
|
92
|
+
# cache predates the `next` release the fixtures pin. It is what made
|
|
93
|
+
# test/e2e/handle-non-hoisted-swc-helpers, the one fixture that pins
|
|
94
|
+
# `npm@10.9.2`, look like a deploy failure.
|
|
95
|
+
INSTALL_ARGS=(--no-audit --no-fund)
|
|
96
|
+
;;
|
|
97
|
+
esac
|
|
98
|
+
echo "harness: package manager $PM (${PM_CMD[*]})"
|
|
99
|
+
# The `build` script the harness writes always chains `pnpm post-build`
|
|
100
|
+
# (hardcoded in base.ts), so pnpm has to be on PATH whatever $PM is - and for a
|
|
101
|
+
# fixture that pins a *different* package manager, pnpm refuses to run any script
|
|
102
|
+
# at all:
|
|
103
|
+
#
|
|
104
|
+
# ERROR This project is configured to use npm
|
|
105
|
+
#
|
|
106
|
+
# which is pnpm honoring `packageJson.packageManager`. There is no way to reach
|
|
107
|
+
# next.js's hardcoded `pnpm post-build`, so the check has to be turned off rather
|
|
108
|
+
# than routed around. This is the documented opt-out and it only affects the
|
|
109
|
+
# packageManager assertion, not resolution or the lockfile. Exported for the
|
|
110
|
+
# `next build` below, which is what runs the chained script.
|
|
111
|
+
export npm_config_package_manager_strict=false
|
|
112
|
+
# Run a command without any credentials: the fixture's install and build are
|
|
113
|
+
# where third-party code runs, and neither needs AWS - only `cdk deploy` and the
|
|
114
|
+
# aws CLI calls below do. In a subshell so they keep them. The config and
|
|
115
|
+
# credentials files are pointed away too, for a local run whose credentials live
|
|
116
|
+
# there rather than in the environment; and the Actions runtime and OIDC request
|
|
117
|
+
# tokens go with them, since with `id-token: write` those mint a token that
|
|
118
|
+
# assumes the deploy role.
|
|
119
|
+
without_credentials() (
|
|
120
|
+
while IFS= read -r var; do unset "$var"; done < <(compgen -e | grep -E '^(AWS_|ACTIONS_ID_TOKEN_REQUEST_|ACTIONS_RUNTIME_TOKEN$)')
|
|
121
|
+
export AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null
|
|
122
|
+
"$@"
|
|
123
|
+
)
|
|
124
|
+
without_credentials "${PM_CMD[@]}" install "${INSTALL_ARGS[@]}"
|
|
125
|
+
|
|
126
|
+
# Make the adapter resolvable as a package rather than a loose file: the adapter
|
|
127
|
+
# resolves its own cache handler with
|
|
128
|
+
# `import.meta.resolve("cdk-nextjs/cache-handler")` (src/adapter/adapter.mts), so
|
|
129
|
+
# it has to be reachable through a package boundary with this repo's `exports`
|
|
130
|
+
# map. Same shape as `examples/app-playground`'s `sync-cdk-nextjs` script. After
|
|
131
|
+
# `pnpm install`, which prunes directories it does not know about.
|
|
132
|
+
LOCAL_PKG="node_modules/cdk-nextjs"
|
|
133
|
+
mkdir -p "$LOCAL_PKG/lib/adapter"
|
|
134
|
+
cp "$ADAPTER_DIR/package.json" "$LOCAL_PKG/package.json"
|
|
135
|
+
# Every bundle, not the list checked above, so a new `exports` entry is copied
|
|
136
|
+
# without anyone remembering to add it here.
|
|
137
|
+
cp "$ADAPTER_DIR"/lib/adapter/*.mjs "$LOCAL_PKG/lib/adapter/"
|
|
138
|
+
# `next build` reads this into `config.adapterPath`
|
|
139
|
+
# (next/dist/server/config-shared.js), which is how the harness's fixtures - who
|
|
140
|
+
# know nothing about cdk-nextjs - get built through our adapter.
|
|
141
|
+
export NEXT_ADAPTER_PATH="$APP_DIR/$LOCAL_PKG/lib/adapter/adapter.mjs"
|
|
142
|
+
# Feeds the fixture's own `post-build` marker line, and the runtime's
|
|
143
|
+
# `?dpl=` skew handling if a test exercises it. Per app directory rather than per
|
|
144
|
+
# stack: with one shared stack the stack name is a constant, and two builds
|
|
145
|
+
# sharing a deployment ID is exactly the skew this is supposed to detect.
|
|
146
|
+
export NEXT_DEPLOYMENT_ID="$(harness_app_id "$APP_DIR")"
|
|
147
|
+
export NEXT_TELEMETRY_DISABLED=1
|
|
148
|
+
# The harness appends a snippet to every fixture's next.config that aliases this
|
|
149
|
+
# to `__NEXT_TEST_MODE` (`test/lib/next-modes/base.ts`, "alias __NEXT_TEST_MODE
|
|
150
|
+
# for next-deploy"), and `define-env.ts` inlines `process.env.__NEXT_TEST_MODE`
|
|
151
|
+
# into the server bundle at build time. Unset, it inlines as `false` and the
|
|
152
|
+
# test-only branches are dead-code eliminated - including the
|
|
153
|
+
# `<!-- PPR_BOUNDARY_SENTINEL -->` chunk that separates a PPR response's static
|
|
154
|
+
# shell from its dynamic part. `splitResponseWithPPRSentinel`
|
|
155
|
+
# (`test/lib/e2e-utils/ppr.ts`) then puts the entire document in `staticPart`, and
|
|
156
|
+
# every `expect(result.static$('#dynamic-thing').length).toBe(0)` fails. Vercel
|
|
157
|
+
# passes it as `--build-env NEXT_PRIVATE_TEST_MODE=e2e`; this is the same thing.
|
|
158
|
+
export NEXT_PRIVATE_TEST_MODE=e2e
|
|
159
|
+
|
|
160
|
+
echo "harness: building with NEXT_ADAPTER_PATH=$NEXT_ADAPTER_PATH"
|
|
161
|
+
# `run build`, not `build`: pnpm accepts the bare script name but npm does not.
|
|
162
|
+
without_credentials "${PM_CMD[@]}" run build 2>&1 | tee "$HARNESS_BUILD_LOG"
|
|
163
|
+
|
|
164
|
+
# The markers the harness parses out of the logs script's output
|
|
165
|
+
# (`test/lib/next-modes/next-deploy.ts`'s `parseIdsFromCliOutput`). The fixture's
|
|
166
|
+
# chained `post-build` prints its own copy into the build log; ours is written
|
|
167
|
+
# first and `.match()` takes the first hit, so these win.
|
|
168
|
+
#
|
|
169
|
+
# DEPLOYMENT_ID has to be the value the *build* inlined, not the stack name: next
|
|
170
|
+
# inlines `NEXT_DEPLOYMENT_ID` into every asset URL as `?dpl=`, and the harness
|
|
171
|
+
# compares those URLs against this marker (`base.ts`'s `getDeploymentIdQuery`).
|
|
172
|
+
# Reporting the stack name made `mdx`'s "should work with next/image" fail on a
|
|
173
|
+
# `?dpl=hrns-shared` that the app never emits.
|
|
174
|
+
BUILD_ID="$(cat .next/BUILD_ID)"
|
|
175
|
+
{
|
|
176
|
+
echo "BUILD_ID: $BUILD_ID"
|
|
177
|
+
echo "DEPLOYMENT_ID: $NEXT_DEPLOYMENT_ID"
|
|
178
|
+
# Flip to 1 once cdk-nextjs opts into `config.supportsImmutableAssets`; until
|
|
179
|
+
# then static assets are re-uploaded per deploy under the same keys.
|
|
180
|
+
echo "NEXT_SUPPORTS_IMMUTABLE_ASSETS: 0"
|
|
181
|
+
} >"$HARNESS_MARKERS_FILE"
|
|
182
|
+
|
|
183
|
+
export HARNESS_APP_DIR="$APP_DIR"
|
|
184
|
+
export HARNESS_STACK_NAME="$STACK_NAME"
|
|
185
|
+
echo "harness: deploying $STACK_NAME"
|
|
186
|
+
# Into one shared stack, so that only the first test file of a run pays for
|
|
187
|
+
# creating and propagating a CloudFront distribution. `--hotswap-fallback` takes
|
|
188
|
+
# the fast path when everything that changed is the function's code, its
|
|
189
|
+
# environment or a bucket deployment, and a full CloudFormation deployment
|
|
190
|
+
# otherwise. The one remaining reason for the latter is the distribution's cache
|
|
191
|
+
# behaviors, which change when a fixture's `public/` differs from the last one's -
|
|
192
|
+
# see the table in scripts/e2e-harness/README.md for what actually differs between
|
|
193
|
+
# two fixtures.
|
|
194
|
+
#
|
|
195
|
+
# `--output` inside the app directory: a shared cdk.out would have two shards'
|
|
196
|
+
# test files overwrite each other's assembly.
|
|
197
|
+
#
|
|
198
|
+
# The container types hotswap too - a new image and a changed environment are
|
|
199
|
+
# both task-definition changes - but a hotswap into ECS is a rolling deployment,
|
|
200
|
+
# and by default it keeps the old task serving until the new one is healthy.
|
|
201
|
+
# Minimum 0% lets ECS stop the previous fixture's task without waiting for that.
|
|
202
|
+
# Maximum stays at 200%: 100% would force stop-then-start, but the service has
|
|
203
|
+
# Availability Zone Rebalancing on, which rejects any maximum <= 100% with an
|
|
204
|
+
# `InvalidParameterException`. The overlap that leaves is covered twice: the
|
|
205
|
+
# hotswap blocks until the service is stable (one deployment left, running the
|
|
206
|
+
# desired count), and the target-group wait below until only the new task is
|
|
207
|
+
# registered. Bounded, because a fixture whose server never comes up would
|
|
208
|
+
# otherwise hold the shard until the job's timeout; the test file fails either
|
|
209
|
+
# way.
|
|
210
|
+
HOTSWAP_ARGS=(--hotswap-fallback)
|
|
211
|
+
case "$(harness_nextjs_type)" in
|
|
212
|
+
*-containers)
|
|
213
|
+
HOTSWAP_ARGS+=(
|
|
214
|
+
--hotswap-ecs-minimum-healthy-percent 0
|
|
215
|
+
--hotswap-ecs-maximum-healthy-percent 200
|
|
216
|
+
--hotswap-ecs-stabilization-timeout-seconds 600
|
|
217
|
+
)
|
|
218
|
+
;;
|
|
219
|
+
esac
|
|
220
|
+
# Bounded by HARNESS_DEPLOY_TIMEOUT (30 minutes), which the slowest real deploy -
|
|
221
|
+
# a Global Containers warm-up creating its distribution, ~20 minutes - fits
|
|
222
|
+
# under. A deploy that hangs past it is stopped here instead of holding the
|
|
223
|
+
# shard; see `harness_wait_for_cdk`.
|
|
224
|
+
harness_run_bounded "${HARNESS_DEPLOY_TIMEOUT:-1800}" \
|
|
225
|
+
"$CDK_BIN" deploy "$STACK_NAME" \
|
|
226
|
+
--app "node $HARNESS_DIR/app.js" \
|
|
227
|
+
--output "$APP_DIR/$HARNESS_CDK_OUT" \
|
|
228
|
+
"${HOTSWAP_ARGS[@]}" \
|
|
229
|
+
--require-approval never \
|
|
230
|
+
--ci 2>&1 | tee "$HARNESS_DEPLOY_LOG"
|
|
231
|
+
|
|
232
|
+
URL="$(harness_stack_output "$STACK_NAME" HarnessUrl)"
|
|
233
|
+
if [ -z "$URL" ]; then
|
|
234
|
+
echo "harness: deploy reported success but $STACK_NAME has no HarnessUrl output" >&2
|
|
235
|
+
exit 1
|
|
236
|
+
fi
|
|
237
|
+
# The host in lowercase, as a browser reports it. An ALB's DNS name has capitals
|
|
238
|
+
# (`hrns-r-Nextj-7SK6...`), and the suite compares `browser.url()` with
|
|
239
|
+
# `next.url + path` exactly, so on the Containers types every such assertion
|
|
240
|
+
# failed on the host alone. Only the host: the path (Regional Functions' stage)
|
|
241
|
+
# is case-sensitive.
|
|
242
|
+
URL_REST="${URL#*://}"
|
|
243
|
+
URL_HOST="${URL_REST%%/*}"
|
|
244
|
+
URL="${URL%%://*}://$(printf '%s' "$URL_HOST" | tr '[:upper:]' '[:lower:]')${URL_REST#"$URL_HOST"}"
|
|
245
|
+
|
|
246
|
+
NEXTJS_TYPE="$(harness_nextjs_type)"
|
|
247
|
+
case "$NEXTJS_TYPE" in
|
|
248
|
+
*-containers)
|
|
249
|
+
# A hotswap into ECS returns once `runningCount` reaches `desiredCount`, which
|
|
250
|
+
# is before the load balancer routes to the new task: it is still in the
|
|
251
|
+
# target group's `initial` health check, and the ALB answers 503 until it
|
|
252
|
+
# passes. A full CloudFormation update waits for that, but leaves the old task
|
|
253
|
+
# `draining` behind it, still answering. So wait here, either way, until the
|
|
254
|
+
# target group holds exactly one target and it is `healthy`.
|
|
255
|
+
TARGET_GROUP_ARN="$(harness_stack_output "$STACK_NAME" TargetGroupArn)"
|
|
256
|
+
if [ -z "$TARGET_GROUP_ARN" ]; then
|
|
257
|
+
echo "harness: $STACK_NAME has no TargetGroupArn output; cannot wait for the new task" >&2
|
|
258
|
+
exit 1
|
|
259
|
+
fi
|
|
260
|
+
TARGETS=""
|
|
261
|
+
for _ in $(seq 1 60); do
|
|
262
|
+
TARGETS="$(aws elbv2 describe-target-health --target-group-arn "$TARGET_GROUP_ARN" \
|
|
263
|
+
--query 'TargetHealthDescriptions[].TargetHealth.State' --output text)"
|
|
264
|
+
[ "$TARGETS" = "healthy" ] && break
|
|
265
|
+
sleep 5
|
|
266
|
+
done
|
|
267
|
+
if [ "$TARGETS" != "healthy" ]; then
|
|
268
|
+
echo "harness: $TARGET_GROUP_ARN never settled on one healthy target (last: ${TARGETS:-none})" >&2
|
|
269
|
+
exit 1
|
|
270
|
+
fi
|
|
271
|
+
echo "harness: new task healthy in $TARGET_GROUP_ARN"
|
|
272
|
+
;;
|
|
273
|
+
esac
|
|
274
|
+
|
|
275
|
+
if [ "$NEXTJS_TYPE" = "regional-containers" ]; then
|
|
276
|
+
# Nothing more to do: no distribution to invalidate, and no stage to put back.
|
|
277
|
+
:
|
|
278
|
+
elif [ "$NEXTJS_TYPE" = "regional-functions" ]; then
|
|
279
|
+
# No distribution to invalidate - API Gateway caches nothing unless told to.
|
|
280
|
+
# What there is instead is the stage in `$URL`, which the harness would strip
|
|
281
|
+
# from every request (see stage-proxy.mjs), so the suite is pointed at a
|
|
282
|
+
# localhost proxy that puts it back.
|
|
283
|
+
#
|
|
284
|
+
# One proxy per stack, reused by every test file: the API ID survives hotswaps
|
|
285
|
+
# and full updates alike, so the target does not change. It is restarted only
|
|
286
|
+
# when it is gone or its target differs (a stack that was deleted and
|
|
287
|
+
# recreated).
|
|
288
|
+
PROXY_PORT="$(harness_proxy_port "$STACK_NAME")"
|
|
289
|
+
PROXY_STATE="$(harness_proxy_state "$STACK_NAME")"
|
|
290
|
+
if PROXY_PID="$(harness_proxy_pid "$STACK_NAME")" \
|
|
291
|
+
&& [ "$(cat "$PROXY_STATE.target" 2>/dev/null)" = "$URL" ]; then
|
|
292
|
+
echo "harness: reusing stage proxy $PROXY_PID on :$PROXY_PORT"
|
|
293
|
+
else
|
|
294
|
+
harness_stop_proxy "$STACK_NAME"
|
|
295
|
+
# Detached, and with none of this script's descriptors: the harness reads
|
|
296
|
+
# the URL from our stdout until EOF, so a child still holding fd 3 (or the
|
|
297
|
+
# stderr pipe) would hang it.
|
|
298
|
+
nohup node "$HARNESS_DIR/stage-proxy.mjs" "$PROXY_PORT" "$URL" \
|
|
299
|
+
</dev/null >"$PROXY_STATE.log" 2>&1 3>&- &
|
|
300
|
+
PROXY_PID="$!"
|
|
301
|
+
printf '%s' "$PROXY_PID" >"$PROXY_STATE.pid"
|
|
302
|
+
printf '%s' "$URL" >"$PROXY_STATE.target"
|
|
303
|
+
# Ready when *this* proxy says it is listening - it logs that from the
|
|
304
|
+
# `listen` callback - not when something answers on the port: a leftover
|
|
305
|
+
# proxy for another stack answering there would have the suite test the
|
|
306
|
+
# wrong deployment, while ours died on EADDRINUSE.
|
|
307
|
+
PROXY_UP=0
|
|
308
|
+
for _ in $(seq 1 50); do
|
|
309
|
+
if grep -q "^stage-proxy: http://127.0.0.1:$PROXY_PORT " "$PROXY_STATE.log" 2>/dev/null; then
|
|
310
|
+
PROXY_UP=1
|
|
311
|
+
break
|
|
312
|
+
fi
|
|
313
|
+
kill -0 "$PROXY_PID" 2>/dev/null || break
|
|
314
|
+
sleep 0.1
|
|
315
|
+
done
|
|
316
|
+
if [ "$PROXY_UP" != "1" ]; then
|
|
317
|
+
echo "harness: stage proxy for $STACK_NAME did not start on :$PROXY_PORT; its log:" >&2
|
|
318
|
+
cat "$PROXY_STATE.log" >&2 || true
|
|
319
|
+
kill "$PROXY_PID" 2>/dev/null || true
|
|
320
|
+
rm -f "$PROXY_STATE.pid" "$PROXY_STATE.target"
|
|
321
|
+
exit 1
|
|
322
|
+
fi
|
|
323
|
+
echo "harness: started stage proxy $PROXY_PID on :$PROXY_PORT -> $URL"
|
|
324
|
+
fi
|
|
325
|
+
URL="http://127.0.0.1:$PROXY_PORT"
|
|
326
|
+
|
|
327
|
+
# Then wait out the stage switch. A fixture whose `basePath` differs from the
|
|
328
|
+
# last one's changes the API's resource tree, so the deploy is a full
|
|
329
|
+
# CloudFormation update with a new stage deployment - and CloudFormation reports
|
|
330
|
+
# it complete before the stage serves it. Measured: the first requests after
|
|
331
|
+
# such a deploy answered 500, or a 403 `MissingAuthenticationTokenException`
|
|
332
|
+
# for the resource tree that was just replaced, with nothing in the Lambda's
|
|
333
|
+
# log. So wait until API Gateway stops answering with its own errors
|
|
334
|
+
# (`x-amzn-errortype`) or a 5xx. Bounded: a fixture can answer 500 on purpose,
|
|
335
|
+
# and that is the test's to report.
|
|
336
|
+
#
|
|
337
|
+
# One probe per kind of resource the tree has, because they did not become
|
|
338
|
+
# ready together: the base resource answered while `_next/static` (the S3
|
|
339
|
+
# integration) and the `{proxy+}` catch-all under it still 500'd. The build
|
|
340
|
+
# manifest is a real object; the catch-all probe is a path the app 404s.
|
|
341
|
+
BASE_PATH="$(node -e 'try{process.stdout.write(require(process.argv[1]).config.basePath||"")}catch{}' "$APP_DIR/.next/required-server-files.json")"
|
|
342
|
+
PROBES=("$BASE_PATH/" "$BASE_PATH/_next/static/$BUILD_ID/_buildManifest.js" "$BASE_PATH/__cdk-nextjs-harness-probe")
|
|
343
|
+
api_gateway_ready() {
|
|
344
|
+
local path head status
|
|
345
|
+
for path in "${PROBES[@]}"; do
|
|
346
|
+
head="$(curl -s -o /dev/null -D - "$URL$path" || true)"
|
|
347
|
+
status="$(printf '%s' "$head" | awk 'NR==1{print $2}')"
|
|
348
|
+
if [ -z "$status" ] || [ "${status:0:1}" = "5" ] \
|
|
349
|
+
|| printf '%s' "$head" | grep -qi '^x-amzn-errortype:'; then
|
|
350
|
+
STATUS="$path -> ${status:-no answer}"
|
|
351
|
+
return 1
|
|
352
|
+
fi
|
|
353
|
+
done
|
|
354
|
+
STATUS="all ${#PROBES[@]} probes"
|
|
355
|
+
}
|
|
356
|
+
# Five rounds in a row, not one: the new deployment reaches API Gateway's fleet
|
|
357
|
+
# unevenly, and a test got the replaced tree's 403 right after all three
|
|
358
|
+
# probes had passed once. 40 x 3s overall: the replaced tree was measured
|
|
359
|
+
# still answering ~90s after CloudFormation reported UPDATE_COMPLETE.
|
|
360
|
+
READY_ROUNDS=0
|
|
361
|
+
for _ in $(seq 1 40); do
|
|
362
|
+
if api_gateway_ready; then
|
|
363
|
+
READY_ROUNDS=$((READY_ROUNDS + 1))
|
|
364
|
+
[ "$READY_ROUNDS" -ge 5 ] && break
|
|
365
|
+
else
|
|
366
|
+
READY_ROUNDS=0
|
|
367
|
+
fi
|
|
368
|
+
sleep 3
|
|
369
|
+
done
|
|
370
|
+
echo "harness: $URL ready: $STATUS"
|
|
371
|
+
else
|
|
372
|
+
# Then evict the previous test file's app from the edge.
|
|
373
|
+
#
|
|
374
|
+
# Two things make this mandatory rather than hygienic: the stack is shared, so
|
|
375
|
+
# the distribution has the last fixture's responses cached under the very paths
|
|
376
|
+
# this one is about to request; and a hotswap never runs CloudFormation, so the
|
|
377
|
+
# post-deploy custom resource that would normally invalidate never fires (its
|
|
378
|
+
# properties are pinned in app.js for exactly that reason). Blocking until it
|
|
379
|
+
# completes, because the first request the harness makes is the one that would
|
|
380
|
+
# read a stale response.
|
|
381
|
+
DISTRIBUTION_ID="$(harness_stack_output "$STACK_NAME" DistributionId)"
|
|
382
|
+
if [ -z "$DISTRIBUTION_ID" ]; then
|
|
383
|
+
echo "harness: $STACK_NAME has no DistributionId output; cannot invalidate" >&2
|
|
384
|
+
exit 1
|
|
385
|
+
fi
|
|
386
|
+
echo "harness: invalidating $DISTRIBUTION_ID"
|
|
387
|
+
INVALIDATION_ID="$(aws cloudfront create-invalidation \
|
|
388
|
+
--distribution-id "$DISTRIBUTION_ID" \
|
|
389
|
+
--paths '/*' \
|
|
390
|
+
--query 'Invalidation.Id' --output text)"
|
|
391
|
+
aws cloudfront wait invalidation-completed \
|
|
392
|
+
--distribution-id "$DISTRIBUTION_ID" \
|
|
393
|
+
--id "$INVALIDATION_ID"
|
|
394
|
+
echo "harness: invalidation $INVALIDATION_ID complete"
|
|
395
|
+
fi
|
|
396
|
+
|
|
397
|
+
echo "harness: deployed $STACK_NAME at $URL"
|
|
398
|
+
printf '%s\n' "$URL" >&3
|