diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index f727260..ad60744 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -35,15 +35,10 @@ jobs: ruby-version: '3.3' working-directory: demo/lambdas/ruby - - name: Setup LocalStack - uses: localstack/setup-localstack@main - with: - image-tag: 'latest' - use-pro: 'true' - configuration: LS_LOG=trace - install-awslocal: 'true' - env: - LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }} + - name: Install lstk + run: | + npm install -g @localstack/lstk aws-cdk + lstk setup aws - name: Install CDK dependencies run: cd cdk && npm ci @@ -57,13 +52,20 @@ jobs: - name: Install Node.js Lambda dependencies run: cd demo/lambdas/nodejs && npm install --omit=dev + - name: Start LocalStack + env: + LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }} + run: | + docker pull localstack/localstack-pro:latest + LOCALSTACK_LS_LOG=trace lstk start + - name: Bootstrap CDK working-directory: cdk env: AWS_ACCESS_KEY_ID: test AWS_SECRET_ACCESS_KEY: test AWS_DEFAULT_REGION: us-east-1 - run: npx cdklocal bootstrap + run: lstk cdk bootstrap - name: Deploy CDK stack working-directory: cdk @@ -71,7 +73,7 @@ jobs: AWS_ACCESS_KEY_ID: test AWS_SECRET_ACCESS_KEY: test AWS_DEFAULT_REGION: us-east-1 - run: npx cdklocal deploy LocalstackDemoStack --require-approval never + run: lstk cdk deploy LocalstackDemoStack --require-approval never - name: Upload frontend to S3 env: @@ -79,7 +81,7 @@ jobs: AWS_SECRET_ACCESS_KEY: test AWS_DEFAULT_REGION: us-east-1 AWS_ENDPOINT_URL: http://localhost.localstack.cloud:4566 - run: awslocal s3 sync demo/web/ s3://archive-bucket/ + run: lstk aws s3 sync demo/web/ s3://archive-bucket/ - name: Run end-to-end test env: @@ -89,7 +91,7 @@ jobs: AWS_ENDPOINT_URL: http://localhost.localstack.cloud:4566 run: | echo "Discovering REST API ID..." - apiId=$(awslocal apigateway get-rest-apis --output json \ + apiId=$(lstk aws apigateway get-rest-apis --output json \ | jq -r '.items[] | select(.name=="localstack-demo") | .id') echo "API ID: $apiId" @@ -110,10 +112,10 @@ jobs: done echo "Verifying S3 result file..." - awslocal s3 cp "s3://archive-bucket/$reqID/result.txt" - | grep -q "Archive result" \ + lstk aws s3 cp "s3://archive-bucket/$reqID/result.txt" - | grep -q "Archive result" \ && echo "✅ End-to-end test passed!" \ || (echo "❌ S3 result not found" && exit 1) - name: LocalStack logs (on failure) if: failure() - run: localstack logs + run: lstk logs diff --git a/Makefile b/Makefile index f2fedd5..d370ed8 100644 --- a/Makefile +++ b/Makefile @@ -18,8 +18,8 @@ check: ## Check if all required prerequisites are installed @command -v docker > /dev/null 2>&1 || { echo "Docker is not installed. Please install Docker and try again."; exit 1; } @command -v node > /dev/null 2>&1 || { echo "Node.js is not installed. Please install Node.js and try again."; exit 1; } @command -v aws > /dev/null 2>&1 || { echo "AWS CLI is not installed. Please install AWS CLI and try again."; exit 1; } - @command -v awslocal > /dev/null 2>&1 || { echo "awslocal is not installed. Run: pip install awscli-local"; exit 1; } - @command -v localstack > /dev/null 2>&1 || { echo "LocalStack CLI is not installed. Run: pip install localstack"; exit 1; } + @command -v lstk > /dev/null 2>&1 || { echo "lstk is not installed. Run: npm install -g @localstack/lstk"; exit 1; } + @command -v cdk > /dev/null 2>&1 || { echo "AWS CDK CLI is not installed. Run: npm install -g aws-cdk"; exit 1; } @command -v jq > /dev/null 2>&1 || { echo "jq is not installed. See https://jqlang.github.io/jq/download/"; exit 1; } @test -n "$(LOCALSTACK_AUTH_TOKEN)" || { echo "LOCALSTACK_AUTH_TOKEN is not set. Find your token at https://app.localstack.cloud/workspace/auth-token"; exit 1; } @echo "All required prerequisites are available." @@ -46,51 +46,47 @@ build: ## Build the CDK TypeScript app $(CDIR) && npm run build bootstrap: ## Bootstrap CDK for LocalStack - $(CDIR) && npm run build && npx cdklocal bootstrap + $(CDIR) && npm run build && lstk cdk bootstrap deploy: ## Build, deploy CDK stack to LocalStack, and upload frontend $(MAKE) bundle-ruby bundle-node $(MAKE) bootstrap - $(CDIR) && npx cdklocal deploy LocalstackDemoStack --require-approval never + $(CDIR) && lstk cdk deploy LocalstackDemoStack --require-approval never @echo "Uploading frontend to s3://archive-bucket/ ..." - awslocal s3 sync demo/web/ s3://archive-bucket/ + lstk aws s3 sync demo/web/ s3://archive-bucket/ @echo "" @echo "Done! Open http://localhost:4566/archive-bucket/index.html in your browser." destroy: ## Destroy the deployed CDK stack on LocalStack - $(CDIR) && npx cdklocal destroy LocalstackDemoStack + $(CDIR) && lstk cdk destroy LocalstackDemoStack send-request: ## Send a test request and poll S3 for the result @command -v jq > /dev/null 2>&1 || { echo "jq is not installed. See https://jqlang.github.io/jq/download/"; exit 1; } @echo "Looking up REST API ID..." - @apiId=$$(awslocal apigateway get-rest-apis --output json | jq -r '.items[] | select(.name=="localstack-demo") | .id'); \ + @apiId=$$(lstk aws apigateway get-rest-apis --output json | jq -r '.items[] | select(.name=="localstack-demo") | .id'); \ echo "Sending request to API ID '$$apiId' ..."; \ reqID=$$(curl -s -d '{}' "http://localhost:4566/_aws/execute-api/$$apiId/local/requests" | jq -r .requestID); \ echo "Received request ID '$$reqID'"; \ for i in 1 2 3 4 5 6 7 8 9 10; do \ echo "Polling s3://archive-bucket/ for result ..."; \ - awslocal s3 ls s3://archive-bucket/ | grep "$$reqID" && exit 0; \ + lstk aws s3 ls s3://archive-bucket/ | grep "$$reqID" && exit 0; \ sleep 3; \ done; \ echo "Timed out waiting for result." -start: ## Start LocalStack in detached mode +start: ## Start LocalStack @echo "Starting LocalStack..." @test -n "$(LOCALSTACK_AUTH_TOKEN)" || { echo "LOCALSTACK_AUTH_TOKEN is not set. Find your token at https://app.localstack.cloud/workspace/auth-token"; exit 1; } - @LOCALSTACK_AUTH_TOKEN=$(LOCALSTACK_AUTH_TOKEN) localstack start -d + @unset AWS_ENDPOINT_URL; LOCALSTACK_AUTH_TOKEN=$(LOCALSTACK_AUTH_TOKEN) lstk start @echo "LocalStack started successfully." stop: ## Stop LocalStack @echo "Stopping LocalStack..." - @localstack stop + @unset AWS_ENDPOINT_URL; lstk stop @echo "LocalStack stopped successfully." -ready: ## Wait until the LocalStack container is ready - @echo "Waiting on the LocalStack container..." - @localstack wait -t 30 && echo "LocalStack is ready to use!" || { echo "Gave up waiting on LocalStack, exiting."; exit 1; } - logs: ## Save LocalStack logs to logs.txt - @localstack logs > logs.txt + @unset AWS_ENDPOINT_URL; lstk logs > logs.txt PKG_SUB_DIRS := $(dir $(shell find . -type d -name node_modules -prune -o -type d -name "vendor" -prune -o -type f -name package.json -print)) @@ -100,4 +96,4 @@ update-deps: ## Update npm dependencies in all sub-projects done .PHONY: usage check install bundle-ruby bundle-node build bootstrap deploy deploy-aws \ - destroy destroy-aws send-request start stop ready logs update-deps + destroy destroy-aws send-request start stop logs update-deps diff --git a/README.md b/README.md index 535c784..27c0433 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,10 @@ The following diagram shows the architecture that this sample application builds ## Prerequisites -- A valid [LocalStack for AWS license](https://localstack.cloud/pricing). Your license provides a [`LOCALSTACK_AUTH_TOKEN`](https://docs.localstack.cloud/getting-started/auth-token/) to activate LocalStack. -- [`localstack` CLI](https://docs.localstack.cloud/getting-started/installation/#localstack-cli). -- [AWS CLI](https://docs.localstack.cloud/user-guide/integrations/aws-cli/) with the [`awslocal` wrapper](https://docs.localstack.cloud/user-guide/integrations/aws-cli/#localstack-aws-cli-awslocal). -- [CDK](https://docs.localstack.cloud/user-guide/integrations/aws-cdk/) with the [`cdklocal`](https://www.npmjs.com/package/aws-cdk-local) wrapper (installed automatically via `cdk/package.json`). +- A valid [LocalStack for AWS license](https://localstack.cloud/pricing). Your license provides a [`LOCALSTACK_AUTH_TOKEN`](https://docs.localstack.cloud/aws/getting-started/auth-token/) to activate LocalStack. +- [`lstk` CLI](https://docs.localstack.cloud/aws/developer-tools/running-localstack/lstk/), installed via `npm install -g @localstack/lstk` or `brew install localstack/tap/lstk`. +- [AWS CLI](https://docs.localstack.cloud/user-guide/integrations/aws-cli/), required by `lstk aws`. +- [CDK](https://docs.localstack.cloud/user-guide/integrations/aws-cdk/) installed globally (`npm install -g aws-cdk`), used via the `lstk cdk` proxy. - [Node.js 22+](https://nodejs.org/en/download/) - [Docker](https://docs.docker.com/get-docker/) — required to bundle Ruby Lambda gems. - [`jq`](https://jqlang.github.io/jq/download/) @@ -65,7 +65,6 @@ Set your LocalStack auth token and start LocalStack: ```bash export LOCALSTACK_AUTH_TOKEN= make start -make ready ``` Deploy the full stack (bundles Lambda dependencies, bootstraps CDK, deploys, uploads frontend): @@ -126,7 +125,7 @@ Polling s3://archive-bucket/ for result ... You can also browse the contents of the archive bucket directly: ```bash -awslocal s3 ls s3://archive-bucket/ +lstk aws s3 ls s3://archive-bucket/ ``` ## Summary @@ -136,7 +135,7 @@ This sample application demonstrates how to build and test a polyglot serverless - Defining AWS infrastructure (API Gateway, Lambda, SQS, Step Functions, DynamoDB, S3) entirely with **AWS CDK in TypeScript**. - Running **three Lambda runtimes** (Node.js, Python, Ruby) side-by-side in the same CDK stack. - Serving a **React frontend from S3** that auto-discovers the API Gateway endpoint and polls for status updates. -- Using `cdklocal` and `awslocal` to streamline **local deployment and testing** without touching real AWS. +- Using the `lstk cdk` and `lstk aws` proxies to streamline **local deployment and testing** without touching real AWS. - Providing a **GitHub Actions workflow** that runs the full integration test suite on every push. ## Learn More diff --git a/cdk/package-lock.json b/cdk/package-lock.json index 14deee4..4288e8c 100644 --- a/cdk/package-lock.json +++ b/cdk/package-lock.json @@ -15,7 +15,6 @@ "devDependencies": { "@types/node": "^22.13.0", "aws-cdk": "^2.1111.0", - "aws-cdk-local": "^2.0.0", "ts-node": "^10.9.2", "typescript": "^5.7.0" } @@ -569,27 +568,6 @@ "node": ">= 6" } }, - "node_modules/aws-cdk-local": { - "version": "2.19.2", - "resolved": "https://registry.npmjs.org/aws-cdk-local/-/aws-cdk-local-2.19.2.tgz", - "integrity": "sha512-1aT+yD4bN23Zgqjyd7AbLOFTK04xM8L7GBWPdAridqG20cyptnDCmRY7iYwWPx19LnJAA0HaiTxoWQTxDwuFLA==", - "dev": true, - "dependencies": { - "diff": "^5.0.0" - }, - "bin": { - "cdklocal": "bin/cdklocal" - } - }, - "node_modules/aws-cdk-local/node_modules/diff": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", - "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", diff --git a/cdk/package.json b/cdk/package.json index 4844f51..6a72be9 100644 --- a/cdk/package.json +++ b/cdk/package.json @@ -14,7 +14,6 @@ "devDependencies": { "@types/node": "^22.13.0", "aws-cdk": "^2.1111.0", - "aws-cdk-local": "^2.0.0", "ts-node": "^10.9.2", "typescript": "^5.7.0" }