@friggframework/devtools 2.0.0--canary.517.5b0b222.0 → 2.0.0--canary.517.837ee90.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.
@@ -178,6 +178,13 @@ class AdminScriptBuilder extends InfrastructureBuilder {
178
178
  }
179
179
 
180
180
  createSchedulerResources(appDefinition, result) {
181
+ // Constructed ARN, not Fn::GetAtt: a GetAtt edge to the executor closes a
182
+ // CloudFormation circular dependency via the shared Lambda execution role.
183
+ const executorArn = {
184
+ 'Fn::Sub':
185
+ 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${self:service}-${self:provider.stage}-adminScriptExecutor',
186
+ };
187
+
181
188
  // Create IAM role for EventBridge Scheduler
182
189
  result.resources.AdminScriptSchedulerRole = {
183
190
  Type: 'AWS::IAM::Role',
@@ -198,7 +205,7 @@ class AdminScriptBuilder extends InfrastructureBuilder {
198
205
  Statement: [{
199
206
  Effect: 'Allow',
200
207
  Action: 'lambda:InvokeFunction',
201
- Resource: { 'Fn::GetAtt': ['AdminScriptExecutorLambdaFunction', 'Arn'] },
208
+ Resource: executorArn,
202
209
  }],
203
210
  },
204
211
  }],
@@ -213,13 +220,17 @@ class AdminScriptBuilder extends InfrastructureBuilder {
213
220
  },
214
221
  };
215
222
 
216
- // Env vars consumed by the admin-script router when building the AWS
217
- // scheduler adapter. Names must match admin-script-router.js exactly.
218
223
  result.environment.SCHEDULER_PROVIDER = 'aws';
