@friggframework/devtools 2.0.0-next.35 → 2.0.0-next.37

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.
@@ -1,26 +1,155 @@
1
- const { spawn, spawnSync } = require('child_process');
1
+ const { spawn } = require('child_process');
2
2
  const path = require('path');
3
+ const fs = require('fs');
3
4
 
4
- async function deployCommand(options) {
5
- console.log('Deploying the serverless application...');
5
+ // Configuration constants
6
+ const PATHS = {
7
+ APP_DEFINITION: 'index.js',
8
+ INFRASTRUCTURE: 'infrastructure.js'
9
+ };
10
+
11
+ const COMMANDS = {
12
+ SERVERLESS: 'serverless'
13
+ };
14
+
15
+ /**
16
+ * Constructs filtered environment variables for serverless deployment
17
+ * @param {string[]} appDefinedVariables - Array of environment variable names from app definition
18
+ * @returns {Object} Filtered environment variables object
19
+ */
20
+ function buildFilteredEnvironment(appDefinedVariables) {
21
+ return {
22
+ // Essential system variables needed to run serverless
23
+ PATH: process.env.PATH,
24
+ HOME: process.env.HOME,
25
+ USER: process.env.USER,
26
+
27
+ // AWS credentials and configuration (all AWS_ prefixed variables)
28
+ ...Object.fromEntries(
29
+ Object.entries(process.env).filter(([key]) =>
30
+ key.startsWith('AWS_')
31
+ )
32
+ ),
33
+
34
+ // App-defined environment variables
35
+ ...Object.fromEntries(
36
+ appDefinedVariables
37
+ .map((key) => [key, process.env[key]])
38
+ .filter(([_, value]) => value !== undefined)
39
+ ),
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Loads and parses the app definition from index.js
45
+ * @returns {Object|null} App definition object or null if not found
46
+ */
47
+ function loadAppDefinition() {
48
+ const appDefPath = path.join(process.cwd(), PATHS.APP_DEFINITION);
49
+
50
+ if (!fs.existsSync(appDefPath)) {
51
+ return null;
52
+ }
53
+
54
+ try {
55
+ const { Definition } = require(appDefPath);
56
+ return Definition;
57
+ } catch (error) {
58
+ console.warn('Could not load appDefinition environment config:', error.message);
59
+ return null;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Extracts environment variable names from app definition
65
+ * @param {Object} appDefinition - App definition object
66
+ * @returns {string[]} Array of environment variable names
67
+ */
68
+ function extractEnvironmentVariables(appDefinition) {
69
+ if (!appDefinition?.environment) {
70
+ return [];
71
+ }
72
+
73
+ console.log('🔧 Loading environment configuration from appDefinition...');
74
+
75
+ const appDefinedVariables = Object.keys(appDefinition.environment).filter(
76
+ (key) => appDefinition.environment[key] === true
77
+ );
78
+
79
+ console.log(` Found ${appDefinedVariables.length} environment variables: ${appDefinedVariables.join(', ')}`);
80
+ return appDefinedVariables;
81
+ }
82
+
83
+ /**
84
+ * Handles environment validation warnings
85
+ * @param {Object} validation - Validation result object
86
+ * @param {Object} options - Deploy command options
87
+ */
88
+ function handleValidationWarnings(validation, options) {
89
+ if (validation.missing.length === 0 || options.skipEnvValidation) {
90
+ return;
91
+ }
6
92
 
7
- // AWS discovery is now handled directly in serverless-template.js
8
- console.log('🤘🏼 Deploying serverless application...');
9
- const backendPath = path.resolve(process.cwd());
10
- const infrastructurePath = 'infrastructure.js';
11
- const command = 'serverless';
93
+ console.warn(`⚠️ Warning: Missing ${validation.missing.length} environment variables: ${validation.missing.join(', ')}`);
94
+ console.warn(' These variables are optional and deployment will continue');
95
+ console.warn(' Run with --skip-env-validation to bypass this check');
96
+ }
97
+
98
+ /**
99
+ * Validates environment variables and builds filtered environment
100
+ * @param {Object} appDefinition - App definition object
101
+ * @param {Object} options - Deploy command options
102
+ * @returns {Object} Filtered environment variables
103
+ */
104
+ function validateAndBuildEnvironment(appDefinition, options) {
105
+ if (!appDefinition) {
106
+ return buildFilteredEnvironment([]);
107
+ }
108
+
109
+ const appDefinedVariables = extractEnvironmentVariables(appDefinition);
110
+
111
+ // Try to use the env-validator if available
112
+ try {
113
+ const { validateEnvironmentVariables } = require('@friggframework/devtools/infrastructure/env-validator');
114
+ const validation = validateEnvironmentVariables(appDefinition);
115
+
116
+ handleValidationWarnings(validation, options);
117
+ return buildFilteredEnvironment(appDefinedVariables);
118
+
119
+ } catch (validatorError) {
120
+ // Validator not available, do basic validation
121
+ const missingVariables = appDefinedVariables.filter((variable) => !process.env[variable]);
122
+
123
+ if (missingVariables.length > 0) {
124
+ console.warn(`⚠️ Warning: Missing ${missingVariables.length} environment variables: ${missingVariables.join(', ')}`);
125
+ console.warn(' These variables are optional and deployment will continue');
126
+ console.warn(' Set them in your CI/CD environment or .env file if needed');
127
+ }
128
+
129
+ return buildFilteredEnvironment(appDefinedVariables);
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Executes the serverless deployment command
135
+ * @param {Object} environment - Environment variables to pass to serverless
136
+ * @param {Object} options - Deploy command options
137
+ */
138
+ function executeServerlessDeployment(environment, options) {
139
+ console.log('🚀 Deploying serverless application...');
140
+
12
141
  const serverlessArgs = [
13
142
  'deploy',
14
143
  '--config',
15
- infrastructurePath,
144
+ PATHS.INFRASTRUCTURE,
16
145
  '--stage',
17
146
  options.stage,
18
147
  ];
19
148
 
20
- const childProcess = spawn(command, serverlessArgs, {
21
- cwd: backendPath,
149
+ const childProcess = spawn(COMMANDS.SERVERLESS, serverlessArgs, {
150
+ cwd: path.resolve(process.cwd()),
22
151
  stdio: 'inherit',
23
- env: { ...process.env },
152
+ env: environment,
24
153
  });
25
154
 
26
155
  childProcess.on('error', (error) => {
@@ -34,4 +163,13 @@ async function deployCommand(options) {
34
163
  });
35
164
  }
36
165
 
166
+ async function deployCommand(options) {
167
+ console.log('Deploying the serverless application...');
168
+
169
+ const appDefinition = loadAppDefinition();
170
+ const environment = validateAndBuildEnvironment(appDefinition, options);
171
+
172
+ executeServerlessDeployment(environment, options);
173
+ }
174
+
37
175
  module.exports = { deployCommand };
@@ -27,7 +27,7 @@ infrastructure/
27
27
  ├── AWS-DISCOVERY-TROUBLESHOOTING.md # AWS discovery troubleshooting
28
28
  ├── DEPLOYMENT-INSTRUCTIONS.md # General deployment instructions
29
29
  ├── README-TESTING.md # Testing strategy documentation
30
- ├──
30
+ ├──
31
31
  ├── cloudformation/ # CloudFormation templates
32
32
  │ ├── monitoring-infrastructure.yaml # Enhanced monitoring (Phase 3)
33
33
  │ ├── cdn-infrastructure.yaml # CDN and UI distribution (Phase 3)
@@ -60,71 +60,79 @@ infrastructure/
60
60
  #### 1. Serverless Template Generator (`serverless-template.js`)
61
61
 
62
62
  Generates complete serverless.yml configurations with:
63
- - VPC configuration and resource discovery
64
- - KMS encryption for field-level encryption
65
- - SSM Parameter Store integration
66
- - Integration-specific functions and queues
67
- - WebSocket support for real-time features
63
+
64
+ - VPC configuration and resource discovery
65
+ - KMS encryption for field-level encryption
66
+ - SSM Parameter Store integration
67
+ - Integration-specific functions and queues
68
+ - WebSocket support for real-time features
68
69
 
69
70
  #### 2. AWS Discovery (`aws-discovery.js`)
70
71
 
71
72
  Automatically discovers existing AWS resources:
72
- - Default VPC and security groups
73
- - Private subnets for Lambda functions
74
- - Customer-managed KMS keys
75
- - Route tables for VPC endpoints
73
+
74
+ - Default VPC and security groups
75
+ - Private subnets for Lambda functions
76
+ - Customer-managed KMS keys
77
+ - Route tables for VPC endpoints
76
78
 
77
79
  #### 3. Build-Time Discovery (`build-time-discovery.js`)
78
80
 
79
81
  Integrates AWS discovery into the build process:
80
- - Pre-build hook for serverless deployments
81
- - Environment variable injection
82
- - Template variable replacement
83
- - Error handling and fallback values
82
+
83
+ - Pre-build hook for serverless deployments
84
+ - Environment variable injection
85
+ - Template variable replacement
86
+ - Error handling and fallback values
84
87
 
85
88
  ### Phase 3 Infrastructure
86
89
 
87
90
  #### 1. Enhanced Monitoring (`cloudformation/monitoring-infrastructure.yaml`)
88
91
 
89
92
  Production-ready monitoring with:
90
- - Code generation service monitoring
91
- - UI distribution monitoring
92
- - Advanced CloudWatch dashboards
93
- - Custom metrics and alarms
93
+
94
+ - Code generation service monitoring
95
+ - UI distribution monitoring
96
+ - Advanced CloudWatch dashboards
97
+ - Custom metrics and alarms
94
98
 
95
99
  #### 2. CDN Infrastructure (`cloudformation/cdn-infrastructure.yaml`)
96
100
 
97
101
  CloudFront distribution for UI packages:
98
- - S3 bucket for multi-framework UI packages
99
- - CloudFront distribution with custom domains
100
- - Lambda function for package deployment
101
- - API Gateway for package management
102
+
103
+ - S3 bucket for multi-framework UI packages
104
+ - CloudFront distribution with custom domains
105
+ - Lambda function for package deployment
106
+ - API Gateway for package management
102
107
 
103
108
  #### 3. Code Generation Infrastructure (`cloudformation/codegen-infrastructure.yaml`)
104
109
 
105
110
  Serverless code generation platform:
106
- - SQS queue for generation requests
107
- - Lambda function with AI/ML integration
108
- - DynamoDB tracking table
109
- - S3 storage for templates and generated code
110
- - ElastiCache for template caching
111
+
112
+ - SQS queue for generation requests
113
+ - Lambda function with AI/ML integration
114
+ - DynamoDB tracking table
115
+ - S3 storage for templates and generated code
116
+ - ElastiCache for template caching
111
117
 
112
118
  #### 4. Advanced Alerting (`cloudformation/alerting-infrastructure.yaml`)
113
119
 
114
120
  Multi-channel alerting system:
115
- - Multiple SNS topics for alert severity levels
116
- - Lambda function for alert processing
117
- - PagerDuty and Slack integration
118
- - Composite alarms for system health
119
- - Advanced metrics collection
121
+
122
+ - Multiple SNS topics for alert severity levels
123
+ - Lambda function for alert processing
124
+ - PagerDuty and Slack integration
125
+ - Composite alarms for system health
126
+ - Advanced metrics collection
120
127
 
121
128
  #### 5. Deployment Pipeline (`cloudformation/deployment-pipeline.yaml`)
122
129
 
123
130
  CI/CD pipeline for automated deployments:
124
- - CodePipeline with GitHub integration
125
- - CodeBuild projects for backend and UI
126
- - Multi-stage deployment workflow
127
- - Integration testing and approval gates
131
+
132
+ - CodePipeline with GitHub integration
133
+ - CodeBuild projects for backend and UI
134
+ - Multi-stage deployment workflow
135
+ - Integration testing and approval gates
128
136
 
129
137
  ## Configuration Options
130
138
 
@@ -135,7 +143,7 @@ const appDefinition = {
135
143
  // Basic configuration
136
144
  name: 'my-frigg-app',
137
145
  provider: 'aws',
138
-
146
+
139
147
  // VPC configuration
140
148
  vpc: {
141
149
  enable: true,
@@ -144,22 +152,22 @@ const appDefinition = {
144
152
  subnetIds: [...], // Optional: custom subnets
145
153
  enableVPCEndpoints: true // Optional: create VPC endpoints
146
154
  },
147
-
155
+
148
156
  // KMS encryption
149
157
  encryption: {
150
158
  useDefaultKMSForFieldLevelEncryption: true
151
159
  },
152
-
160
+
153
161
  // SSM Parameter Store
154
162
  ssm: {
155
163
  enable: true
156
164
  },
157
-
165
+
158
166
  // WebSocket support (Phase 3)
159
167
  websockets: {
160
168
  enable: true
161
169
  },
162
-
170
+
163
171
  // Integrations
164
172
  integrations: [
165
173
  { Definition: { name: 'hubspot' } },
@@ -195,10 +203,8 @@ SERVICE_NAME=my-frigg-app
195
203
  const { composeServerlessDefinition } = require('./serverless-template');
196
204
 
197
205
  const appDefinition = {
198
- name: 'my-app',
199
- integrations: [
200
- { Definition: { name: 'hubspot' } }
201
- ]
206
+ name: 'my-app',
207
+ integrations: [{ Definition: { name: 'hubspot' } }],
202
208
  };
203
209
 
204
210
  const serverlessConfig = await composeServerlessDefinition(appDefinition);
@@ -209,13 +215,11 @@ const serverlessConfig = await composeServerlessDefinition(appDefinition);
209
215
 
210
216
  ```javascript
211
217
  const appDefinition = {
212
- name: 'secure-app',
213
- vpc: { enable: true },
214
- encryption: { useDefaultKMSForFieldLevelEncryption: true },
215
- ssm: { enable: true },
216
- integrations: [
217
- { Definition: { name: 'salesforce' } }
218
- ]
218
+ name: 'secure-app',
219
+ vpc: { enable: true },
220
+ encryption: { useDefaultKMSForFieldLevelEncryption: true },
221
+ ssm: { enable: true },
222
+ integrations: [{ Definition: { name: 'salesforce' } }],
219
223
  };
220
224
 
221
225
  const serverlessConfig = await composeServerlessDefinition(appDefinition);
@@ -225,12 +229,10 @@ const serverlessConfig = await composeServerlessDefinition(appDefinition);
225
229
 
226
230
  ```javascript
227
231
  const appDefinition = {
228
- name: 'realtime-app',
229
- websockets: { enable: true },
230
- vpc: { enable: true },
231
- integrations: [
232
- { Definition: { name: 'slack' } }
233
- ]
232
+ name: 'realtime-app',
233
+ websockets: { enable: true },
234
+ vpc: { enable: true },
235
+ integrations: [{ Definition: { name: 'slack' } }],
234
236
  };
235
237
 
236
238
  const serverlessConfig = await composeServerlessDefinition(appDefinition);
@@ -259,19 +261,21 @@ npm test -- --watch
259
261
  ### Test Categories
260
262
 
261
263
  1. **Unit Tests**: Test individual components
262
- - AWS discovery utilities
263
- - Serverless template generation
264
- - IAM policy generation
264
+
265
+ - AWS discovery utilities
266
+ - Serverless template generation
267
+ - IAM policy generation
265
268
 
266
269
  2. **Integration Tests**: Test end-to-end workflows
267
- - Complete discovery and template generation
268
- - Plugin integration
269
- - Phase 3 infrastructure validation
270
+
271
+ - Complete discovery and template generation
272
+ - Plugin integration
273
+ - Phase 3 infrastructure validation
270
274
 
271
275
  3. **Performance Tests**: Validate infrastructure limits
272
- - CloudFormation template sizes
273
- - Resource count limits
274
- - Cross-stack dependencies
276
+ - CloudFormation template sizes
277
+ - Resource count limits
278
+ - Cross-stack dependencies
275
279
 
276
280
  ### Mock Data
277
281
 
@@ -279,11 +283,12 @@ Tests use mock AWS resources to avoid real AWS API calls:
279
283
 
280
284
  ```javascript
281
285
  const mockAWSResources = {
282
- defaultVpcId: 'vpc-12345678',
283
- defaultSecurityGroupId: 'sg-12345678',
284
- privateSubnetId1: 'subnet-private-1',
285
- privateSubnetId2: 'subnet-private-2',
286
- defaultKmsKeyId: 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012'
286
+ defaultVpcId: 'vpc-12345678',
287
+ defaultSecurityGroupId: 'sg-12345678',
288
+ privateSubnetId1: 'subnet-private-1',
289
+ privateSubnetId2: 'subnet-private-2',
290
+ defaultKmsKeyId:
291
+ 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012',
287
292
  };
288
293
  ```
289
294
 
@@ -293,18 +298,18 @@ const mockAWSResources = {
293
298
 
294
299
  The infrastructure requires specific IAM permissions for AWS resource discovery and deployment:
295
300
 
296
- - **EC2**: Describe VPCs, subnets, security groups, route tables
297
- - **KMS**: List keys, describe keys
298
- - **STS**: Get caller identity
299
- - **CloudFormation**: Full access for stack operations
300
- - **Lambda**: Function management
301
- - **API Gateway**: API management
302
- - **S3**: Bucket and object operations (including tagging)
303
- - **DynamoDB**: Table operations
304
- - **SQS**: Queue operations
305
- - **SNS**: Topic operations
306
- - **CloudWatch**: Metrics and alarms
307
- - **IAM**: Role and policy management
301
+ - **EC2**: Describe VPCs, subnets, security groups, route tables
302
+ - **KMS**: List keys, describe keys
303
+ - **STS**: Get caller identity
304
+ - **CloudFormation**: Full access for stack operations
305
+ - **Lambda**: Function management
306
+ - **API Gateway**: API management
307
+ - **S3**: Bucket and object operations (including tagging)
308
+ - **DynamoDB**: Table operations
309
+ - **SQS**: Queue operations
310
+ - **SNS**: Topic operations
311
+ - **CloudWatch**: Metrics and alarms
312
+ - **IAM**: Role and policy management
308
313
 
309
314
  ### Best Practices
310
315
 
@@ -348,6 +353,8 @@ serverless print
348
353
  aws cloudformation validate-template --template-body file://template.json
349
354
  ```
350
355
 
356
+ - **Connectivity to external services (e.g., databases):** If your Lambda functions in a VPC cannot connect to external services, ensure that the `FriggLambdaSecurityGroup` has the correct **egress** rules to allow outbound traffic on the required ports (e.g., port 27017 for MongoDB).
357
+
351
358
  #### Infrastructure Test Failures
352
359
 
353
360
  ```bash
@@ -364,19 +371,22 @@ npm run test:debug
364
371
  ### Performance Optimization
365
372
 
366
373
  #### Lambda Cold Starts
367
- - Use provisioned concurrency for critical functions
368
- - Optimize function size and dependencies
369
- - Monitor cold start metrics
374
+
375
+ - Use provisioned concurrency for critical functions
376
+ - Optimize function size and dependencies
377
+ - Monitor cold start metrics
370
378
 
371
379
  #### VPC Performance
372
- - Use VPC endpoints to reduce NAT Gateway costs
373
- - Monitor ENI creation/deletion times
374
- - Consider Lambda@Edge for global distribution
380
+
381
+ - Use VPC endpoints to reduce NAT Gateway costs
382
+ - Monitor ENI creation/deletion times
383
+ - Consider Lambda@Edge for global distribution
375
384
 
376
385
  #### Cost Optimization
377
- - Use S3 Intelligent Tiering
378
- - Configure CloudWatch log retention
379
- - Monitor and alert on unexpected usage
386
+
387
+ - Use S3 Intelligent Tiering
388
+ - Configure CloudWatch log retention
389
+ - Monitor and alert on unexpected usage
380
390
 
381
391
  ## Contributing
382
392
 
@@ -405,17 +415,17 @@ npm run test:debug
405
415
 
406
416
  ## Support
407
417
 
408
- - **Documentation**: See `PHASE3-DEPLOYMENT-GUIDE.md` for detailed deployment instructions
409
- - **Testing**: See `README-TESTING.md` for testing strategy
410
- - **Troubleshooting**: See `AWS-DISCOVERY-TROUBLESHOOTING.md` for common issues
411
- - **Issues**: Create GitHub issues for bugs and feature requests
412
- - **Discussions**: Use GitHub Discussions for questions and ideas
418
+ - **Documentation**: See `PHASE3-DEPLOYMENT-GUIDE.md` for detailed deployment instructions
419
+ - **Testing**: See `README-TESTING.md` for testing strategy
420
+ - **Troubleshooting**: See `AWS-DISCOVERY-TROUBLESHOOTING.md` for common issues
421
+ - **Issues**: Create GitHub issues for bugs and feature requests
422
+ - **Discussions**: Use GitHub Discussions for questions and ideas
413
423
 
414
424
  ## Related Documentation
415
425
 
416
- - [Phase 3 Deployment Guide](./PHASE3-DEPLOYMENT-GUIDE.md)
417
- - [Testing Strategy](./README-TESTING.md)
418
- - [AWS Discovery Troubleshooting](./AWS-DISCOVERY-TROUBLESHOOTING.md)
419
- - [IAM Policy Templates](./IAM-POLICY-TEMPLATES.md)
420
- - [VPC Configuration](./VPC-CONFIGURATION.md)
421
- - [WebSocket Configuration](./WEBSOCKET-CONFIGURATION.md)
426
+ - [Phase 3 Deployment Guide](./PHASE3-DEPLOYMENT-GUIDE.md)
427
+ - [Testing Strategy](./README-TESTING.md)
428
+ - [AWS Discovery Troubleshooting](./AWS-DISCOVERY-TROUBLESHOOTING.md)
429
+ - [IAM Policy Templates](./IAM-POLICY-TEMPLATES.md)
430
+ - [VPC Configuration](./VPC-CONFIGURATION.md)
431
+ - [WebSocket Configuration](./WEBSOCKET-CONFIGURATION.md)
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Environment variable validator for Frigg applications
3
+ * Validates that required environment variables are present based on appDefinition
4
+ */
5
+
6
+ /**
7
+ * Validate environment variables against appDefinition
8
+ * @param {Object} AppDefinition - Application definition with environment config
9
+ * @returns {Object} Validation results with valid, missing, and warnings arrays
10
+ */
11
+ const validateEnvironmentVariables = (AppDefinition) => {
12
+ const results = {
13
+ valid: [],
14
+ missing: [],
15
+ warnings: [],
16
+ };
17
+
18
+ if (!AppDefinition.environment) {
19
+ return results;
20
+ }
21
+
22
+ console.log('🔍 Validating environment variables...');
23
+
24
+ for (const [key, value] of Object.entries(AppDefinition.environment)) {
25
+ if (value === true) {
26
+ if (process.env[key]) {
27
+ results.valid.push(key);
28
+ } else {
29
+ results.missing.push(key);
30
+ }
31
+ }
32
+ }
33
+
34
+ // Special handling for certain variables
35
+ if (results.missing.includes('NODE_ENV')) {
36
+ results.warnings.push('NODE_ENV not set, defaulting to "production"');
37
+ // Remove from missing since it has a default
38
+ results.missing = results.missing.filter((v) => v !== 'NODE_ENV');
39
+ }
40
+
41
+ // Report results
42
+ if (results.valid.length > 0) {
43
+ console.log(
44
+ ` ✅ Valid: ${results.valid.length} environment variables found`
45
+ );
46
+ }
47
+
48
+ if (results.missing.length > 0) {
49
+ console.log(` ⚠️ Missing: ${results.missing.join(', ')}`);
50
+ results.warnings.push(
51
+ `Missing ${results.missing.length} environment variables. These should be set in your CI/CD environment or .env file`
52
+ );
53
+ }
54
+
55
+ if (results.warnings.length > 0) {
56
+ results.warnings.forEach((warning) => {
57
+ console.log(` ⚠️ ${warning}`);
58
+ });
59
+ }
60
+
61
+ return results;
62
+ };
63
+
64
+ /**
65
+ * Check if all required environment variables are present
66
+ * @param {Object} AppDefinition - Application definition
67
+ * @returns {boolean} True if all required variables are present
68
+ */
69
+ const hasAllRequiredEnvVars = (AppDefinition) => {
70
+ const results = validateEnvironmentVariables(AppDefinition);
71
+ return results.missing.length === 0;
72
+ };
73
+
74
+ module.exports = {
75
+ validateEnvironmentVariables,
76
+ hasAllRequiredEnvVars,
77
+ };
@@ -180,6 +180,114 @@ function generateIAMCloudFormation(appDefinition, options = {}) {
180
180
  };
181
181
 
182
182
  // Add Core Deployment Policy (always needed)
183
+ const coreActions = [
184
+ // CloudFormation permissions
185
+ 'cloudformation:CreateStack',
186
+ 'cloudformation:UpdateStack',
187
+ 'cloudformation:DeleteStack',
188
+ 'cloudformation:DescribeStacks',
189
+ 'cloudformation:DescribeStackEvents',
190
+ 'cloudformation:DescribeStackResources',
191
+ 'cloudformation:DescribeStackResource',
192
+ 'cloudformation:ListStackResources',
193
+ 'cloudformation:GetTemplate',
194
+ 'cloudformation:DescribeChangeSet',
195
+ 'cloudformation:CreateChangeSet',
196
+ 'cloudformation:DeleteChangeSet',
197
+ 'cloudformation:ExecuteChangeSet',
198
+ 'cloudformation:ValidateTemplate',
199
+
200
+ // Lambda permissions
201
+ 'lambda:CreateFunction',
202
+ 'lambda:UpdateFunctionCode',
203
+ 'lambda:UpdateFunctionConfiguration',
204
+ 'lambda:DeleteFunction',
205
+ 'lambda:GetFunction',
206
+ 'lambda:ListFunctions',
207
+ 'lambda:PublishVersion',
208
+ 'lambda:CreateAlias',
209
+ 'lambda:UpdateAlias',
210
+ 'lambda:DeleteAlias',
211
+ 'lambda:GetAlias',
212
+ 'lambda:AddPermission',
213
+ 'lambda:RemovePermission',
214
+ 'lambda:GetPolicy',
215
+ 'lambda:PutProvisionedConcurrencyConfig',
216
+ 'lambda:DeleteProvisionedConcurrencyConfig',
217
+ 'lambda:PutConcurrency',
218
+ 'lambda:DeleteConcurrency',
219
+ 'lambda:TagResource',
220
+ 'lambda:UntagResource',
221
+ 'lambda:ListVersionsByFunction',
222
+
223
+ // IAM permissions
224
+ 'iam:CreateRole',
225
+ 'iam:DeleteRole',
226
+ 'iam:GetRole',
227
+ 'iam:PassRole',
228
+ 'iam:PutRolePolicy',
229
+ 'iam:DeleteRolePolicy',
230
+ 'iam:GetRolePolicy',
231
+ 'iam:AttachRolePolicy',
232
+ 'iam:DetachRolePolicy',
233
+ 'iam:TagRole',
234
+ 'iam:UntagRole',
235
+ 'iam:ListPolicyVersions',
236
+
237
+ // S3 permissions
238
+ 's3:CreateBucket',
239
+ 's3:PutObject',
240
+ 's3:GetObject',
241
+ 's3:DeleteObject',
242
+ 's3:PutBucketPolicy',
243
+ 's3:PutBucketVersioning',
244
+ 's3:PutBucketPublicAccessBlock',
245
+ 's3:GetBucketLocation',
246
+ 's3:ListBucket',
247
+
248
+ // SQS permissions
249
+ 'sqs:CreateQueue',
250
+ 'sqs:DeleteQueue',
251
+ 'sqs:GetQueueAttributes',
252
+ 'sqs:SetQueueAttributes',
253
+ 'sqs:GetQueueUrl',
254
+ 'sqs:TagQueue',
255
+ 'sqs:UntagQueue',
256
+
257
+ // SNS permissions
258
+ 'sns:CreateTopic',
259
+ 'sns:DeleteTopic',
260
+ 'sns:GetTopicAttributes',
261
+ 'sns:SetTopicAttributes',
262
+ 'sns:Subscribe',
263
+ 'sns:Unsubscribe',
264
+ 'sns:ListSubscriptionsByTopic',
265
+ 'sns:TagResource',
266
+ 'sns:UntagResource',
267
+
268
+ // CloudWatch and Logs permissions
269
+ 'cloudwatch:PutMetricAlarm',
270
+ 'cloudwatch:DeleteAlarms',
271
+ 'cloudwatch:DescribeAlarms',
272
+ 'logs:CreateLogGroup',
273
+ 'logs:CreateLogStream',
274
+ 'logs:DeleteLogGroup',
275
+ 'logs:DescribeLogGroups',
276
+ 'logs:DescribeLogStreams',
277
+ 'logs:FilterLogEvents',
278
+ 'logs:PutLogEvents',
279
+ 'logs:PutRetentionPolicy',
280
+
281
+ // API Gateway permissions
282
+ 'apigateway:POST',
283
+ 'apigateway:PUT',
284
+ 'apigateway:DELETE',
285
+ 'apigateway:GET',
286
+ 'apigateway:PATCH',
287
+ 'apigateway:TagResource',
288
+ 'apigateway:UntagResource',
289
+ ];
290
+
183
291
  const coreStatements = [
184
292
  {
185
293
  Sid: 'CloudFormationFriggStacks',
@@ -374,6 +482,8 @@ function generateIAMCloudFormation(appDefinition, options = {}) {
374
482
  'apigateway:DELETE',
375
483
  'apigateway:GET',
376
484
  'apigateway:PATCH',
485
+ 'apigateway:TagResource',
486
+ 'apigateway:UntagResource',
377
487
  ],
378
488
  Resource: [
379
489
  'arn:aws:apigateway:*::/restapis',
@@ -397,8 +507,6 @@ function generateIAMCloudFormation(appDefinition, options = {}) {
397
507
  'arn:aws:apigateway:*::/apis/*',
398
508
  'arn:aws:apigateway:*::/apis/*/stages',
399
509
  'arn:aws:apigateway:*::/apis/*/stages/*',
400
- 'arn:aws:apigateway:*::/apis/*/mappings',
401
- 'arn:aws:apigateway:*::/apis/*/mappings/*',
402
510
  'arn:aws:apigateway:*::/domainnames',
403
511
  'arn:aws:apigateway:*::/domainnames/*',
404
512
  'arn:aws:apigateway:*::/domainnames/*/apimappings',
@@ -199,13 +199,17 @@
199
199
  "apigateway:PUT",
200
200
  "apigateway:DELETE",
201
201
  "apigateway:GET",
202
- "apigateway:PATCH"
202
+ "apigateway:PATCH",
203
+ "apigateway:TagResource",
204
+ "apigateway:UntagResource"
203
205
  ],
204
206
  "Resource": [
205
207
  "arn:aws:apigateway:*::/restapis",
206
208
  "arn:aws:apigateway:*::/restapis/*",
207
209
  "arn:aws:apigateway:*::/apis",
208
210
  "arn:aws:apigateway:*::/apis/*",
211
+ "arn:aws:apigateway:*::/apis/*/stages",
212
+ "arn:aws:apigateway:*::/apis/*/stages/*",
209
213
  "arn:aws:apigateway:*::/domainnames",
210
214
  "arn:aws:apigateway:*::/domainnames/*"
211
215
  ]
@@ -199,13 +199,17 @@
199
199
  "apigateway:PUT",
200
200
  "apigateway:DELETE",
201
201
  "apigateway:GET",
202
- "apigateway:PATCH"
202
+ "apigateway:PATCH",
203
+ "apigateway:TagResource",
204
+ "apigateway:UntagResource"
203
205
  ],
204
206
  "Resource": [
205
207
  "arn:aws:apigateway:*::/restapis",
206
208
  "arn:aws:apigateway:*::/restapis/*",
207
209
  "arn:aws:apigateway:*::/apis",
208
210
  "arn:aws:apigateway:*::/apis/*",
211
+ "arn:aws:apigateway:*::/apis/*/stages",
212
+ "arn:aws:apigateway:*::/apis/*/stages/*",
209
213
  "arn:aws:apigateway:*::/domainnames",
210
214
  "arn:aws:apigateway:*::/domainnames/*"
211
215
  ]
@@ -16,6 +16,68 @@ const shouldRunDiscovery = (AppDefinition) => {
16
16
  );
17
17
  };
18
18
 
19
+ /**
20
+ * Extract environment variables from AppDefinition
21
+ * @param {Object} AppDefinition - Application definition
22
+ * @returns {Object} Environment variables to set in serverless
23
+ */
24
+ const getAppEnvironmentVars = (AppDefinition) => {
25
+ const envVars = {};
26
+
27
+ // AWS Lambda reserved environment variables that cannot be set (from official AWS docs)
28
+ const reservedVars = new Set([
29
+ '_HANDLER',
30
+ '_X_AMZN_TRACE_ID',
31
+ 'AWS_DEFAULT_REGION',
32
+ 'AWS_EXECUTION_ENV',
33
+ 'AWS_REGION',
34
+ 'AWS_LAMBDA_FUNCTION_NAME',
35
+ 'AWS_LAMBDA_FUNCTION_MEMORY_SIZE',
36
+ 'AWS_LAMBDA_FUNCTION_VERSION',
37
+ 'AWS_LAMBDA_INITIALIZATION_TYPE',
38
+ 'AWS_LAMBDA_LOG_GROUP_NAME',
39
+ 'AWS_LAMBDA_LOG_STREAM_NAME',
40
+ 'AWS_ACCESS_KEY',
41
+ 'AWS_ACCESS_KEY_ID',
42
+ 'AWS_SECRET_ACCESS_KEY',
43
+ 'AWS_SESSION_TOKEN',
44
+ ]);
45
+
46
+ if (AppDefinition.environment) {
47
+ console.log('📋 Loading environment variables from appDefinition...');
48
+ const envKeys = [];
49
+ const skippedKeys = [];
50
+
51
+ for (const [key, value] of Object.entries(AppDefinition.environment)) {
52
+ if (value === true) {
53
+ if (reservedVars.has(key)) {
54
+ skippedKeys.push(key);
55
+ } else {
56
+ envVars[key] = `\${env:${key}, ''}`;
57
+ envKeys.push(key);
58
+ }
59
+ }
60
+ }
61
+
62
+ if (envKeys.length > 0) {
63
+ console.log(
64
+ ` Found ${
65
+ envKeys.length
66
+ } environment variables: ${envKeys.join(', ')}`
67
+ );
68
+ }
69
+ if (skippedKeys.length > 0) {
70
+ console.log(
71
+ ` ⚠️ Skipped ${
72
+ skippedKeys.length
73
+ } reserved AWS Lambda variables: ${skippedKeys.join(', ')}`
74
+ );
75
+ }
76
+ }
77
+
78
+ return envVars;
79
+ };
80
+
19
81
  /**
20
82
  * Find the actual path to node_modules directory
21
83
  * Tries multiple methods to locate node_modules:
@@ -388,6 +450,13 @@ const createVPCInfrastructure = (AppDefinition) => {
388
450
  CidrIp: '0.0.0.0/0',
389
451
  Description: 'DNS UDP',
390
452
  },
453
+ {
454
+ IpProtocol: 'tcp',
455
+ FromPort: 27017,
456
+ ToPort: 27017,
457
+ CidrIp: '0.0.0.0/0',
458
+ Description: 'MongoDB outbound',
459
+ },
391
460
  ],
392
461
  Tags: [
393
462
  {
@@ -555,17 +624,8 @@ const composeServerlessDefinition = async (AppDefinition) => {
555
624
  }
556
625
  }
557
626
 
558
- // Debug: log keys of env vars available during deploy (to verify GA -> Serverless pass-through)
559
- try {
560
- const envKeys = Object.keys(process.env || {}).sort();
561
- console.log(
562
- 'Frigg deploy env keys (sample):',
563
- envKeys.slice(0, 30),
564
- `... total=${envKeys.length}`
565
- );
566
- } catch (e) {
567
- console.log('Frigg deploy env keys: <unavailable>', e?.message);
568
- }
627
+ // Get environment variables from appDefinition
628
+ const appEnvironmentVars = getAppEnvironmentVars(AppDefinition);
569
629
 
570
630
  const definition = {
571
631
  frameworkVersion: '>=3.17.0',
@@ -588,18 +648,8 @@ const composeServerlessDefinition = async (AppDefinition) => {
588
648
  environment: {
589
649
  STAGE: '${opt:stage, "dev"}',
590
650
  AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1,
591
- // Pass through FRIGG__ prefixed variables (stripping the prefix)
592
- // This avoids CloudFormation validation errors from system variables
593
- ...Object.fromEntries(
594
- Object.entries(process.env)
595
- .filter(([key]) => key.startsWith('FRIGG__'))
596
- .map(([key, value]) => [
597
- key.replace('FRIGG__', ''),
598
- value,
599
- ])
600
- ),
601
- // Also include essential non-prefixed variables
602
- ...(process.env.NODE_ENV && { NODE_ENV: process.env.NODE_ENV }),
651
+ // Add environment variables from appDefinition
652
+ ...appEnvironmentVars,
603
653
  // Add discovered resources to environment if available
604
654
  ...(discoveredResources.defaultVpcId && {
605
655
  AWS_DISCOVERY_VPC_ID: discoveredResources.defaultVpcId,
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.35",
4
+ "version": "2.0.0-next.37",
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.35",
13
- "@friggframework/test": "2.0.0-next.35",
12
+ "@friggframework/schemas": "2.0.0-next.37",
13
+ "@friggframework/test": "2.0.0-next.37",
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.35",
36
- "@friggframework/prettier-config": "2.0.0-next.35",
35
+ "@friggframework/eslint-config": "2.0.0-next.37",
36
+ "@friggframework/prettier-config": "2.0.0-next.37",
37
37
  "prettier": "^2.7.1",
38
38
  "serverless": "3.39.0",
39
39
  "serverless-dotenv-plugin": "^6.0.0",
@@ -65,5 +65,5 @@
65
65
  "publishConfig": {
66
66
  "access": "public"
67
67
  },
68
- "gitHead": "2b5b01f23819faad6b2780f1aaabbd9bfb52fe4a"
68
+ "gitHead": "39f0a48fa6bd17c4b8c5caba58c88edc6d6fdd1f"
69
69
  }