Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
02840e2
Support for LocalStack
ServerlessLife Nov 3, 2025
29fa2c8
chore: Update LocalStack detection to use IAMClient and ListAccountAl…
ServerlessLife Nov 3, 2025
9f1f03c
fix: Add CDK bootstrap step for LocalStack in CI workflow
ServerlessLife Nov 3, 2025
77db711
fix: Update CDK bootstrap region for LocalStack in CI workflow
ServerlessLife Nov 4, 2025
6d78b53
fix: Update AWS region from us-east-1 to eu-west-1 in CI workflow
ServerlessLife Nov 4, 2025
d532c7d
fix: Add environment parameter to CDK bootstrap command for LocalStack
ServerlessLife Nov 4, 2025
0cb808c
fix: Install cdklocal for LocalStack and update CDK bootstrap commands
ServerlessLife Nov 4, 2025
bef4844
fix: Update installation command for cdklocal to aws-cdk-local in CI …
ServerlessLife Nov 4, 2025
b3c2fb1
fix: Refactor LocalStack AWS credentials configuration to use environ…
ServerlessLife Nov 4, 2025
0be6a4d
fix: Install aws-cdk alongside aws-cdk-local for LocalStack
ServerlessLife Nov 4, 2025
8b4bf74
fix: Enhance LocalStack region detection and logging in IoT service c…
ServerlessLife Nov 4, 2025
c182bbc
fix: Set AWS_SDK_LOAD_CONFIG environment variable for LocalStack in t…
ServerlessLife Nov 4, 2025
06a1d43
fix: Add AWS_ENDPOINT_URL_S3 environment variable for LocalStack in C…
ServerlessLife Nov 4, 2025
fd37613
fix: Update deployment command to use npx serverless with specified r…
ServerlessLife Nov 4, 2025
7258a44
fix: Update concurrency group names to include useLocalStack variable
ServerlessLife Nov 4, 2025
3968c3f
fix: Set CDK_DEFAULT_REGION environment variable for LocalStack boots…
ServerlessLife Nov 4, 2025
de0736b
feat: Enhance LocalStack support in CI workflows and configurations
ServerlessLife Nov 5, 2025
7e12e91
Disable remove-old-layers job in pull-request workflow and update dep…
ServerlessLife Nov 5, 2025
0b839f8
feat: Add LocalStack support and specify region for deployments acros…
ServerlessLife Nov 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,199 changes: 790 additions & 409 deletions .github/workflows/common-test.yml

Large diffs are not rendered by default.

74 changes: 42 additions & 32 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,51 @@ jobs:
uses: ./.github/workflows/common-build.yml
secrets: inherit

remove-old-layers:
runs-on: ubuntu-latest
needs: build
concurrency:
group: remove-all-layers
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.node_version }}
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: dist
path: dist
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: eu-west-1
role-to-assume: ${{ secrets.AWS_ROLE }}
role-session-name: GitHubActions
- name: Remove old layers
run: |
node ../../dist/lldebugger.mjs -r all --config-env=test -v
# Picking random test so I can have environment
working-directory: test/sam-basic
# remove-old-layers:
# runs-on: ubuntu-latest
# needs: build
# concurrency:
# group: remove-all-layers
# steps:
# - uses: actions/checkout@v4
# - name: Use Node.js
# uses: actions/setup-node@v4
# with:
# node-version: ${{ env.node_version }}
# registry-url: 'https://registry.npmjs.org'
# - name: Install dependencies
# run: npm ci
# - name: Download build artifact
# uses: actions/download-artifact@v4
# with:
# name: dist
# path: dist
# - name: Configure AWS Credentials
# uses: aws-actions/configure-aws-credentials@v4
# with:
# aws-region: eu-west-1
# role-to-assume: ${{ secrets.AWS_ROLE }}
# role-session-name: GitHubActions
# - name: Remove old layers
# run: |
# node ../../dist/lldebugger.mjs -r all --config-env=test -v
# # Picking random test so I can have environment
# working-directory: test/sam-basic

# test:
# uses: ./.github/workflows/common-test.yml
# secrets: inherit
# needs: remove-old-layers
# with:
# mode: build
# testMonorepo: false
# useLocalStack: false

