@sembix/cli 0.1.0 → 1.0.2
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.
- package/README.md +135 -18
- package/dist/__tests__/config-schema.test.d.ts +2 -0
- package/dist/__tests__/config-schema.test.d.ts.map +1 -0
- package/dist/__tests__/config-schema.test.js +471 -0
- package/dist/__tests__/config-schema.test.js.map +1 -0
- package/dist/__tests__/integration/fixtures/configs.d.ts +477 -0
- package/dist/__tests__/integration/fixtures/configs.d.ts.map +1 -0
- package/dist/__tests__/integration/fixtures/configs.js +175 -0
- package/dist/__tests__/integration/fixtures/configs.js.map +1 -0
- package/dist/__tests__/integration/helpers/cli-runner.d.ts +63 -0
- package/dist/__tests__/integration/helpers/cli-runner.d.ts.map +1 -0
- package/dist/__tests__/integration/helpers/cli-runner.js +152 -0
- package/dist/__tests__/integration/helpers/cli-runner.js.map +1 -0
- package/dist/__tests__/integration/helpers/command-runner.d.ts +53 -0
- package/dist/__tests__/integration/helpers/command-runner.d.ts.map +1 -0
- package/dist/__tests__/integration/helpers/command-runner.js +117 -0
- package/dist/__tests__/integration/helpers/command-runner.js.map +1 -0
- package/dist/__tests__/integration/studio-create.test.d.ts +2 -0
- package/dist/__tests__/integration/studio-create.test.d.ts.map +1 -0
- package/dist/__tests__/integration/studio-create.test.js +209 -0
- package/dist/__tests__/integration/studio-create.test.js.map +1 -0
- package/dist/__tests__/integration/studio-update.test.d.ts +2 -0
- package/dist/__tests__/integration/studio-update.test.d.ts.map +1 -0
- package/dist/__tests__/integration/studio-update.test.js +166 -0
- package/dist/__tests__/integration/studio-update.test.js.map +1 -0
- package/dist/index.js +0 -0
- package/dist/prompts/__tests__/prompt-helpers.test.d.ts +2 -0
- package/dist/prompts/__tests__/prompt-helpers.test.d.ts.map +1 -0
- package/dist/prompts/__tests__/prompt-helpers.test.js +235 -0
- package/dist/prompts/__tests__/prompt-helpers.test.js.map +1 -0
- package/dist/sembix-cli-1.0.2.tgz +0 -0
- package/dist/utils/__tests__/config-loader.test.d.ts +2 -0
- package/dist/utils/__tests__/config-loader.test.d.ts.map +1 -0
- package/dist/utils/__tests__/config-loader.test.js +325 -0
- package/dist/utils/__tests__/config-loader.test.js.map +1 -0
- package/dist/utils/__tests__/github.test.d.ts +2 -0
- package/dist/utils/__tests__/github.test.d.ts.map +1 -0
- package/dist/utils/__tests__/github.test.js +282 -0
- package/dist/utils/__tests__/github.test.js.map +1 -0
- package/dist/utils/__tests__/ui.test.d.ts +2 -0
- package/dist/utils/__tests__/ui.test.d.ts.map +1 -0
- package/dist/utils/__tests__/ui.test.js +256 -0
- package/dist/utils/__tests__/ui.test.js.map +1 -0
- package/package.json +20 -9
package/README.md
CHANGED
|
@@ -22,6 +22,42 @@ A terminal-based CLI tool for managing Sembix products. An intuitive command-lin
|
|
|
22
22
|
- `workflow` - Update GitHub Action workflows
|
|
23
23
|
- **Deployment Repository** - A GitHub repository set up for Sembix Studio deployments
|
|
24
24
|
|
|
25
|
+
## Deployment Process
|
|
26
|
+
|
|
27
|
+
Sembix Studio uses a **two-phased deployment** process to establish secure cross-account access with the Sembix Hub:
|
|
28
|
+
|
|
29
|
+
### Phase 1: Bootstrap & Initial Deployment
|
|
30
|
+
|
|
31
|
+
1. **Create environment** - Run `sembix studio create` to configure your environment (Steps 1-8)
|
|
32
|
+
- Skip Hub integration initially (use `--skip-hub` or skip Step 9)
|
|
33
|
+
- Creates GitHub Actions environment with AWS configuration
|
|
34
|
+
|
|
35
|
+
2. **Bootstrap deployment** - Run the GitHub Actions workflow
|
|
36
|
+
- Deploys initial Terraform infrastructure
|
|
37
|
+
- Creates IAM roles and resources in your AWS account
|
|
38
|
+
- Produces IAM ARNs needed for Hub integration
|
|
39
|
+
|
|
40
|
+
3. **Coordinate with Sembix Support** - Send the IAM ARNs to Sembix support
|
|
41
|
+
- Sembix configures the Hub to trust your account
|
|
42
|
+
- Sembix provides Hub role ARNs back to you:
|
|
43
|
+
- Hub Engine Execution Role ARN
|
|
44
|
+
- Hub Consumer Role ARN
|
|
45
|
+
- Hub Admin Role ARN
|
|
46
|
+
- Hub AppConfig Role ARN (plus Application ID, Environment ID, Profile ID)
|
|
47
|
+
|
|
48
|
+
### Phase 2: Hub Integration & Full Deployment
|
|
49
|
+
|
|
50
|
+
4. **Add Hub integration** - Run `sembix studio add-hub` with ARNs from Sembix support
|
|
51
|
+
- Updates GitHub Actions environment with Hub role ARNs
|
|
52
|
+
- Configures cross-account access to Sembix Hub
|
|
53
|
+
|
|
54
|
+
5. **Final deployment** - Run the GitHub Actions workflow again
|
|
55
|
+
- Completes full Sembix Studio deployment
|
|
56
|
+
- Enables Hub cross-account access
|
|
57
|
+
- Your Studio environment is now fully operational
|
|
58
|
+
|
|
59
|
+
**Important:** Do not skip Phase 1. The Hub integration requires IAM resources created during the bootstrap deployment.
|
|
60
|
+
|
|
25
61
|
## Installation
|
|
26
62
|
|
|
27
63
|
### From npm (Recommended)
|
|
@@ -106,8 +142,25 @@ See [`config.example.yaml`](./config.example.yaml) for a complete configuration
|
|
|
106
142
|
|
|
107
143
|
### Quick Start
|
|
108
144
|
|
|
145
|
+
For a complete deployment, follow the **two-phased process** (see [Deployment Process](#deployment-process) section above):
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# PHASE 1: Bootstrap deployment
|
|
149
|
+
sembix studio create --skip-hub
|
|
150
|
+
# → Deploy via GitHub Actions
|
|
151
|
+
# → Send IAM ARNs to Sembix support
|
|
152
|
+
# → Receive Hub role ARNs from support
|
|
153
|
+
|
|
154
|
+
# PHASE 2: Hub integration and full deployment
|
|
155
|
+
sembix studio add-hub <environment>
|
|
156
|
+
# → Deploy via GitHub Actions again
|
|
157
|
+
# → Studio environment fully operational
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Alternative workflows:**
|
|
161
|
+
|
|
109
162
|
```bash
|
|
110
|
-
# Fully interactive (
|
|
163
|
+
# Fully interactive (prompts for all configuration)
|
|
111
164
|
sembix studio create
|
|
112
165
|
|
|
113
166
|
# Fast setup with config file
|
|
@@ -337,41 +390,105 @@ sembix studio list acme-corp/sembix-deployment
|
|
|
337
390
|
sembix studio list acme/deploy --token ghp_custom_token
|
|
338
391
|
```
|
|
339
392
|
|
|
340
|
-
### `sembix studio add-hub`
|
|
393
|
+
### `sembix studio add-hub`
|
|
341
394
|
|
|
342
|
-
|
|
395
|
+
Add Sembix Hub integration to an existing Studio environment. **This is typically used in Phase 2 of the deployment process** after receiving Hub role ARNs from Sembix support.
|
|
343
396
|
|
|
344
|
-
|
|
397
|
+
**Syntax:**
|
|
398
|
+
```bash
|
|
399
|
+
sembix studio add-hub [environment] [options]
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
**Options:**
|
|
403
|
+
- `-r, --repo <repo>` - Target repository (owner/repo format)
|
|
404
|
+
- `-e, --env <environment>` - Environment name (alternative to positional argument)
|
|
405
|
+
- `--hub-engine-execution-role <arn>` - Hub Engine Execution Role ARN (from Sembix support)
|
|
406
|
+
- `--hub-consumer-role <arn>` - Hub Consumer Role ARN (from Sembix support)
|
|
407
|
+
- `--hub-admin-role <arn>` - Hub Admin Role ARN (from Sembix support)
|
|
408
|
+
- `--hub-appconfig-role <arn>` - Hub AppConfig Role ARN (from Sembix support)
|
|
409
|
+
- `--hub-appconfig-app-id <id>` - Hub AppConfig Application ID (from Sembix support)
|
|
410
|
+
- `--hub-appconfig-env-id <id>` - Hub AppConfig Environment ID (from Sembix support)
|
|
411
|
+
- `--hub-appconfig-profile-id <id>` - Hub AppConfig Profile ID (from Sembix support)
|
|
412
|
+
|
|
413
|
+
**Example:**
|
|
414
|
+
```bash
|
|
415
|
+
# Interactive mode (prompts for ARNs)
|
|
416
|
+
sembix studio add-hub production --repo acme/deploy
|
|
417
|
+
|
|
418
|
+
# With CLI flags (using ARNs from Sembix support)
|
|
419
|
+
sembix studio add-hub production \
|
|
420
|
+
--repo acme/deploy \
|
|
421
|
+
--hub-engine-execution-role arn:aws:iam::999888777666:role/HubEngineRole \
|
|
422
|
+
--hub-consumer-role arn:aws:iam::999888777666:role/HubConsumerRole \
|
|
423
|
+
--hub-admin-role arn:aws:iam::999888777666:role/HubAdminRole \
|
|
424
|
+
--hub-appconfig-role arn:aws:iam::999888777666:role/HubAppConfigRole \
|
|
425
|
+
--hub-appconfig-app-id abc123 \
|
|
426
|
+
--hub-appconfig-env-id def456 \
|
|
427
|
+
--hub-appconfig-profile-id ghi789
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**When to use this command:**
|
|
431
|
+
|
|
432
|
+
- **Phase 2 deployment** (recommended): After completing Phase 1 bootstrap deployment and receiving Hub ARNs from Sembix support
|
|
433
|
+
- **Alternative**: Hub integration can also be added during initial environment creation (Step 9 of `create`) if you already have the Hub ARNs
|
|
434
|
+
- **Updates**: Use the `update` command to modify existing Hub configuration
|
|
435
|
+
|
|
436
|
+
**Important:** The Hub role ARNs are provided by Sembix support after they configure the Hub to trust your AWS account. Do not use this command until you have received these ARNs.
|
|
345
437
|
|
|
346
438
|
## Workflow
|
|
347
439
|
|
|
348
|
-
###
|
|
440
|
+
### Two-Phased Deployment Process
|
|
349
441
|
|
|
350
442
|
```
|
|
443
|
+
╔═══════════════════════════════════════════════════════════════════╗
|
|
444
|
+
║ PHASE 1: BOOTSTRAP DEPLOYMENT ║
|
|
445
|
+
╚═══════════════════════════════════════════════════════════════════╝
|
|
446
|
+
|
|
351
447
|
┌─────────────────────────────────────────────────┐
|
|
352
|
-
│ 1.
|
|
448
|
+
│ 1. Create Environment (Skip Hub) │
|
|
449
|
+
│ sembix studio create --skip-hub │
|
|
353
450
|
│ • Configure environment (Steps 1-8) │
|
|
354
|
-
│ •
|
|
355
|
-
│ • Creates GitHub environment
|
|
451
|
+
│ • Skip Hub integration (Step 9) │
|
|
452
|
+
│ • Creates GitHub Actions environment │
|
|
356
453
|
└─────────────────────────────────────────────────┘
|
|
357
454
|
↓
|
|
358
455
|
┌─────────────────────────────────────────────────┐
|
|
359
|
-
│ 2.
|
|
360
|
-
│ • Go to repo → Actions
|
|
361
|
-
│ •
|
|
362
|
-
│ •
|
|
456
|
+
│ 2. Bootstrap Deployment via GitHub Actions │
|
|
457
|
+
│ • Go to repo → Actions → Run workflow │
|
|
458
|
+
│ • Deploys initial Terraform infrastructure │
|
|
459
|
+
│ • Creates IAM roles in your AWS account │
|
|
460
|
+
│ • Copy IAM ARNs from deployment output │
|
|
363
461
|
└─────────────────────────────────────────────────┘
|
|
364
462
|
↓
|
|
365
463
|
┌─────────────────────────────────────────────────┐
|
|
366
|
-
│ 3.
|
|
367
|
-
│ •
|
|
368
|
-
│ •
|
|
464
|
+
│ 3. Coordinate with Sembix Support │
|
|
465
|
+
│ • Send IAM ARNs to Sembix support │
|
|
466
|
+
│ • Wait for Sembix to configure Hub │
|
|
467
|
+
│ • Receive Hub role ARNs from support: │
|
|
468
|
+
│ - Hub Engine Execution Role ARN │
|
|
469
|
+
│ - Hub Consumer Role ARN │
|
|
470
|
+
│ - Hub Admin Role ARN │
|
|
471
|
+
│ - Hub AppConfig Role ARN + IDs │
|
|
472
|
+
└─────────────────────────────────────────────────┘
|
|
473
|
+
|
|
474
|
+
╔═══════════════════════════════════════════════════════════════════╗
|
|
475
|
+
║ PHASE 2: HUB INTEGRATION & FULL DEPLOYMENT ║
|
|
476
|
+
╚═══════════════════════════════════════════════════════════════════╝
|
|
477
|
+
|
|
478
|
+
┌─────────────────────────────────────────────────┐
|
|
479
|
+
│ 4. Add Hub Integration │
|
|
480
|
+
│ sembix studio add-hub <environment> │
|
|
481
|
+
│ • Provide Hub role ARNs from support │
|
|
482
|
+
│ • Updates GitHub Actions environment │
|
|
483
|
+
│ • Configures cross-account access │
|
|
369
484
|
└─────────────────────────────────────────────────┘
|
|
370
485
|
↓
|
|
371
486
|
┌─────────────────────────────────────────────────┐
|
|
372
|
-
│
|
|
373
|
-
│ •
|
|
374
|
-
│ •
|
|
487
|
+
│ 5. Full Deployment via GitHub Actions │
|
|
488
|
+
│ • Go to repo → Actions → Run workflow │
|
|
489
|
+
│ • Completes full Sembix Studio deployment │
|
|
490
|
+
│ • Enables Hub cross-account access │
|
|
491
|
+
│ • ✅ Studio environment fully operational │
|
|
375
492
|
└─────────────────────────────────────────────────┘
|
|
376
493
|
```
|
|
377
494
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-schema.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config-schema.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { configFileSchema } from '../config-schema.js';
|
|
3
|
+
describe('config-schema', () => {
|
|
4
|
+
describe('configFileSchema - basic fields', () => {
|
|
5
|
+
it('should accept valid environment name', () => {
|
|
6
|
+
const config = { environmentName: 'production' };
|
|
7
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
it('should accept environment name with numbers and hyphens', () => {
|
|
10
|
+
const config = { environmentName: 'prod-env-123' };
|
|
11
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
it('should reject environment name with uppercase', () => {
|
|
14
|
+
const config = { environmentName: 'Production' };
|
|
15
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
it('should reject environment name with underscores', () => {
|
|
18
|
+
const config = { environmentName: 'prod_env' };
|
|
19
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
it('should reject environment name shorter than 3 characters', () => {
|
|
22
|
+
const config = { environmentName: 'ab' };
|
|
23
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
it('should accept valid AWS account ID', () => {
|
|
26
|
+
const config = { awsAccountId: '123456789012' };
|
|
27
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
it('should reject AWS account ID with less than 12 digits', () => {
|
|
30
|
+
const config = { awsAccountId: '12345678901' };
|
|
31
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
it('should reject AWS account ID with more than 12 digits', () => {
|
|
34
|
+
const config = { awsAccountId: '1234567890123' };
|
|
35
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
it('should reject AWS account ID with letters', () => {
|
|
38
|
+
const config = { awsAccountId: '12345678901a' };
|
|
39
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
it('should accept valid AWS region', () => {
|
|
42
|
+
const config = { awsRegion: 'us-east-1' };
|
|
43
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
it('should reject invalid AWS region', () => {
|
|
46
|
+
const config = { awsRegion: 'invalid-region' };
|
|
47
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('configFileSchema - IAM role ARNs', () => {
|
|
51
|
+
it('should accept valid IAM role ARN', () => {
|
|
52
|
+
const config = {
|
|
53
|
+
customerRoleArn: 'arn:aws:iam::123456789012:role/MyRole',
|
|
54
|
+
};
|
|
55
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
it('should accept IAM role ARN with path', () => {
|
|
58
|
+
const config = {
|
|
59
|
+
customerRoleArn: 'arn:aws:iam::123456789012:role/path/to/MyRole',
|
|
60
|
+
};
|
|
61
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
it('should reject invalid IAM role ARN format', () => {
|
|
64
|
+
const config = {
|
|
65
|
+
customerRoleArn: 'invalid-arn',
|
|
66
|
+
};
|
|
67
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
it('should reject IAM role ARN with wrong service', () => {
|
|
70
|
+
const config = {
|
|
71
|
+
customerRoleArn: 'arn:aws:s3::123456789012:role/MyRole',
|
|
72
|
+
};
|
|
73
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('configFileSchema - ACM certificate ARNs', () => {
|
|
77
|
+
it('should accept valid CloudFront certificate ARN', () => {
|
|
78
|
+
const config = {
|
|
79
|
+
tls: {
|
|
80
|
+
cloudfrontCertArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc-123',
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
it('should accept valid ALB certificate ARN', () => {
|
|
86
|
+
const config = {
|
|
87
|
+
tls: {
|
|
88
|
+
bffAlbCertificateArn: 'arn:aws:acm:us-west-2:123456789012:certificate/def-456',
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
it('should reject invalid certificate ARN', () => {
|
|
94
|
+
const config = {
|
|
95
|
+
tls: {
|
|
96
|
+
cloudfrontCertArn: 'invalid-cert-arn',
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('configFileSchema - Route53 hosted zone IDs', () => {
|
|
103
|
+
it('should accept valid hosted zone ID', () => {
|
|
104
|
+
const config = {
|
|
105
|
+
tls: {
|
|
106
|
+
hostedZoneId: 'Z1234567890ABC',
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
it('should reject hosted zone ID not starting with Z', () => {
|
|
112
|
+
const config = {
|
|
113
|
+
tls: {
|
|
114
|
+
hostedZoneId: 'A1234567890ABC',
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
it('should reject hosted zone ID with lowercase', () => {
|
|
120
|
+
const config = {
|
|
121
|
+
tls: {
|
|
122
|
+
hostedZoneId: 'Z1234567890abc',
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('configFileSchema - VPC and networking', () => {
|
|
129
|
+
it('should accept valid VPC ID', () => {
|
|
130
|
+
const config = {
|
|
131
|
+
networking: {
|
|
132
|
+
customVpcId: 'vpc-1234567890abcdef',
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
it('should reject invalid VPC ID format', () => {
|
|
138
|
+
const config = {
|
|
139
|
+
networking: {
|
|
140
|
+
customVpcId: 'invalid-vpc-id',
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
it('should accept valid CIDR block', () => {
|
|
146
|
+
const config = {
|
|
147
|
+
networking: {
|
|
148
|
+
vpcCidr: '10.0.0.0/16',
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
it('should reject invalid CIDR block', () => {
|
|
154
|
+
const config = {
|
|
155
|
+
networking: {
|
|
156
|
+
vpcCidr: '10.0.0.0',
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
160
|
+
});
|
|
161
|
+
it('should accept valid subnet IDs', () => {
|
|
162
|
+
const config = {
|
|
163
|
+
networking: {
|
|
164
|
+
customPublicSubnetIds: 'subnet-abc123,subnet-def456',
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
it('should accept single subnet ID', () => {
|
|
170
|
+
const config = {
|
|
171
|
+
networking: {
|
|
172
|
+
customPrivateSubnetIds: 'subnet-abc123',
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
it('should reject invalid subnet ID format', () => {
|
|
178
|
+
const config = {
|
|
179
|
+
networking: {
|
|
180
|
+
customPublicSubnetIds: 'invalid-subnet',
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
184
|
+
});
|
|
185
|
+
it('should accept valid JSON array for subnet CIDRs', () => {
|
|
186
|
+
const config = {
|
|
187
|
+
networking: {
|
|
188
|
+
publicSubnetCidrs: '["10.0.1.0/24", "10.0.2.0/24"]',
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
192
|
+
});
|
|
193
|
+
it('should reject non-JSON string for subnet CIDRs', () => {
|
|
194
|
+
const config = {
|
|
195
|
+
networking: {
|
|
196
|
+
publicSubnetCidrs: '10.0.1.0/24, 10.0.2.0/24',
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
200
|
+
});
|
|
201
|
+
it('should accept valid AZ count', () => {
|
|
202
|
+
const config = {
|
|
203
|
+
networking: {
|
|
204
|
+
azCount: '2',
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
208
|
+
});
|
|
209
|
+
it('should reject invalid AZ count', () => {
|
|
210
|
+
const config = {
|
|
211
|
+
networking: {
|
|
212
|
+
azCount: '4',
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
describe('configFileSchema - security groups', () => {
|
|
219
|
+
it('should accept valid security group ID', () => {
|
|
220
|
+
const config = {
|
|
221
|
+
security: {
|
|
222
|
+
customSecurityGroups: {
|
|
223
|
+
aurora: 'sg-1234567890abcdef',
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
228
|
+
});
|
|
229
|
+
it('should reject invalid security group ID', () => {
|
|
230
|
+
const config = {
|
|
231
|
+
security: {
|
|
232
|
+
customSecurityGroups: {
|
|
233
|
+
workflowEngine: 'invalid-sg',
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
238
|
+
});
|
|
239
|
+
it('should accept multiple security groups', () => {
|
|
240
|
+
const config = {
|
|
241
|
+
security: {
|
|
242
|
+
customSecurityGroups: {
|
|
243
|
+
aurora: 'sg-1234567890abcdef',
|
|
244
|
+
rdsProxy: 'sg-1234567890abcdef',
|
|
245
|
+
bffEcs: 'sg-1234567890abcdef',
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
const result = configFileSchema.safeParse(config);
|
|
250
|
+
if (!result.success) {
|
|
251
|
+
console.log('Validation errors:', result.error.errors);
|
|
252
|
+
}
|
|
253
|
+
expect(result.success).toBe(true);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
describe('configFileSchema - database', () => {
|
|
257
|
+
it('should accept valid database name', () => {
|
|
258
|
+
const config = {
|
|
259
|
+
database: {
|
|
260
|
+
name: 'mydb',
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
264
|
+
});
|
|
265
|
+
it('should accept database name with underscores and numbers', () => {
|
|
266
|
+
const config = {
|
|
267
|
+
database: {
|
|
268
|
+
name: 'my_db_123',
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
272
|
+
});
|
|
273
|
+
it('should reject database name starting with number', () => {
|
|
274
|
+
const config = {
|
|
275
|
+
database: {
|
|
276
|
+
name: '123db',
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
280
|
+
});
|
|
281
|
+
it('should reject database name with hyphens', () => {
|
|
282
|
+
const config = {
|
|
283
|
+
database: {
|
|
284
|
+
name: 'my-db',
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
describe('configFileSchema - repository', () => {
|
|
291
|
+
it('should accept valid repository', () => {
|
|
292
|
+
const config = {
|
|
293
|
+
repository: {
|
|
294
|
+
owner: 'my-org',
|
|
295
|
+
repo: 'my-repo',
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
299
|
+
});
|
|
300
|
+
it('should reject repository with empty owner', () => {
|
|
301
|
+
const config = {
|
|
302
|
+
repository: {
|
|
303
|
+
owner: '',
|
|
304
|
+
repo: 'my-repo',
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
308
|
+
});
|
|
309
|
+
it('should reject repository with empty repo name', () => {
|
|
310
|
+
const config = {
|
|
311
|
+
repository: {
|
|
312
|
+
owner: 'my-org',
|
|
313
|
+
repo: '',
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
describe('configFileSchema - feature flags', () => {
|
|
320
|
+
it('should accept boolean feature flags', () => {
|
|
321
|
+
const config = {
|
|
322
|
+
features: {
|
|
323
|
+
deploySembixStudioMemory: true,
|
|
324
|
+
deploySembixStudioNotifications: false,
|
|
325
|
+
enableBffWaf: true,
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
329
|
+
});
|
|
330
|
+
it('should reject non-boolean feature flags', () => {
|
|
331
|
+
const config = {
|
|
332
|
+
features: {
|
|
333
|
+
deploySembixStudioMemory: 'yes',
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
describe('configFileSchema - frontend', () => {
|
|
340
|
+
it('should accept valid frontend configuration', () => {
|
|
341
|
+
const config = {
|
|
342
|
+
frontend: {
|
|
343
|
+
githubAppClientId: 'Iv1.abc123',
|
|
344
|
+
githubAppName: 'my-github-app',
|
|
345
|
+
jiraClientId: 'jira-client-123',
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
349
|
+
});
|
|
350
|
+
it('should reject empty GitHub App Client ID', () => {
|
|
351
|
+
const config = {
|
|
352
|
+
frontend: {
|
|
353
|
+
githubAppClientId: '',
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
describe('configFileSchema - hub configuration', () => {
|
|
360
|
+
it('should accept valid hub configuration', () => {
|
|
361
|
+
const config = {
|
|
362
|
+
hub: {
|
|
363
|
+
hubRoles: {
|
|
364
|
+
engineExecutionRole: 'arn:aws:iam::123456789012:role/hub-engine',
|
|
365
|
+
consumerRole: 'arn:aws:iam::123456789012:role/hub-consumer',
|
|
366
|
+
},
|
|
367
|
+
appConfig: {
|
|
368
|
+
roleArn: 'arn:aws:iam::123456789012:role/appconfig',
|
|
369
|
+
appId: 'app-123',
|
|
370
|
+
envId: 'env-456',
|
|
371
|
+
profileId: 'profile-789',
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
376
|
+
});
|
|
377
|
+
it('should reject invalid hub role ARN', () => {
|
|
378
|
+
const config = {
|
|
379
|
+
hub: {
|
|
380
|
+
hubRoles: {
|
|
381
|
+
engineExecutionRole: 'invalid-arn',
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
describe('configFileSchema - terraform state bucket', () => {
|
|
389
|
+
it('should accept valid S3 bucket name', () => {
|
|
390
|
+
const config = {
|
|
391
|
+
terraformStateBucket: 'my-terraform-state-bucket',
|
|
392
|
+
};
|
|
393
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
394
|
+
});
|
|
395
|
+
it('should accept S3 bucket name with numbers', () => {
|
|
396
|
+
const config = {
|
|
397
|
+
terraformStateBucket: 'my-bucket-123',
|
|
398
|
+
};
|
|
399
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
400
|
+
});
|
|
401
|
+
it('should reject S3 bucket name with uppercase', () => {
|
|
402
|
+
const config = {
|
|
403
|
+
terraformStateBucket: 'My-Bucket',
|
|
404
|
+
};
|
|
405
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
406
|
+
});
|
|
407
|
+
it('should reject S3 bucket name starting with hyphen', () => {
|
|
408
|
+
const config = {
|
|
409
|
+
terraformStateBucket: '-my-bucket',
|
|
410
|
+
};
|
|
411
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
412
|
+
});
|
|
413
|
+
it('should reject S3 bucket name ending with hyphen', () => {
|
|
414
|
+
const config = {
|
|
415
|
+
terraformStateBucket: 'my-bucket-',
|
|
416
|
+
};
|
|
417
|
+
expect(configFileSchema.safeParse(config).success).toBe(false);
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
describe('configFileSchema - complete configuration', () => {
|
|
421
|
+
it('should accept complete valid configuration', () => {
|
|
422
|
+
const config = {
|
|
423
|
+
repository: {
|
|
424
|
+
owner: 'sembix-ai',
|
|
425
|
+
repo: 'sembix-studio',
|
|
426
|
+
},
|
|
427
|
+
environmentName: 'production',
|
|
428
|
+
awsAccountId: '123456789012',
|
|
429
|
+
awsRegion: 'us-east-1',
|
|
430
|
+
customerRoleArn: 'arn:aws:iam::123456789012:role/CustomerRole',
|
|
431
|
+
terraformStateBucket: 'my-terraform-state',
|
|
432
|
+
database: {
|
|
433
|
+
name: 'studiodb',
|
|
434
|
+
user: 'dbadmin',
|
|
435
|
+
},
|
|
436
|
+
networking: {
|
|
437
|
+
enableVpcEndpoints: true,
|
|
438
|
+
useCustomNetworking: false,
|
|
439
|
+
vpcCidr: '10.0.0.0/16',
|
|
440
|
+
azCount: '3',
|
|
441
|
+
},
|
|
442
|
+
security: {
|
|
443
|
+
useCustomSecurityGroups: false,
|
|
444
|
+
useCustomIamPolicies: false,
|
|
445
|
+
},
|
|
446
|
+
tls: {
|
|
447
|
+
cloudfrontDomain: 'app.example.com',
|
|
448
|
+
cloudfrontCertArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc',
|
|
449
|
+
bffAlbCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/def',
|
|
450
|
+
hostedZoneId: 'Z1234567890ABC',
|
|
451
|
+
},
|
|
452
|
+
features: {
|
|
453
|
+
deploySembixStudioMemory: true,
|
|
454
|
+
deploySembixStudioNotifications: true,
|
|
455
|
+
enableBffWaf: false,
|
|
456
|
+
},
|
|
457
|
+
frontend: {
|
|
458
|
+
githubAppClientId: 'Iv1.abc123',
|
|
459
|
+
githubAppName: 'my-app',
|
|
460
|
+
jiraClientId: 'jira-123',
|
|
461
|
+
},
|
|
462
|
+
};
|
|
463
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
464
|
+
});
|
|
465
|
+
it('should accept empty configuration (all fields optional)', () => {
|
|
466
|
+
const config = {};
|
|
467
|
+
expect(configFileSchema.safeParse(config).success).toBe(true);
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
//# sourceMappingURL=config-schema.test.js.map
|