219
- result.environment.SCHEDULER_ROLE_ARN = { 'Fn::GetAtt': ['AdminScriptSchedulerRole', 'Arn'] };
220
- result.environment.ADMIN_SCRIPT_SCHEDULE_GROUP = { Ref: 'AdminScriptScheduleGroup' };
221
- result.environment.ADMIN_SCRIPT_EXECUTOR_LAMBDA_ARN = {
222
- 'Fn::GetAtt': ['AdminScriptExecutorLambdaFunction', 'Arn'],
224
+
225
+ // Router-scoped, not shared provider env: broadcasting these resource
226
+ // references to every function creates CloudFormation circular deps.
227
+ result.functions.adminScriptRouter.environment = {
228
+ ...(result.functions.adminScriptRouter.environment || {}),
229
+ SCHEDULER_ROLE_ARN: {
230
+ 'Fn::GetAtt': ['AdminScriptSchedulerRole', 'Arn'],
231
+ },
232
+ ADMIN_SCRIPT_SCHEDULE_GROUP: { Ref: 'AdminScriptScheduleGroup' },
233
+ ADMIN_SCRIPT_EXECUTOR_LAMBDA_ARN: executorArn,
223
234
  };
224
235
 
225
236
  // The router manages schedules through the AWS scheduler adapter, so it
@@ -366,17 +366,33 @@ describe('AdminScriptBuilder', () => {
366
366
  expect(result.resources.AdminScriptScheduleGroup).toBeDefined();
367
367
  expect(result.resources.AdminScriptScheduleGroup.Type).toBe('AWS::Scheduler::ScheduleGroup');
368
368
 
369
- // Check for environment variables consumed by the router's scheduler adapter
369
+ // SCHEDULER_PROVIDER is a constant and stays on the shared env.
370
370
  expect(result.environment.SCHEDULER_PROVIDER).toBe('aws');
371
- expect(result.environment.SCHEDULER_ROLE_ARN).toEqual({
371
+
372
+ // Resource-reference env vars are scoped to the router function (not
373
+ // the shared provider env) to avoid CloudFormation circular deps.
374
+ const routerEnv = result.functions.adminScriptRouter.environment;
375
+ expect(routerEnv.SCHEDULER_ROLE_ARN).toEqual({
372
376
  'Fn::GetAtt': ['AdminScriptSchedulerRole', 'Arn'],
373
377
  });
374
- expect(result.environment.ADMIN_SCRIPT_SCHEDULE_GROUP).toEqual({
378
+ expect(routerEnv.ADMIN_SCRIPT_SCHEDULE_GROUP).toEqual({
375
379
  Ref: 'AdminScriptScheduleGroup',
376
380
  });
377
- expect(result.environment.ADMIN_SCRIPT_EXECUTOR_LAMBDA_ARN).toEqual({
378
- 'Fn::GetAtt': ['AdminScriptExecutorLambdaFunction', 'Arn'],
381
+ // Executor referenced by constructed ARN (Fn::Sub), not Fn::GetAtt,
382
+ // so it creates no dependency edge.
383
+ expect(routerEnv.ADMIN_SCRIPT_EXECUTOR_LAMBDA_ARN).toEqual({
384
+ 'Fn::Sub':
385
+ 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${self:service}-${self:provider.stage}-adminScriptExecutor',
379
386
  });
387
+
388
+ // Regression guard: these must NOT leak onto the shared provider env
389
+ // (that is what produced the circular dependency, incl. the executor
390
+ // self-reference).
391
+ expect(result.environment.SCHEDULER_ROLE_ARN).toBeUndefined();
392
+ expect(result.environment.ADMIN_SCRIPT_SCHEDULE_GROUP).toBeUndefined();
393
+ expect(
394
+ result.environment.ADMIN_SCRIPT_EXECUTOR_LAMBDA_ARN
395
+ ).toBeUndefined();
380
396
  });
381
397
 
382
398
  it('should not create scheduler resources when enableScheduling is false', async () => {
@@ -494,10 +510,15 @@ describe('AdminScriptBuilder', () => {
494
510
  const policies = result.resources.AdminScriptSchedulerRole.Properties.Policies;
495
511
 
496
512
  expect(policies[0].PolicyName).toBe('InvokeLambda');
513
+ // Constructed ARN (Fn::Sub), not Fn::GetAtt — a GetAtt here closes a
514
+ // circular dependency through the shared Lambda execution role.
497
515
  expect(policies[0].PolicyDocument.Statement[0]).toEqual({
498
516
  Effect: 'Allow',
499
517
  Action: 'lambda:InvokeFunction',
500
- Resource: { 'Fn::GetAtt': ['AdminScriptExecutorLambdaFunction', 'Arn'] },
518
+ Resource: {
519
+ 'Fn::Sub':
520
+ 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${self:service}-${self:provider.stage}-adminScriptExecutor',
521
+ },
501
522
  });
502
523
  });
503
524
 
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--canary.517.5b0b222.0",
4
+ "version": "2.0.0--canary.517.837ee90.0",
5
5
  "bin": {
6
6
  "frigg": "./frigg-cli/index.js"
7
7
  },
@@ -25,9 +25,9 @@
25
25
  "@babel/eslint-parser": "^7.18.9",
26
26
  "@babel/parser": "^7.25.3",
27
27
  "@babel/traverse": "^7.25.3",
28
- "@friggframework/core": "2.0.0--canary.517.5b0b222.0",
29
- "@friggframework/schemas": "2.0.0--canary.517.5b0b222.0",
30
- "@friggframework/test": "2.0.0--canary.517.5b0b222.0",
28
+ "@friggframework/core": "2.0.0--canary.517.837ee90.0",
29
+ "@friggframework/schemas": "2.0.0--canary.517.837ee90.0",
30
+ "@friggframework/test": "2.0.0--canary.517.837ee90.0",
31
31
  "@hapi/boom": "^10.0.1",
32
32
  "@inquirer/prompts": "^5.3.8",
33
33
  "axios": "^1.18.0",
@@ -55,8 +55,8 @@
55
55
  "validate-npm-package-name": "^5.0.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@friggframework/eslint-config": "2.0.0--canary.517.5b0b222.0",
59
- "@friggframework/prettier-config": "2.0.0--canary.517.5b0b222.0",
58
+ "@friggframework/eslint-config": "2.0.0--canary.517.837ee90.0",
59
+ "@friggframework/prettier-config": "2.0.0--canary.517.837ee90.0",
60
60
  "aws-sdk-client-mock": "^4.1.0",
61
61
  "aws-sdk-client-mock-jest": "^4.1.0",
62
62
  "jest": "^30.1.3",
@@ -88,5 +88,5 @@
88
88
  "publishConfig": {
89
89
  "access": "public"
90
90
  },
91
- "gitHead": "5b0b222431533db4fe13b46369d88026a6fd4a19"
91
+ "gitHead": "837ee90500d8b87fbe9d904c1b64d0f1ce8b8f7a"
92
92
  }