@friggframework/devtools 2.0.0-next.40 → 2.0.0-next.41

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.
@@ -9,6 +9,8 @@ function startCommand(options) {
9
9
  console.log('Starting backend and optional frontend...');
10
10
  // Suppress AWS SDK warning message about maintenance mode
11
11
  process.env.AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE = 1;
12
+ // Skip AWS discovery for local development
13
+ process.env.FRIGG_SKIP_AWS_DISCOVERY = 'true';
12
14
  const backendPath = path.resolve(process.cwd());
13
15
  console.log(`Starting backend in ${backendPath}...`);
14
16
  const infrastructurePath = 'infrastructure.js';
@@ -34,6 +36,10 @@ function startCommand(options) {
34
36
  const childProcess = spawn(command, args, {
35
37
  cwd: backendPath,
36
38
  stdio: 'inherit',
39
+ env: {
40
+ ...process.env,
41
+ FRIGG_SKIP_AWS_DISCOVERY: 'true',
42
+ },
37
43
  });
38
44
 
39
45
  childProcess.on('error', (error) => {
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Test suite for the frigg start command
3
+ *
4
+ * These tests ensure that the start command properly:
5
+ * 1. Sets FRIGG_SKIP_AWS_DISCOVERY=true in the parent process to skip AWS API calls
6
+ * 2. Suppresses AWS SDK maintenance mode warnings
7
+ * 3. Spawns serverless with correct configuration
8
+ *
9
+ * This fixes the issue where frigg start would attempt AWS discovery during local development,
10
+ * causing unnecessary AWS API calls and potential failures when AWS credentials aren't available.
11
+ */
12
+
13
+ const { spawn } = require('node:child_process');
14
+ const { startCommand } = require('./index');
15
+
16
+ // Mock the spawn function
17
+ jest.mock('node:child_process', () => ({
18
+ spawn: jest.fn(),
19
+ }));
20
+
21
+ describe('startCommand', () => {
22
+ let mockChildProcess;
23
+
24
+ beforeEach(() => {
25
+ // Reset mocks
26
+ jest.clearAllMocks();
27
+
28
+ // Clear environment variables
29
+ delete process.env.AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE;
30
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
31
+
32
+ // Create a mock child process
33
+ mockChildProcess = {
34
+ on: jest.fn(),
35
+ stdout: { on: jest.fn() },
36
+ stderr: { on: jest.fn() },
37
+ };
38
+
39
+ spawn.mockReturnValue(mockChildProcess);
40
+ });
41
+
42
+ afterEach(() => {
43
+ // Clean up environment
44
+ delete process.env.AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE;
45
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
46
+ });
47
+
48
+ it('should set FRIGG_SKIP_AWS_DISCOVERY to true in the parent process', () => {
49
+ const options = { stage: 'dev' };
50
+
51
+ startCommand(options);
52
+
53
+ // Verify the environment variable is set in the parent process
54
+ expect(process.env.FRIGG_SKIP_AWS_DISCOVERY).toBe('true');
55
+ });
56
+
57
+ it('should set AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE to suppress warnings', () => {
58
+ const options = { stage: 'dev' };
59
+
60
+ startCommand(options);
61
+
62
+ expect(process.env.AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE).toBe('1');
63
+ });
64
+
65
+ it('should spawn serverless with correct arguments', () => {
66
+ const options = { stage: 'prod' };
67
+
68
+ startCommand(options);
69
+
70
+ expect(spawn).toHaveBeenCalledWith(
71
+ 'serverless',
72
+ ['offline', '--config', 'infrastructure.js', '--stage', 'prod'],
73
+ expect.objectContaining({
74
+ cwd: expect.any(String),
75
+ stdio: 'inherit',
76
+ env: expect.objectContaining({
77
+ FRIGG_SKIP_AWS_DISCOVERY: 'true',
78
+ }),
79
+ })
80
+ );
81
+ });
82
+
83
+ it('should include verbose flag when verbose option is enabled', () => {
84
+ const options = { stage: 'dev', verbose: true };
85
+
86
+ startCommand(options);
87
+
88
+ expect(spawn).toHaveBeenCalledWith(
89
+ 'serverless',
90
+ ['offline', '--config', 'infrastructure.js', '--stage', 'dev', '--verbose'],
91
+ expect.any(Object)
92
+ );
93
+ });
94
+
95
+ it('should pass FRIGG_SKIP_AWS_DISCOVERY in spawn environment', () => {
96
+ const options = { stage: 'dev' };
97
+
98
+ startCommand(options);
99
+
100
+ const spawnCall = spawn.mock.calls[0];
101
+ const spawnOptions = spawnCall[2];
102
+
103
+ expect(spawnOptions.env).toHaveProperty('FRIGG_SKIP_AWS_DISCOVERY', 'true');
104
+ });
105
+
106
+ it('should handle child process errors', () => {
107
+ const options = { stage: 'dev' };
108
+ const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
109
+
110
+ startCommand(options);
111
+
112
+ // Simulate an error
113
+ const errorCallback = mockChildProcess.on.mock.calls.find(call => call[0] === 'error')[1];
114
+ const testError = new Error('Test error');
115
+ errorCallback(testError);
116
+
117
+ expect(consoleErrorSpy).toHaveBeenCalledWith('Error executing command: Test error');
118
+
119
+ consoleErrorSpy.mockRestore();
120
+ });
121
+
122
+ it('should handle child process exit with non-zero code', () => {
123
+ const options = { stage: 'dev' };
124
+ const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
125
+
126
+ startCommand(options);
127
+
128
+ // Simulate exit with error code
129
+ const closeCallback = mockChildProcess.on.mock.calls.find(call => call[0] === 'close')[1];
130
+ closeCallback(1);
131
+
132
+ expect(consoleLogSpy).toHaveBeenCalledWith('Child process exited with code 1');
133
+
134
+ consoleLogSpy.mockRestore();
135
+ });
136
+
137
+ it('should not log on successful exit', () => {
138
+ const options = { stage: 'dev' };
139
+ const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
140
+
141
+ startCommand(options);
142
+
143
+ // Clear the spy calls from startCommand execution
144
+ consoleLogSpy.mockClear();
145
+
146
+ // Simulate successful exit
147
+ const closeCallback = mockChildProcess.on.mock.calls.find(call => call[0] === 'close')[1];
148
+ closeCallback(0);
149
+
150
+ // Should not log anything for successful exit
151
+ expect(consoleLogSpy).not.toHaveBeenCalledWith(expect.stringContaining('exited'));
152
+
153
+ consoleLogSpy.mockRestore();
154
+ });
155
+ });
@@ -2,10 +2,19 @@ const path = require('path');
2
2
  const fs = require('fs');
3
3
  const { AWSDiscovery } = require('./aws-discovery');
4
4
 
5
- const shouldRunDiscovery = (AppDefinition) =>
6
- AppDefinition.vpc?.enable === true ||
7
- AppDefinition.encryption?.fieldLevelEncryptionMethod === 'kms' ||
8
- AppDefinition.ssm?.enable === true;
5
+ const shouldRunDiscovery = (AppDefinition) => {
6
+ console.log('⚙️ Checking FRIGG_SKIP_AWS_DISCOVERY:', process.env.FRIGG_SKIP_AWS_DISCOVERY);
7
+ if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
8
+ console.log('⚙️ Skipping AWS discovery because FRIGG_SKIP_AWS_DISCOVERY is set.');
9
+ return false;
10
+ }
11
+
12
+ return (
13
+ AppDefinition.vpc?.enable === true ||
14
+ AppDefinition.encryption?.fieldLevelEncryptionMethod === 'kms' ||
15
+ AppDefinition.ssm?.enable === true
16
+ );
17
+ };
9
18
 
10
19
  const getAppEnvironmentVars = (AppDefinition) => {
11
20
  const envVars = {};
@@ -649,6 +658,12 @@ const applyKmsConfiguration = (definition, AppDefinition, discoveredResources) =
649
658
  return;
650
659
  }
651
660
 
661
+ // Skip KMS configuration for local development when AWS discovery is disabled
662
+ if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
663
+ console.log('⚙️ Skipping KMS configuration for local development (FRIGG_SKIP_AWS_DISCOVERY is set)');
664
+ return;
665
+ }
666
+
652
667
  if (discoveredResources.defaultKmsKeyId) {
653
668
  console.log(`Using existing KMS key: ${discoveredResources.defaultKmsKeyId}`);
654
669
  definition.resources.Resources.FriggKMSKeyAlias = {
@@ -846,6 +861,12 @@ const configureVpc = (definition, AppDefinition, discoveredResources) => {
846
861
  return;
847
862
  }
848
863
 
864
+ // Skip VPC configuration for local development when AWS discovery is disabled
865
+ if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
866
+ console.log('⚙️ Skipping VPC configuration for local development (FRIGG_SKIP_AWS_DISCOVERY is set)');
867
+ return;
868
+ }
869
+
849
870
  definition.provider.iamRoleStatements.push({
850
871
  Effect: 'Allow',
851
872
  Action: [
@@ -55,6 +55,7 @@ describe('composeServerlessDefinition', () => {
55
55
  jest.restoreAllMocks();
56
56
  // Restore env
57
57
  delete process.env.AWS_REGION;
58
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
58
59
  process.argv = ['node', 'test'];
59
60
  });
60
61
 
@@ -86,6 +87,50 @@ describe('composeServerlessDefinition', () => {
86
87
 
87
88
  expect(AWSDiscovery).toHaveBeenCalledTimes(1);
88
89
  });
90
+
91
+ it('should skip AWS discovery when FRIGG_SKIP_AWS_DISCOVERY is set to true', async () => {
92
+ AWSDiscovery.mockClear();
93
+ process.env.FRIGG_SKIP_AWS_DISCOVERY = 'true';
94
+
95
+ const appDefinition = {
96
+ integrations: [],
97
+ vpc: { enable: true },
98
+ encryption: { fieldLevelEncryptionMethod: 'kms' },
99
+ ssm: { enable: true },
100
+ };
101
+
102
+ await composeServerlessDefinition(appDefinition);
103
+
104
+ expect(AWSDiscovery).not.toHaveBeenCalled();
105
+ });
106
+
107
+ it('should run AWS discovery when FRIGG_SKIP_AWS_DISCOVERY is not set', async () => {
108
+ AWSDiscovery.mockClear();
109
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
110
+
111
+ const appDefinition = {
112
+ integrations: [],
113
+ vpc: { enable: true },
114
+ };
115
+
116
+ await composeServerlessDefinition(appDefinition);
117
+
118
+ expect(AWSDiscovery).toHaveBeenCalledTimes(1);
119
+ });
120
+
121
+ it('should skip VPC configuration when FRIGG_SKIP_AWS_DISCOVERY is true', async () => {
122
+ process.env.FRIGG_SKIP_AWS_DISCOVERY = 'true';
123
+
124
+ const appDefinition = {
125
+ integrations: [],
126
+ vpc: { enable: true, management: 'discover' },
127
+ };
128
+
129
+ const result = await composeServerlessDefinition(appDefinition);
130
+
131
+ // VPC configuration should not be present when skipped
132
+ expect(result.provider.vpc).toBeUndefined();
133
+ });
89
134
  });
90
135
 
91
136
  describe('Basic Configuration', () => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0-next.40",
4
+ "version": "2.0.0-next.41",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-ec2": "^3.835.0",
7
7
  "@aws-sdk/client-kms": "^3.835.0",
@@ -9,8 +9,8 @@
9
9
  "@babel/eslint-parser": "^7.18.9",
10
10
  "@babel/parser": "^7.25.3",
11
11
  "@babel/traverse": "^7.25.3",
12
- "@friggframework/schemas": "2.0.0-next.40",
13
- "@friggframework/test": "2.0.0-next.40",
12
+ "@friggframework/schemas": "2.0.0-next.41",
13
+ "@friggframework/test": "2.0.0-next.41",
14
14
  "@hapi/boom": "^10.0.1",
15
15
  "@inquirer/prompts": "^5.3.8",
16
16
  "axios": "^1.7.2",
@@ -32,8 +32,8 @@
32
32
  "serverless-http": "^2.7.0"
33
33
  },
34
34
  "devDependencies": {
35
- "@friggframework/eslint-config": "2.0.0-next.40",
36
- "@friggframework/prettier-config": "2.0.0-next.40",
35
+ "@friggframework/eslint-config": "2.0.0-next.41",
36
+ "@friggframework/prettier-config": "2.0.0-next.41",
37
37
  "aws-sdk-client-mock": "^4.1.0",
38
38
  "aws-sdk-client-mock-jest": "^4.1.0",
39
39
  "jest": "^30.1.3",
@@ -68,5 +68,5 @@
68
68
  "publishConfig": {
69
69
  "access": "public"
70
70
  },
71
- "gitHead": "088c50c6e1e37a6d42be05af49349b70ae94ee31"
71
+ "gitHead": "a5c413164c1b492a228689851d037706f7f869af"
72
72
  }