test:
test-localstack:
uses: ./.github/workflows/common-test.yml
secrets: inherit
needs: remove-old-layers
needs: build
with:
mode: build
testMonorepo: false
useLocalStack: true
43 changes: 43 additions & 0 deletions .github/workflows/test-with-real-npm-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ jobs:
mode: global
testMonorepo: false
version: ${{ inputs.version }}
useLocalStack: false
secrets: inherit

test-global-not-monorepo-localstack:
uses: ./.github/workflows/common-test.yml
with:
mode: global
testMonorepo: false
version: ${{ inputs.version }}
useLocalStack: true
secrets: inherit

test-local-not-monorepo:
Expand All @@ -28,6 +38,17 @@ jobs:
mode: local
testMonorepo: false
version: ${{ inputs.version }}
useLocalStack: false
secrets: inherit

test-local-not-monorepo-localstack:
needs: test-global-not-monorepo
uses: ./.github/workflows/common-test.yml
with:
mode: local
testMonorepo: false
version: ${{ inputs.version }}
useLocalStack: true
secrets: inherit

test-global-monorepo:
Expand All @@ -37,6 +58,17 @@ jobs:
mode: global
testMonorepo: true
version: ${{ inputs.version }}
useLocalStack: false
secrets: inherit

test-global-monorepo-localstack:
needs: test-local-not-monorepo
uses: ./.github/workflows/common-test.yml
with:
mode: global
testMonorepo: true
version: ${{ inputs.version }}
useLocalStack: true
secrets: inherit

test-local-monorepo:
Expand All @@ -46,4 +78,15 @@ jobs:
mode: local
testMonorepo: true
version: ${{ inputs.version }}
useLocalStack: false
secrets: inherit

test-local-monorepo-localstack:
needs: test-global-monorepo
uses: ./.github/workflows/common-test.yml
with:
mode: local
testMonorepo: true
version: ${{ inputs.version }}
useLocalStack: true
secrets: inherit
5 changes: 5 additions & 0 deletions .github/workflows/test-with-real-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ on:
description: 'Test monorepo by specifying folder in config'
type: boolean
default: false
useLocalStack:
description: 'Use LocalStack instead of AWS'
type: boolean
default: false
version:
description: 'Specify the version of the package'
type: string
Expand All @@ -32,4 +36,5 @@ jobs:
mode: ${{ inputs.mode }}
testMonorepo: ${{ inputs.testMonorepo }}
version: ${{ inputs.version }}
useLocalStack: ${{ inputs.useLocalStack }}
secrets: inherit
22 changes: 21 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions src/awsCredentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,50 @@ function getCredentialsProvider(
});
}

/**
* Detects if LocalStack is being used by making a test request and checking for
* the 'x-localstack' header in the response.
* @see {@link https://github.com/localstack/localstack/pull/12769}
* @param awsConfiguration - AWS configuration to use for the test request
* @returns true if LocalStack is detected (x-localstack header present), false otherwise
*/
async function isLocalStackDetected(
awsConfiguration: AwsConfiguration,
): Promise<boolean> {
// Enable LocalStack response header to detect LocalStack
process.env.LOCALSTACK_RESPONSE_HEADER_ENABLED = 'true';

const { IAMClient, ListAccountAliasesCommand } = await import(
'@aws-sdk/client-iam'
);

const client = new IAMClient({
region: awsConfiguration.region,
credentials: fromNodeProviderChain({
clientConfig: { region: awsConfiguration.region },
profile: awsConfiguration.profile,
roleArn: awsConfiguration.role,
}),
});

const command = new ListAccountAliasesCommand({});
const response = await client.send(command);

// Check for x-localstack header in response metadata
const headers = (response.$metadata as any)?.httpHeaders;
if (
headers &&
('x-localstack' in headers ||
'X-Localstack' in headers ||
'X-LOCALSTACK' in headers)
) {
return true;
}

return false;
}

export const AwsCredentials = {
getCredentialsProvider,
isLocalStackDetected,
};
13 changes: 13 additions & 0 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getConfigTsFromConfigFile } from './configuration/getConfigFromTsConfig
import { configFileDefaultName } from './constants.js';
import { ResourceDiscovery } from './resourceDiscovery.js';
import { Logger } from './logger.js';
import { AwsCredentials } from './awsCredentials.js';

let config: LldConfig;
const lambdas: Record<string, LambdaResource> = {};
Expand Down Expand Up @@ -41,9 +42,15 @@ async function readConfig() {
});

const debuggerId = await generateDebuggerId(!!configFromWizard.observable);
const localStack = await AwsCredentials.isLocalStackDetected({
profile: configFromWizard.profile,
region: configFromWizard.region,
role: configFromWizard.role,
});
setConfig({
...configFromWizard,
debuggerId,
localStack,
start: false, // don't start the debugger after the wizard
});
} else {
Expand All @@ -63,9 +70,15 @@ async function readConfig() {
: configFromConfigFile?.context,
};
const debuggerId = await generateDebuggerId(!!configMerged.observable);
const localStack = await AwsCredentials.isLocalStackDetected({
profile: configMerged.profile,
region: configMerged.region,
role: configMerged.role,
});
setConfig({
...configMerged,
debuggerId,
localStack,
start: true,
});
}
Expand Down
8 changes: 8 additions & 0 deletions src/extension/interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ async function regularMode(context: any, event: any) {
});
}, 5 * 1000);

let region: string | undefined;
if (process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0') {
// it is localstack
region = process.env.AWS_REGION;
Logger.verbose(`Region is set to ${region} due to LocalStack detection`);
}

const ioTService = await IoTService.connect({
onMessage: async (message: IoTMessage) => {
if (Logger.isVerbose()) {
Expand Down Expand Up @@ -83,6 +90,7 @@ async function regularMode(context: any, event: any) {
}
},
topic,
region,
});

const payload: IoTMessage = {
Expand Down
5 changes: 5 additions & 0 deletions src/infraDeploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,11 @@ function getEnvironmentVariablesForDebugger({
env.LLD_VERBOSE = 'true';
}

// Disable TLS verification for LocalStack
if (Configuration.config.localStack) {
env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
}

return env;
}

Expand Down
9 changes: 9 additions & 0 deletions src/lambdaConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ const lambdasProcessingObservableMode = new Set<string>();
*/
async function connect() {
topic = `${Configuration.config.debuggerId}/events`;

let region = Configuration.config.region;
if (!region && Configuration.config.localStack) {
region = process.env.AWS_REGION || 'us-east-1';
Logger.verbose(
`Empty region detected with LocalStack, defaulting to ${region}`,
);
}

ioTServiceConnection = await IoTService.connect({
onMessage: onMessageFromLambda,
topic,
Expand Down
5 changes: 5 additions & 0 deletions src/lldebugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ async function run() {
return;
}

if (Configuration.config.localStack) {
//https://github.com/localstack/localstack/issues/13308
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
}

let message = `Starting the debugger ${
Configuration.config.observable
? 'in Observability mode'
Expand Down
9 changes: 8 additions & 1 deletion src/types/lldConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,11 @@ export type LldConfigCliArgs = {

export type LldConfigTs = Partial<LldConfigBase>;

export type LldConfig = LldConfigCliArgs & LldConfigTs & { debuggerId: string };
export type LldConfig = LldConfigCliArgs &
LldConfigTs & {
debuggerId: string;
/**
* Indicates whether LocalStack is being used instead of real AWS services.
*/
localStack: boolean;
};
8 changes: 4 additions & 4 deletions test/cdk-basic/bin/cdk-basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import * as cdk from 'aws-cdk-lib';
import { CdkbasicStack } from '../lib/cdk-basic-stack';
import { CdkbasicStack2 } from '../lib/subfolder/cdk-basic-stack2';

if (process.env.CDK_DEFAULT_REGION !== 'eu-west-1') {
// checking if the region is set with Lambda Live Debugger
throw new Error('CDK_DEFAULT_REGION must be set to eu-west-1');
}
// if (process.env.CDK_DEFAULT_REGION !== 'eu-west-1') {
// // checking if the region is set with Lambda Live Debugger
// throw new Error('CDK_DEFAULT_REGION must be set to eu-west-1');
// }

const app = new cdk.App();

Expand Down
Loading
Loading